Saya memperhatikan hal yang aneh di komputer saya. * Tes keterbagian tulisan tangan secara signifikan lebih cepat daripada %operator. Pertimbangkan contoh minimal:
* 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);
}
Contoh dibatasi oleh ganjil adan m > 0. Namun, dapat dengan mudah digeneralisasikan untuk semua adan m. Kode hanya mengubah divisi menjadi serangkaian tambahan.
Sekarang pertimbangkan program pengujian yang disusun dengan -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
}
}
... dan hasilnya di komputer saya:
| implementation | time [secs] |
|--------------------|-------------|
| divisible_ui_p | 8.52user |
| builtin % operator | 17.61user |
Karena itu lebih dari 2 kali lebih cepat.
Pertanyaannya: Bisakah Anda memberi tahu saya bagaimana kode berperilaku pada mesin Anda? Apakah itu kehilangan peluang pengoptimalan di GCC? Bisakah Anda melakukan tes ini lebih cepat?
PEMBARUAN: Seperti yang diminta, berikut adalah contoh minimal yang dapat direproduksi:
#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;
}
dikompilasi dengan gcc -std=c99 -march=native -O3 -DNDEBUGAMD Ryzen Threadripper 2990WX dengan
gcc --version
gcc (Gentoo 9.2.0-r2 p3) 9.2.0
UPDATE2: Seperti yang diminta, versi yang dapat menangani apa saja adan m(jika Anda juga ingin menghindari overflow integer, tes harus dilaksanakan dengan tipe integer dua kali selama input integer):
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;
}
rs yang Anda hitung memang sama satu sama lain.
a % bmemiliki bjauh lebih kecil daripada a. Melalui sebagian besar iterasi dalam test case Anda, mereka memiliki ukuran yang sama, atau blebih besar, dan versi Anda bisa lebih cepat pada banyak CPU dalam situasi tersebut.