Discussion:
Ile cykli zajmuje mnożenie liczb 64-bitowych?
(Wiadomość utworzona zbyt dawno temu. Odpowiedź niemożliwa.)
osobli...@gmail.com
2023-05-11 14:28:08 UTC
Permalink
Znalazłem dwa źródła:

http://www.phys.ufl.edu/~coldwell/MultiplePrecision/fpvsintmult.htm

https://stackoverflow.com/questions/21819682/is-integer-multiplication-really-done-at-the-same-speed-as-addition-on-a-modern

W jednym piszą, że to jest 20 cykli. W drugim 2-4 cykle (dla liczb 32-bitowych, dla 64-bitowych będzie dwa razy więcej?). Chcę zgrubnie oszacować liczbę cykli przypadającą na dwa różne algorytmy. Na przykład:

https://prng.di.unimi.it/xoroshiro128plusplus.c

Ale w drugim algorytmie mam mnożenie dwóch uint64_t. I nie wiem ile cykli mniej więcej przyjąć.
Bogdan
2023-05-13 14:07:51 UTC
Permalink
Post by ***@gmail.com
http://www.phys.ufl.edu/~coldwell/MultiplePrecision/fpvsintmult.htm
https://stackoverflow.com/questions/21819682/is-integer-multiplication-really-done-at-the-same-speed-as-addition-on-a-modern
https://prng.di.unimi.it/xoroshiro128plusplus.c
Ale w drugim algorytmie mam mnożenie dwóch uint64_t. I nie wiem ile cykli mniej więcej przyjąć.
Być może to nieoczywiste, ale jaka architektura? Na amd64 mnożenie
liczb 64-bitowych (czyli o wielkości rejestru) będzie zapewne o wiele
szybsze, niż na systemach 32-bitowych, o 16-bitowych nie wspominając.

We floating point może bym nie szedł, bo może być utrata precyzji, no
i trzeba konwertować.

Tak czy siak, pierwszy link z wyszukiwarki zapytanej o "intel
instruction latencies", zakładając, że jednak chodzi o architekturę
x86/x64: www.agner.org/optimize/instruction_tables.pdf.
Wybieram losowo procesor Haskell: MUL: czas: 3-4 cykli,
przepustowość: 0,5-1 instrukcji na cykl.

Najlepiej pobierz dokument i wybierz stosowny procesor.
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
osobli...@gmail.com
2023-05-13 17:28:55 UTC
Permalink
Post by Bogdan
Post by ***@gmail.com
http://www.phys.ufl.edu/~coldwell/MultiplePrecision/fpvsintmult.htm
https://stackoverflow.com/questions/21819682/is-integer-multiplication-really-done-at-the-same-speed-as-addition-on-a-modern
https://prng.di.unimi.it/xoroshiro128plusplus.c
Ale w drugim algorytmie mam mnożenie dwóch uint64_t. I nie wiem ile cykli mniej więcej przyjąć.
Być może to nieoczywiste, ale jaka architektura? Na amd64 mnożenie
liczb 64-bitowych (czyli o wielkości rejestru) będzie zapewne o wiele
szybsze, niż na systemach 32-bitowych, o 16-bitowych nie wspominając.
We floating point może bym nie szedł, bo może być utrata precyzji, no
i trzeba konwertować.
Tak czy siak, pierwszy link z wyszukiwarki zapytanej o "intel
instruction latencies", zakładając, że jednak chodzi o architekturę
x86/x64: www.agner.org/optimize/instruction_tables.pdf.
Wybieram losowo procesor Haskell: MUL: czas: 3-4 cykli,
przepustowość: 0,5-1 instrukcji na cykl.
Najlepiej pobierz dokument i wybierz stosowny procesor.
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
Interesuje mnie głównie architektura 64-bitowa, ale też GPU. Co do tabeli - te 3-4 cykle to jest dla liczb 32-bitowych? Mam zakładać, że dla 64-bitowych będzie to samo, czy dwa razy więcej?
osobli...@gmail.com
2023-05-13 17:43:31 UTC
Permalink
Swoją drogą mierzę sobie względną szybkość generatorów PRNG za pomocą:

