以下是我将要测试的一种方法,因为问题的评论已经明确了
x
和
m
的分布很重要。
我没有意识到的是,
m > x
的概率是50%。
未经测试的伪代码:
fn mod(x: u64, m: u64) -> u64 {
if m > x {
x
} else if 2*m > x {
x - m
} else {
x % m
}
}
这很可能是编译器生成的内容。我还没有测试过。
更新:我已在我的i5 2500K上测试了这种优化,处理了1 GiB的数据。
结果是稳定的提速67%:
opt: 713.113926ms
plain: 1.195687298s
基准测试代码是:
use std::time::Instant;
use rand::Rng;
const ARR_LEN: usize = 128 * 1024;
const ROUNDS: usize = 1024;
fn plain_mod_test(x: &[u64; ARR_LEN], m: u64, result: &mut [u64; ARR_LEN]) {
for i in 0..ARR_LEN {
result[i] = x[i] % m;
}
}
fn opt_mod_test(x: &[u64; ARR_LEN], m: u64, result: &mut [u64; ARR_LEN]) {
for i in 0..ARR_LEN {
result[i] = if m > x[i] {
x[i]
} else if m > x[i] / 2 {
x[i] - m
} else {
x[i] % m
}
}
}
fn main() {
let mut rng = rand::thread_rng();
let mut x = [0u64; ARR_LEN];
for i in 0..ARR_LEN {
x[i] = rng.gen();
}
let mut m = [0u64; ROUNDS];
for r in 0..ROUNDS {
m[r] = rng.gen();
}
let mut plain_results = vec![[0u64; ARR_LEN]; ROUNDS];
let mut opt_results = vec![[0u64; ARR_LEN]; ROUNDS];
let start_opt = Instant::now();
for r in 0..ROUNDS {
opt_mod_test(&x, m[r], &mut opt_results[r]);
}
let stop_opt = Instant::now();
println!("opt: {:?}", stop_opt - start_opt);
let start_plain = Instant::now();
for r in 0..ROUNDS {
plain_mod_test(&x, m[r], &mut plain_results[r]);
}
let stop_plain = Instant::now();
println!("plain: {:?}", stop_plain - start_plain);
let mut plain_sum = 0;
let mut opt_sum = 0;
for r in 0..ROUNDS {
for i in 0..ARR_LEN {
plain_sum += plain_results[r][i];
opt_sum += opt_results[r][i];
}
}
println!("opt_sum: {:?}", opt_sum);
println!("plain_sum: {:?}", plain_sum);
}
m
的分布未被指定。实际上,整体上下文也没有被指定。提供更大的背景可能有助于改善性能,而不仅仅是这种狭隘的方法。祝好运。 - chux - Reinstate Monicam
,您可以执行很多操作,类似于编译时发生的情况,但是您会在运行时执行它。顺便问一下,你会如何使用它?或者你只是“普遍地”感兴趣?有各种特殊解决方案的情况,例如使用蒙哥马利模乘法(如果适用)或使用模乘逆元素进行乘法来检查可除性。 - harold