【问题标题】:Math behind gcc9+ modulus optimizationsgcc9+ 模数优化背后的数学
【发布时间】:2018-11-21 14:53:19
【问题描述】:

背景

我在 c 中使用素数时偶然发现 gcc 主干(将是 9.x 版)中的一个新优化,它将模数比较优化为 0 到整数乘法和使用幻数比较。换句话说x%prime==0变成x*Magic_mul<=Magic_cmp

_Bool mod(unsigned x){return x % Constant == 0;}

mod:
  imul edi, edi, Magic_mul
  cmp edi, Magic_cmp
  setbe al

详情

根据看到 asm 输出,它对所有整数(至少是素数)进行这些优化,我将它们转换为十六进制以帮助查看模式,但目前还不是很明显。

//32bit examples for _Bool mod_n(unsigned x){return x%n==0;};
//note: parameter is unsigned but it becomes a signed multiply
x%3==0;  // x*0xAAAAAAAB <= 0x55555555
x%5==0;  // x*0xCCCCCCCD <= 0x33333333
x%7==0;  // x*0xB6DB6DB7 <= 0x24924924
x%11==0; // x*0xBA2E8BA3 <= 0x1745D174
x%13==0; // x*0xC4EC4EC5 <= 0x13B13B13
x%17==0; // x*0xF0F0F0F1 <= 0x0F0F0F0F
x%19==0; // x*0x286BCA1B <= 0x0D79435E
x%23==0; // x*0xE9BD37A7 <= 0x0B21642C
x%29==0; // x*0x4F72C235 <= 0x08D3DCB0
x%31==0; // x*0xBDEF7BDF <= 0x08421084
x%37==0; // x*0x914C1BAD <= 0x06EB3E45
x%41==0; // x*0xC18F9C19 <= 0x063E7063
x%43==0; // x*0x2FA0BE83 <= 0x05F417D0
x%47==0; // x*0x677D46CF <= 0x0572620A
x%53==0; // x*0x8C13521D <= 0x04D4873E
x%59==0; // x*0xA08AD8F3 <= 0x0456C797
x%61==0; // x*0xC10C9715 <= 0x04325C53
x%67==0; // x*0x07A44C6B <= 0x03D22635
x%71==0; // x*0xE327A977 <= 0x039B0AD1
x%73==0; // x*0xC7E3F1F9 <= 0x0381C0E0
x%79==0; // x*0x613716AF <= 0x033D91D2
x%83==0; // x*0x2B2E43DB <= 0x03159721
x%89==0; // x*0xFA3F47E9 <= 0x02E05C0B
x%97==0; // x*0x5F02A3A1 <= 0x02A3A0FD
///...and even up to 64bit
x%4294967291==0; //x*0x70A3D70A33333333 <= 0x100000005

我检查了hacker's delight "INTEGER DIVISION BY CONSTANTS",这似乎是乘法和右移余数的特例,但我不确定。有一个form on hacker's delight 可以计算这些相同的乘数常数,所以这看起来很有希望。我猜神奇的比较常数取代了移位并比较为零,但我无法想象 2s 补码以及移位是算术还是逻辑移位。

问题

这背后有一些数学原理,还是使用二进制表示以其他方式确定数字?

含义

由于这是简单的整数乘法和比较,因此可以大大加快(或减少内存占用)使用向量扩展/内在函数检查素数的速度。如果数学可以扩展到 64 位以上,它可能会更快地找到大数素数?

【问题讨论】:

标签: c math compiler-optimization modulus


【解决方案1】:

以 3 为例。

0xAB * 3 = 0x201,因此,模 0x100,0xAB 是 1 / 3,反之,0xAB * 3 ≡ 1。

任何 8 位无符号整数 n 可以表示为 n = 3*k + r,r

所以我们有选择:

  1. r = 0 ⇒ n * 0xAB = 3k * 0xAB = k * (3 * 0xAB) ≡ k * 1 = k ≤ 0x55。

  2. r = 1 ⇒ n * 0xAB = 3k * 0xAB + 0xAB;由于 3k * 0xAB 最多为 0x55 (mod 0x100),添加到 0xAB 不会溢出,所以 3k * 0xAB + 0xAB ≥ 0xAB > 0x55。

  3. r = 2 ⇒ n * 0xAB = 3k * 0xAB + 0x156 ≡ 3k * 0xAB + 0x56 ≥ 0x56 > 0x55(同 2)

【讨论】:

  • 您的数学看起来不错,并且似乎与此概念的其他文档一致。我想我只需要研究出一些二进制符号的例子来遵循它(或者睡一觉,这样我就可以在脑海中做十六进制->二进制)。我也希望找出数学来获得比较常数(一旦我可以想象它可能会很明显)
  • 嗯,常数很简单,0xAB 只是 1/3,而 0x55 是 8 位 n 的 n/3 的最高层。含义如下。 :)
猜你喜欢
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 2010-09-29
  • 2019-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多