https://quick-bench.com

Jedyne sensowne zestawienie, po zliczeniu przez mnie ręcznie liczby cykli na operacje (wynik 22 do 13), które wykonują algorytmy, dostaję, gdy włączam optim=None. Jeżeli zaś włączę OFast xoroshiro dostaje takiego przyspieszenia, że wyprzedza drugi PRNG, według tego benchmarku.

W samym xoroshiro liczę operację:

const uint64_t s0 = s[0];

jako jeden cykl, bo następuje wywołanie zmiennej z tablicy. Ale nie jestem pewien, czy to tak należy szacować.
Bogdan
2023-05-14 09:26:42 UTC
Permalink
Post by ***@gmail.com
https://quick-bench.com
Jedyne sensowne zestawienie, po zliczeniu przez mnie ręcznie liczby cykli na operacje (wynik 22 do 13), które wykonują algorytmy, dostaję, gdy włączam optim=None. Jeżeli zaś włączę OFast xoroshiro dostaje takiego przyspieszenia, że wyprzedza drugi PRNG, według tego benchmarku.
const uint64_t s0 = s[0];
jako jeden cykl, bo następuje wywołanie zmiennej z tablicy. Ale nie jestem pewien, czy to tak należy szacować.
To zależy od poziomu optymalizacji.
Bez optymalizacji na samo to wziąłbym 1 cykl na kopię z pamięci do
rejestru i 1 na kopię z rejestru do innej pamięci. Ale wspomniany
dokument podaje np. 3 cykle na kopiowanie do pamięci, więc nawet to
nie jest takie oczywiste.
Z optymalizacją jest szansa, że "s0" siedzi w rejestrze, więc
wystarczy pewnie 1 cykl na załadowanie.
Oczywiście, jeśli s[0] jest ułożone na równym adresie.
Oczywiście, jeśli s[0] siedzi w cache, bo jeśli nie, to w najgorszym
przypadku mogą być może dziesiątki, jak nie setki cykli na pobranie z
głównej pamięci.
I pewnie jeszcze różne inne warunki, więc tabelki tabelkami, ale
najlepiej albo pomierzyć (RDTSC), albo użyć narzędzi mówiących, co ile
potrwa (kiedyś było np. jakieś VTune Analyzer).
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
osobli...@gmail.com
2023-05-14 14:00:10 UTC
Permalink
Post by Bogdan
Post by ***@gmail.com
https://quick-bench.com
Jedyne sensowne zestawienie, po zliczeniu przez mnie ręcznie liczby cykli na operacje (wynik 22 do 13), które wykonują algorytmy, dostaję, gdy włączam optim=None. Jeżeli zaś włączę OFast xoroshiro dostaje takiego przyspieszenia, że wyprzedza drugi PRNG, według tego benchmarku.
const uint64_t s0 = s[0];
jako jeden cykl, bo następuje wywołanie zmiennej z tablicy. Ale nie jestem pewien, czy to tak należy szacować.
To zależy od poziomu optymalizacji.
Bez optymalizacji na samo to wziąłbym 1 cykl na kopię z pamięci do
rejestru i 1 na kopię z rejestru do innej pamięci. Ale wspomniany
dokument podaje np. 3 cykle na kopiowanie do pamięci, więc nawet to
nie jest takie oczywiste.
Z optymalizacją jest szansa, że "s0" siedzi w rejestrze, więc
wystarczy pewnie 1 cykl na załadowanie.
Oczywiście, jeśli s[0] jest ułożone na równym adresie.
Oczywiście, jeśli s[0] siedzi w cache, bo jeśli nie, to w najgorszym
przypadku mogą być może dziesiątki, jak nie setki cykli na pobranie z
głównej pamięci.
I pewnie jeszcze różne inne warunki, więc tabelki tabelkami, ale
najlepiej albo pomierzyć (RDTSC), albo użyć narzędzi mówiących, co ile
potrwa (kiedyś było np. jakieś VTune Analyzer).
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
Ok, czyli liczę to raczej prawidłowo. Przykładowe szacunki:

