刚刚突然问到我的问题,我不认为我在这里看到了答案。二进制加法算法所花费的时间是否与操作数的大小成比例?
显然,添加1101011010101010101101010
和10110100101010010101
的时间会比1 + 1
更长,但我的问题更多地涉及较小的值。是否存在可忽略的差异,没有差异,理论差异?
在什么时候,通过这些基本的计算,我们应该开始研究更有效的计算方法吗?即:通过与大指数进行平方来计算巨大的权力。
答案 0 :(得分:5)
1101011010101010101101010
(大)
10110100101010010101
(中)
1
(小)
00000001101011010101010101101010
32位,
00000000000010110100101010010101
32位,
00000000000000000000000000000001
我很喜欢
在32位系统上,以上所有数字都需要添加相同的时间(CPU指令数)。因为它们都适合基本的计算模块,即32位CPU寄存器。
1
+1 = ?
0000000000000001
我很喜欢
0000000000000001
我很喜欢
00000001101011010101010101101010
+00000000000010110100101010010101 = ?
00000001101011010101010101101010
对我来说太大了!
00000000000010110100101010010101
对我来说太大了!
在16位系统上,由于较大的数字不适合16位寄存器,因此需要额外传递(以添加在添加前16LSB之后剩余的有效位)。
步骤1:添加最低有效位
0101010101101010
0100101010010101
步骤2:添加其余部分(记住上次操作中的进位)
000000000000000C
0000000110101101
0000000000001011
我们可以开始考虑优化数学运算 数字一旦数字不再适合基本计算单位 系统,即CPU寄存器。
开发现代硬件架构时要牢记这一点并支持SIMD指令。当编译器看到这样的情况,即在32位系统上运行128位解密逻辑时,编译器通常会使用它们(在x86上 SSE ,在ARM上 NEON )。
此外,结果的大小不是仅检查操作数的大小,而是确定系统是否可以在一个步骤内完成数学运算。不仅涉及操作数,还需要考虑正在执行的操作。
例如,在32位系统上,使用常规操作可以绝对添加两个30位数字,因为结果保证不会超过32位寄存器。但是,将相同的两个30位数相乘可能会导致数字不适合32位。
如果没有能够将结果存储在单个计算单元中的保证,为了确保所有可能值的结果的有效性,架构(和编译器)必须:
答案 1 :(得分:2)
实际上,添加适合处理器单词的不同整数之间没有(或完全可以忽略不计),因为它应该始终是固定时间操作。
理论上,添加两个无符号整数的复杂度应该是O(log(n)),其中n是两者中较大的一个。因此,在添加成为问题之前,你需要走得很高。
至于在简单和复杂算法之间用于计算数字的界限,我没有确切的答案。但是,我想到了GMP库。根据我的理解,他们仔细选择了他们的算法,并在什么情况下在性能方面使用它们。你可能想看看他们做了什么。
答案 2 :(得分:0)
我有点不同意上述答案。这在很大程度上取决于具体情况。
对于简单的整数运算(对于循环计数器等),然后在64位机器上,计算将使用64位通用寄存器(RSI / RCX /等)完成。在这些情况下,8位或64位添加之间的速度没有差异。
如果你正在处理整数数组,并假设编译器能够很好地优化代码,那么是的,更小更快(但不是你认为的原因)。
在AVX2指令集中,您可以访问4个整数加法指令:
__m256i _mm256_add_epi8 (__m256i a, __m256i b); // 32 x 8bit
__m256i _mm256_add_epi16(__m256i a, __m256i b); // 16 x 16bit
__m256i _mm256_add_epi32(__m256i a, __m256i b); // 8 x 32bit
__m256i _mm256_add_epi64(__m256i a, __m256i b); // 4 x 64bit
您会注意到它们一次只能运行256位,这意味着如果您使用的是64位,则可以处理4个整数加法,而如果使用8位整数则可以处理32位加法。 (如上所述,您需要确保您有足够的精度)。它们都需要相同数量的时钟周期来计算 - 1clk。
使用较小的数据类型还有其他影响,主要是更好的CPU缓存使用率和减少的内存读/写次数。
然而,回到原始问题的逐位计算。在新的AVX-512指令集之前,它可能看起来有点傻。但是,新指令集包含三元逻辑指令。使用此指令,可以非常容易地计算任意位长度的512个加法。
inline __m512i add(__m512i x, __m512i x, __m512i carry_in)
{
return _mm512_ternarylogic_epi32(carry_in, y, x, 0x96);
}
inline __m512i adc(__m512i x, __m512i x, __m512i carry_in)
{
return _mm512_ternarylogic_epi32(carry_in, y, x, 0xE8);
}
__m512i A[NUM_BITS];
__m512i B[NUM_BITS];
__m512i RESULT[NUM_BITS];
__m512i CARRY = _mm512_setzero_ps();
for(int i = 0; i < NUM_BITS; ++i)
{
RESULT[i] = add(A[i], B[i], CARRY);
CARRY = adc(A[i], B[i], CARRY);
}
在这个特定的例子中(说实话,现实世界的使用可能非常有限!),执行512次添加所花费的时间确实与NUM_BITS成正比。