Ну, это заняло всего 4,04 с на моей коробке, но я получил мгновенное сокращение до 2,5 с , просто изменив r ** 2
в r * r
. Я мог бы дополнительно уменьшить его до 1,6 с , используя битовый сдвиг и побитовые операции, такие как изменение:
r = (r * r + m) % 4294967296
r = (r // 256) % 65536
на:
r = (r * r + m) & 0xffffffff
r = (r >> 8) & 0xffff
Затем, когда вы поймете, какие биты переживут эти две операции, вы можете превратить это в одно выражение:
r = ((r * r + m) & 0xffff00) >> 8
Это сокращает время до 1,33 с. , легко удваивая скорость (уменьшение 67% затраченного времени).
Если вы переключитесь на что-то вроде Numba , вы можете получить еще большее улучшение:
from numba import jit
from time import perf_counter
@jit()
def loop(seed, n):
r, m = seed, 0
for i in range(n):
m += 362437
r = ((r * r + m) & 0xffff00) >> 8
return r
tic_0 = perf_counter()
x = loop(366, 11223300)
print('\nTotal Runtime = {:.6f}'.format(perf_counter() - tic_0))
print(x)
Это сокращает время выполнения до довольно незначительного 110 мс , что в пределах вашей одной секунды, и примерно 90% улучшение затраченного времени по сравнению с оригиналом.
И это фактически включает время, необходимое для первоначальной JIT-компиляции кода, поэтому улучшение будет еще лучше при последующих вызовах. Если изменить код таким образом, чтобы мы сначала выполняли JIT-функцию, время снижается до 20 мс , экономия 99,5% :
x = loop(1, 2)
tic_0 = perf_counter()
x = loop(366, 11223300)
print('\nTotal Runtime = {:.6f}'.format(perf_counter() - tic_0))
print(x)
Это наравне с аналогичным кодом C, который, вероятно, можно рассматривать как эталон производительности. Например, следующий код C выполняется за 27 мс , что примерно на 99,3% по времени (это по умолчанию оптимизация; использование -O3
дает около 18 мс ):
#include <stdio.h>
int main(void) {
unsigned long r = 366, m = 0;
for (int i = 0; i < 11223300; ++i) {
m += 362437;
r = ((r * r + m) & 0xffff00UL) >> 8;
}
printf("%ld\n", r);
return 0;
}