class xoroshiro256plus {

uint64_t s[4] = { 5, 11, 13, 99 };

static uint64_t rotl(const uint64_t x, int k)
{
return (x << k) | (x >> (64 - k));
}

public:
uint64_t next() noexcept
{
const uint64_t result = s[0] + s[3]; // 3 cycles

const uint64_t t = s[1] << 17; // 2 cycles

s[2] ^= s[0]; // 4 cycles
s[3] ^= s[1]; // 4 cycles
s[1] ^= s[2]; // 4 cycles
s[0] ^= s[3]; // 4 cycles

s[2] ^= t; // 2 cycles

s[3] = rotl(s[3], 45); // 6 cycles

return result;
}
};

//Xoroshiro256+ ma 29 cykli.
Bogdan
2023-05-15 12:00:58 UTC
Permalink
Post by ***@gmail.com
Post by Bogdan
Post by ***@gmail.com
https://quick-bench.com
Jedyne sensowne zestawienie, po zliczeniu przez mnie ręcznie liczby cykli na operacje (wynik 22 do 13), które wykonują algorytmy, dostaję, gdy włączam optim=None. Jeżeli zaś włączę OFast xoroshiro dostaje takiego przyspieszenia, że wyprzedza drugi PRNG, według tego benchmarku.
const uint64_t s0 = s[0];
jako jeden cykl, bo następuje wywołanie zmiennej z tablicy. Ale nie jestem pewien, czy to tak należy szacować.
To zależy od poziomu optymalizacji.
Bez optymalizacji na samo to wziąłbym 1 cykl na kopię z pamięci do
rejestru i 1 na kopię z rejestru do innej pamięci. Ale wspomniany
dokument podaje np. 3 cykle na kopiowanie do pamięci, więc nawet to
nie jest takie oczywiste.
Z optymalizacją jest szansa, że "s0" siedzi w rejestrze, więc
wystarczy pewnie 1 cykl na załadowanie.
Oczywiście, jeśli s[0] jest ułożone na równym adresie.
Oczywiście, jeśli s[0] siedzi w cache, bo jeśli nie, to w najgorszym
przypadku mogą być może dziesiątki, jak nie setki cykli na pobranie z
głównej pamięci.
I pewnie jeszcze różne inne warunki, więc tabelki tabelkami, ale
najlepiej albo pomierzyć (RDTSC), albo użyć narzędzi mówiących, co ile
potrwa (kiedyś było np. jakieś VTune Analyzer).
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
class xoroshiro256plus {
uint64_t s[4] = { 5, 11, 13, 99 };
static uint64_t rotl(const uint64_t x, int k)
{
return (x << k) | (x >> (64 - k));
}
uint64_t next() noexcept
{
const uint64_t result = s[0] + s[3]; // 3 cycles
const uint64_t t = s[1] << 17; // 2 cycles
s[2] ^= s[0]; // 4 cycles
s[3] ^= s[1]; // 4 cycles
s[1] ^= s[2]; // 4 cycles
s[0] ^= s[3]; // 4 cycles
s[2] ^= t; // 2 cycles
s[3] = rotl(s[3], 45); // 6 cycles
return result;
}
};
//Xoroshiro256+ ma 29 cykli.
Jak już pisałem - to może zależeć od konkretnego modelu procesora...
Nie tylko od tego, że jest 64-bitowy. I od poziomu optymalizacji.

