コンピューターに不思議なことに気づきました。*手書きの分割可能性テストは、%
オペレーターよりも大幅に高速です。最小限の例を考えてみましょう:
* AMD Ryzen Threadripper 2990WX、GCC 9.2.0
static int divisible_ui_p(unsigned int m, unsigned int a)
{
if (m <= a) {
if (m == a) {
return 1;
}
return 0;
}
m += a;
m >>= __builtin_ctz(m);
return divisible_ui_p(m, a);
}
例は奇数a
とによって制限されm > 0
ます。ただし、すべてのa
およびに簡単に一般化できますm
。コードは除算を一連の追加に変換するだけです。
でコンパイルされたテストプログラムを考えてみましょう-std=c99 -march=native -O3
:
for (unsigned int a = 1; a < 100000; a += 2) {
for (unsigned int m = 1; m < 100000; m += 1) {
#if 1
volatile int r = divisible_ui_p(m, a);
#else
volatile int r = (m % a == 0);
#endif
}
}
...そして私のコンピューター上の結果:
| implementation | time [secs] |
|--------------------|-------------|
| divisible_ui_p | 8.52user |
| builtin % operator | 17.61user |
したがって、2倍以上速くなります。
質問:コードがマシンでどのように動作するか教えていただけますか?GCCで最適化の機会を逃していませんか?このテストをもっと速く行うことができますか?
更新: 要求されたとおり、これは最小限の再現可能な例です:
#include <assert.h>
static int divisible_ui_p(unsigned int m, unsigned int a)
{
if (m <= a) {
if (m == a) {
return 1;
}
return 0;
}
m += a;
m >>= __builtin_ctz(m);
return divisible_ui_p(m, a);
}
int main()
{
for (unsigned int a = 1; a < 100000; a += 2) {
for (unsigned int m = 1; m < 100000; m += 1) {
assert(divisible_ui_p(m, a) == (m % a == 0));
#if 1
volatile int r = divisible_ui_p(m, a);
#else
volatile int r = (m % a == 0);
#endif
}
}
return 0;
}
gcc -std=c99 -march=native -O3 -DNDEBUG
AMD Ryzen Threadripper 2990WXでコンパイル
gcc --version
gcc (Gentoo 9.2.0-r2 p3) 9.2.0
UPDATE2:要求に応じて、すべてa
を処理できるバージョンm
(整数オーバーフローも回避したい場合は、入力整数の2倍の整数型でテストを実装する必要があります):
int divisible_ui_p(unsigned int m, unsigned int a)
{
#if 1
/* handles even a */
int alpha = __builtin_ctz(a);
if (alpha) {
if (__builtin_ctz(m) < alpha) {
return 0;
}
a >>= alpha;
}
#endif
while (m > a) {
m += a;
m >>= __builtin_ctz(m);
}
if (m == a) {
return 1;
}
#if 1
/* ensures that 0 is divisible by anything */
if (m == 0) {
return 1;
}
#endif
return 0;
}
r
計算した2つのが実際に互いに等しいと実際に主張するテストも見たいと思います。
a % b
持っているb
よりもはるかに小さいですa
。テストケースのほとんどの反復では、それらは同じようなサイズか、それb
よりも大きく、そのような状況では、多くのCPUでバージョンが高速になる可能性があります。