考虑两个向量, A 和 B ,大小 n ,7< = n < = 23. A 和 B 仅包含-1s,0和1。
我需要一个快速算法来计算 A 和 B 的内在产品。
到目前为止,我已经考虑过使用以下编码将符号和值存储在单独的uint32_t
中:
我想到的C ++实现如下所示:
struct ternary_vector {
uint32_t sign, value;
};
int inner_product(const ternary_vector & a, const ternary_vector & b) {
uint32_t psign = a.sign ^ b.sign;
uint32_t pvalue = a.value & b.value;
psign &= pvalue;
pvalue ^= psign;
return __builtin_popcount(pvalue) - __builtin_popcount(psign);
}
这种方法运作得相当好,但我不确定是否可以做得更好。对此事的任何评论都非常感谢。
答案 0 :(得分:3)
我喜欢2 uint32_t
,但我认为你的实际计算有点浪费
只是几点:
我不确定引用(通过a
获取b
和const &
) - 与将它们放在堆栈上相比,这增加了一个间接级别。当代码很小(可能是几个时钟)时,这很重要。尝试通过值传递,看看你得到了什么
__builtin_popcount
非常低效。我自己用过它,但发现即使是我写的一个非常基本的实现也比这快得多。但是 - 这取决于平台。
基本上,如果平台具有硬件popcount实现,__builtin_popcount
使用它。如果不是 - 它使用非常低效的替代品。
答案 1 :(得分:0)
这里的一个严重问题是对psign
和pvalue
变量重复使用正负向量。通过以这种方式混淆代码,你既不是你的编译器也不是你自己的任何好处。
答案 2 :(得分:0)
您是否可以在std::bitset<2>
中对三元状态进行编码,并根据and
定义产品?例如,如果您的三元类型是:
1 = P = (1, 1)
0 = Z = (0, 0)
-1 = M = (1, 0) or (0, 1)
我相信你可以将他们的产品定义为:
1 * 1 = 1 => P * P = P => (1, 1) & (1, 1) = (1, 1) = P
1 * 0 = 0 => P * Z = Z => (1, 1) & (0, 0) = (0, 0) = Z
1 * -1 = -1 => P * M = M => (1, 1) & (1, 0) = (1, 0) = M
然后内部产品可以通过获取元素的位数来开始......我正在研究如何将它们组合在一起。
我的愚蠢建议没有考虑(-1)(-1) = 1
,这是我提出的表述无法处理的。感谢@ user92382提出这个问题。
答案 3 :(得分:0)
根据您的体系结构,您可能希望优化临时位向量 - 例如如果你的代码要编译成FPGA,或者布局到ASIC,那么一系列逻辑运算在速度/能量/面积方面比存储和读/写两个大缓冲区更好。
在这种情况下,你可以这样做:
int inner_product(const ternary_vector & a, const ternary_vector & b) {
return __builtin_popcount( a.value & b.value & ~(a.sign ^ b.sign))
- __builtin_popcount( a.value & b.value & (a.sign ^ b.sign));
}
这将很好地布局 - (a.value&amp; b.value&amp; ...)可以启用/禁用XOR门,其输出分成两个带符号的累加器,第一个路径在累积之前未被记录