result = s[0] + s[3];
// jeśli result idzie do pamięci
// mov + mov + add + mov = 2+2+1+3
// mov + add + mov = 2+6+3
// jeśli result idzie do rejestru
// mov + mov + add = 2+2+1
// mov + add = 2+6

const uint64_t t = s[1] << 17;
// jeśli t idzie do pamięci
// mov + shl + mov = 2+1+3
// jeśli t idzie do rejestru
// mov + shl = 2+1

I tak dalej...
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
osobli...@gmail.com
2023-05-14 14:39:04 UTC
Permalink
Post by Bogdan
Post by ***@gmail.com
https://quick-bench.com
Jedyne sensowne zestawienie, po zliczeniu przez mnie ręcznie liczby cykli na operacje (wynik 22 do 13), które wykonują algorytmy, dostaję, gdy włączam optim=None. Jeżeli zaś włączę OFast xoroshiro dostaje takiego przyspieszenia, że wyprzedza drugi PRNG, według tego benchmarku.
const uint64_t s0 = s[0];
jako jeden cykl, bo następuje wywołanie zmiennej z tablicy. Ale nie jestem pewien, czy to tak należy szacować.
To zależy od poziomu optymalizacji.
Bez optymalizacji na samo to wziąłbym 1 cykl na kopię z pamięci do
rejestru i 1 na kopię z rejestru do innej pamięci.
To jest to samo co niejakie load/store time? Jeżeli w algorytmie mam:

k = k + x;

To dobrze rozumiem, że mam liczyć to jako 4 cykle? Bo jeden cykl na pobranie k, drugi cykl na pobranie x, trzeci cykl na dodawanie i czwarty cykl na przypisanie wyniku do k?
Bogdan
2023-05-15 12:03:13 UTC
Permalink
Post by ***@gmail.com
Post by Bogdan
Post by ***@gmail.com
https://quick-bench.com
Jedyne sensowne zestawienie, po zliczeniu przez mnie ręcznie liczby cykli na operacje (wynik 22 do 13), które wykonują algorytmy, dostaję, gdy włączam optim=None. Jeżeli zaś włączę OFast xoroshiro dostaje takiego przyspieszenia, że wyprzedza drugi PRNG, według tego benchmarku.
const uint64_t s0 = s[0];
jako jeden cykl, bo następuje wywołanie zmiennej z tablicy. Ale nie jestem pewien, czy to tak należy szacować.
To zależy od poziomu optymalizacji.
Bez optymalizacji na samo to wziąłbym 1 cykl na kopię z pamięci do
rejestru i 1 na kopię z rejestru do innej pamięci.
k = k + x;
To dobrze rozumiem, że mam liczyć to jako 4 cykle? Bo jeden cykl na pobranie k, drugi cykl na pobranie x, trzeci cykl na dodawanie i czwarty cykl na przypisanie wyniku do k?
Zależy od konkretnego modelu procesora i od poziomu optymalizacji. I
od rozmiaru zmiennych, i od cache, i od ułożenia w pamięci.
Jeśli 'k' jest 64-bitowe, to:

1) MOV 'k' z pamięci do rejestru = 2, MOV 'x' z pamięci do rejestru =
2, ADD = 1, potem ewentualne czekanie na wynik, potem MOV nowej
wartości do pamięci = 3.

2) MOV 'x' z pamięci do rejestru = 2, ADD 'x' do 'k' w pamięci z
rejestru = 6.

Najlepiej mierzyć fizycznie, co jest najszybsze.
Jak mierzenie jest trudne, to można chociaż zobaczyć, jakie
instrukcje kompilator generuje (z różnymi poziomami optymalizacji i
wyboru architektury docelowej) i dopiero mając je, wziąć tabele czasów
instrukcji i liczyć.
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
osobli...@gmail.com
2023-05-15 13:10:17 UTC
Permalink
A gdyby użyć tego:

https://godbolt.org

Jak wrzucam sobie tam kod:

#include <cstdint>

