【发布时间】:2019-08-27 19:29:47
【问题描述】:
我必须承认我对宏有点迷茫。 我想构建一个执行以下任务的宏 我不知道该怎么做。我想执行一个标量产品 两个数组,比如 x 和 y,它们具有相同的长度 N。 我要计算的结果是这样的:
z = sum_{i=0}^{N-1} x[i] * y[i].
x 是 const 哪些元素是 0, 1, or -1 在编译时已知,
而y 的元素是在运行时确定的。由于
x 的结构,很多计算都是无用的(项乘以 0
可以从和中去掉,而1 * y[i], -1 * y[i]形式的乘法可以分别转化为y[i], -y[i])。
例如,如果x = [-1, 1, 0],上面的标量积将是
z=-1 * y[0] + 1 * y[1] + 0 * y[2]
为了加快计算速度,我可以手动展开循环并重写
没有x[i],我可以将上面的公式硬编码为
z = -y[0] + y[1]
但是这个过程并不优雅,容易出错 当N变大时非常乏味。
我很确定我可以用宏来做到这一点,但我不知道在哪里 开始(我阅读的不同书籍并没有深入探讨宏和 我被卡住了)...
你们中有人知道如何(如果可能的话)使用宏来解决这个问题吗?
提前感谢您的帮助!
编辑:正如许多答案中所指出的,编译器足够聪明,可以在整数的情况下删除优化循环。我不仅使用整数,还使用浮点数(x 数组是 i32s,但通常y 是f64s),所以编译器不够聪明(而且理所当然地)来优化循环。下面的一段代码给出了下面的 asm。
const X: [i32; 8] = [0, 1, -1, 0, 0, 1, 0, -1];
pub fn dot_x(y: [f64; 8]) -> f64 {
X.iter().zip(y.iter()).map(|(i, j)| (*i as f64) * j).sum()
}
playground::dot_x:
xorpd %xmm0, %xmm0
movsd (%rdi), %xmm1
mulsd %xmm0, %xmm1
addsd %xmm0, %xmm1
addsd 8(%rdi), %xmm1
subsd 16(%rdi), %xmm1
movupd 24(%rdi), %xmm2
xorpd %xmm3, %xmm3
mulpd %xmm2, %xmm3
addsd %xmm3, %xmm1
unpckhpd %xmm3, %xmm3
addsd %xmm1, %xmm3
addsd 40(%rdi), %xmm3
mulsd 48(%rdi), %xmm0
addsd %xmm3, %xmm0
subsd 56(%rdi), %xmm0
retq
【问题讨论】:
-
写一个函数有什么问题?
fn scalar_product(x: &[i64], y: &[i64]) -> i64 { return x.iter().zip(y.iter()).map(|(l, r)| l * r).sum() } -
这个想法是让它运行得更快。你可以节省至少一半的计算,因为
l将是 0、1 和 -1。 -
我会首先假设编译器是智能的,通过优化来编译东西,并检查循环是否以所需的方式展开。可能是,甚至不需要宏。
-
感谢您的回答。正如我在 edited 帖子中指出的那样,不幸的是,编译器不够聪明,无法拯救我,因为我在计算中也使用了浮点数。
标签: rust rust-macros