Я обнаружил, что это:
#define mulmod52(a,b,m) (((a * b) - (((uint64_t)(((double)a * (double)b) / (double)m) - 1ULL) * m)) % m)
... быстрее, чем:
static inline uint64_t _mulmod(uint64_t a, uint64_t b, uint64_t n) {
uint64_t d, dummy; /* d will get a*b mod c */
asm ("mulq %3\n\t" /* mul a*b -> rdx:rax */
"divq %4\n\t" /* (a*b)/c -> quot in rax remainder in rdx */
:"=a"(dummy), "=&d"(d) /* output */
:"a"(a), "rm"(b), "rm"(n) /* input */
:"cc" /* mulq and divq can set conditions */
);
return d;
}
Первый способ - использовать FPU для вычисления модульного умножения двух до до 52 битных чисел. Последний простой X64 ASM для вычисления модульного умножения двух 64-разрядных чисел, и, конечно, он также отлично работает только для 52 битов.
Первый быстрее, чем последний примерно на 5-15% в зависимости от где я проверяю это.
Как это возможно, учитывая, что трюк FPU также включает в себя одно целочисленное умножение и одно целочисленное деление (модуль) плюс дополнительную работу FPU? Есть кое-что, чего я не понимаю здесь. Является ли это каким-то странным артефактом компилятора, таким как asm проходы оптимизации встроенного разрушающего компилятора?