inline uint64_t rotl(const uint64_t x, int k) {
return (x << k) | (x >> (64 - k));
}


uint64_t s[2] = {1,2};

uint64_t next(void) {
const uint64_t s0 = s[0];
uint64_t s1 = s[1];
const uint64_t result = s0 + s1;

s1 ^= s0;
s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b
s[1] = rotl(s1, 37); // c

return result;
}

To ma sens analiza tego:

movq s(%rip), %rax
movq s+8(%rip), %rsi
movq %rax, %rdx
movq %rax, %rcx
addq %rsi, %rax
xorq %rsi, %rdx
rolq $24, %rcx
movq %rdx, %rdi
xorq %rdx, %rcx
rorq $27, %rdx
salq $16, %rdi
movq %rdx, s+8(%rip)
xorq %rdi, %rcx
movq %rcx, s(%rip)
ret

Jak rozumiem wszystko to są standardowe nazwy instrukcji? Więc wystarczy zsumować ich cykle? Mogę założyć, że movq to 2 cykle, xorq to 1 cykl, salq to 1 cykl?
osobli...@gmail.com
2023-05-15 16:02:31 UTC
Permalink
Swoją drogą jak biorę wartości z tych tabel:

https://www.agner.org/optimize/instruction_tables.pdf

Mam brać pod uwagę sumę Ops i Latency?
Wojciech Muła
2023-05-22 17:30:29 UTC
Permalink
Post by ***@gmail.com
https://www.agner.org/optimize/instruction_tables.pdf
Mam brać pod uwagę sumę Ops i Latency?
Pytałeś już o to dwa lata temu. I nie, ops i latency to dwie **kompletnie** różne rzeczy - przeczytaj dokładnie opisy kolumn.

Można teoretycznie oszacować throughput małego kawałka kodu (np. na https://uica.uops.info), można nawet oszacować dolne ograniczenie latency ze ścieżki krytycznej. Między Tobą a ISA jest kompilator, runtime, system operacyjny i wg mnie warto mierzyć czas wykonania dla dużej liczby iteracji, eksprymentując z ustawieniami kompilatora. Od wielu lat używam tego zestawu makr do mierzenia cykli: https://github.com/WojciechMula/toys/blob/master/000helpers/benchmark.h. Możesz zobaczyć w tym repo jak z tego korzystać, np. tutaj: https://github.com/WojciechMula/toys/blob/master/avx512-varuint/benchmark.cpp#L41

w.
osobli...@gmail.com
2023-06-02 09:01:55 UTC
Permalink
Post by Wojciech Muła
Post by ***@gmail.com
https://www.agner.org/optimize/instruction_tables.pdf
Mam brać pod uwagę sumę Ops i Latency?
Pytałeś już o to dwa lata temu. I nie, ops i latency to dwie **kompletnie** różne rzeczy - przeczytaj dokładnie opisy kolumn.
Fakt, wtedy chodziło mi tylko o rzędy wielkości, bo nie miałem pojęcia ile to w ogóle zajmuje. Dzisiaj porównuję mój kod z różnymi wersjami xoroshiro:

https://prng.di.unimi.it/xoshiro256plusplus.c
Post by Wojciech Muła
Można teoretycznie oszacować throughput małego kawałka kodu (np. na https://uica.uops.info), można nawet oszacować dolne ograniczenie latency ze ścieżki krytycznej.
Nie wiem, czy czegoś podobnego nie robi Godbolt:

https://godbolt.org/z/7rxMdKerz

[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * movq _ZL1s.0(%rip), %rcx
1 5 0.50 * movq _ZL1s.3(%rip), %rdx
1 1 0.50 leaq (%rdx,%rcx), %rax
1 1 0.50 rolq $23, %rax
1 1 0.25 addq %rcx, %rax
1 5 0.50 * movq _ZL1s.1(%rip), %rsi
1 1 0.25 movq %rsi, %rdi
1 1 0.50 shlq $17, %rdi
1 5 0.50 * movq _ZL1s.2(%rip), %r8
1 1 0.25 xorq %rcx, %r8
1 1 0.25 xorq %rsi, %rdx
1 1 0.25 xorq %r8, %rsi
1 1 1.00 * movq %rsi, _ZL1s.1(%rip)
1 1 0.25 xorq %rdx, %rcx
1 1 1.00 * movq %rcx, _ZL1s.0(%rip)
1 1 0.25 xorq %rdi, %r8
1 1 1.00 * movq %r8, _ZL1s.2(%rip)
1 1 0.50 rolq $45, %rdx
1 1 1.00 * movq %rdx, _ZL1s.3(%rip)
3 7 1.00 U retq
Post by Wojciech Muła
Między Tobą a ISA jest kompilator, runtime, system operacyjny i wg mnie warto mierzyć czas wykonania dla dużej liczby iteracji, eksprymentując z ustawieniami kompilatora.
Tak to obecnie mierzę. Kompiluję z takimi flagami g++ -o test benchmarks_PRNGs.cpp -Wall -Wextra -O3 -fno-unroll-loops. No i coś tam mi wychodzi. Wyniki różnią się od tych z Godbolt. Wydaje mi się, że ze względu na mnożenie w moich generatorze niewiele tutaj można zrobić (przyspieszyć). Tym bardziej, że, jeśli porównuję wyniki xoroshiro i PCG-DXSM:

static __uint128_t LCG_state = 123456789;

uint64_t next_PCG(void)
{
LCG_state = LCG_state * 0xda942042e4dd58b5 + 1;
uint64_t hi = LCG_state >> 64;
uint64_t lo = LCG_state;

lo |= 1;
hi ^= hi >> 32;
hi *= 0xda942042e4dd58b5ULL;
hi ^= hi >> 48;
hi *= lo;
return hi;
}

To dostaję podobne wyniki do prof. Vigny https://prng.di.unimi.it. PCG jest jakieś 1,5 razy wolniejszy. Zastanawiam się jeszcze tylko, czy mnożenie można jakoś zoptymalizować. Twórczyni PCG Melissa O'Neil pisała gdzieś, że w jej pomiarach PCG jest tylko nieznacznie wolniejszy od xoroshiro. Tutaj:

https://github.com/numpy/numpy/pull/13163#issuecomment-496030958

wyszło komuś z Numpy, że na Linuxie PCG jest tylko nieznacznie wolniejszy od xoroshiro, a na Windowsie różnica jest ponad dwukrotna. Prędkość zależna od platformy?
Post by Wojciech Muła
Od wielu lat używam tego zestawu makr do mierzenia cykli: https://github.com/WojciechMula/toys/blob/master/000helpers/benchmark.h. Możesz zobaczyć w tym repo jak z tego korzystać, np. tutaj: https://github.com/WojciechMula/toys/blob/master/avx512-varuint/benchmark.cpp#L41
Dzięki, ale nie wiem jak tego użyć.
osobli...@gmail.com
2023-06-02 12:11:42 UTC
Permalink
Tutaj Vignie wyszły podobne wyniki przy użyciu Intel® Architecture Code Analyzer, 3.50 cykla dla xoroshiro128++ i 5.53 cykla dla PCG64DXSM:

https://pcg.di.unimi.it/pcg.php

Także, jeśli uzyskuję podobne wyniki, chyba robię pomiary w miarę reprezentatywnie. I chyba niewiele można tu przyspieszyć. Nie rozumiem tylko dlaczego ludziom z Numpy wyszły wyrównane wyniki i Melissie O'Neil też takie ponoć wychodzą.
osobli...@gmail.com
2023-05-13 17:34:32 UTC
Permalink
Ok, widzę, że dla 64-bitowych liczb to też jest 4-5 cykli.
Loading...