Wydajność przypisywania wartości do tablicy

Optymalizacja kodu mówi się tutaj, że profilowanie jest pierwszym krokiem do optymalizacji javascript, a sugerowane silniki to profilery Chrome i Firefox. Problem z tym polega na tym, że w jakiś dziwny sposób określają czas wykonania każdej funkcji, ale nie rozumiem ich dobrze. Najbardziej pomocny byłby sposób, w jaki profiler powiedziałby, ile razy każdy wiersz jest wykonywany, a jeśli to możliwe, także czas spędzony w każdym wierszu. W ten sposób będzie można zobaczyć wąskie gardła. Ale zanim takie narzędzie zostanie zaimplementowane / znalezione, mamy dwie opcje:

1) stwórz własny kalkulator, który zlicza zarówno czas, jak i ile razy wykonywany jest określony blok kodu lub wiersz 2) nauczyć się rozumieć, które metody są powolne, a które nie

Dla opcji 2jsperf.com jest bardzo pomocny. Próbowałem nauczyć się optymalizacji macierzy i przeprowadzić test prędkościJSPERF.COM. Poniższy obraz pokazuje wyniki w 5 głównych przeglądarkach i znalazł pewne wąskie gardła, o których wcześniej nie wiedziałam.

Główne wnioski były następujące:

1) Przypisywanie wartości do tablic jest znacznie wolniejsze niż przypisywanie do zmiennych normalnych, niezależnie od tego, która metoda jest używana do przypisywania.

2) Wstępne inicjowanie i / lub wstępne wypełnianie macierzy przed pętlami o krytycznym znaczeniu może znacznie zwiększyć prędkość

3) Funkcje trygonometryczne matematyki nie są tak powolne w porównaniu z wypychaniem wartości do tablic (!)

Oto wyjaśnienia każdego testu:

1. non_array (100%):

Zmienne otrzymały w ten sposób wstępnie zdefiniowaną wartość:

var non_array_0=0;
var non_array_1=0;
var non_array_2=0;
...

w regionie czasowym nazywano je w ten sposób:

non_array_0=0;
non_array_1=1;
non_array_2=2;
non_array_3=3;
non_array_4=4;
non_array_5=5;
non_array_6=6;
non_array_7=7;
non_array_8=8;
non_array_9=9;

Powyższa zmienna jest podobna do tablicy, ale wydaje się, że nie ma możliwości iteracji lub odwoływania się do tych zmiennych w inny sposób niż oppocite do tablicy. Czy istnieje?

Nic w tym teście nie jest szybsze niż przypisanie liczby do zmiennej.

2. non_array_non_pre (83,78%)

Dokładnie tak samo jak test 1, ale zmienne nie zostały wstępnie zainicjowane ani wstępnie wypełnione. Prędkość wynosi 83,78% prędkości testu 1. W każdej testowanej przeglądarce szybkość wstępnie wypełnionych zmiennych była szybsza niż nie wypełniona.Więc zainicjuj (i ewentualnie wypełnij) zmienne poza dowolnymi krytycznymi pętlami prędkości.

Kod testu jest tutaj:

var non_array_non_pre_0=0;
var non_array_non_pre_1=0;
var non_array_non_pre_2=0;
var non_array_non_pre_3=0;
var non_array_non_pre_4=0;
var non_array_non_pre_5=0;
var non_array_non_pre_6=0;
var non_array_non_pre_7=0;
var non_array_non_pre_8=0;
var non_array_non_pre_9=0;

3. pre_filled_array (19,96%):

Tablice są złe! Kiedy odrzucamy zwykłe zmienne (test1 i test2) i wprowadzamy tablice do obrazu, prędkość znacznie się zmniejsza.Chociaż wykonujemy wszystkie optymalizacje (preinicjalizacja i wstępne wypełnianie tablic), a następnie przypisujemy wartości bezpośrednio bez zapętlania lub pchania, prędkość spada do 19,96 procent. To bardzo smutne i naprawdę nie rozumiem, dlaczego tak się dzieje. To był jeden z głównych wstrząsów w tym teście. Tablice są tak ważne i nie znalazłem sposobu, aby zrobić wiele rzeczy bez tablic.

Dane testowe są tutaj:

pre_filled_array[0]=0;
pre_filled_array[1]=1;
pre_filled_array[2]=2;
pre_filled_array[3]=3;
pre_filled_array[4]=4;
pre_filled_array[5]=5;
pre_filled_array[6]=6;
pre_filled_array[7]=7;
pre_filled_array[8]=8;
pre_filled_array[9]=9;

4. non_pre_filled_array (8,34%):

Jest to ten sam test co 3, ale elementy tablicy nie są wstępnie zdefiniowane ani wstępnie wypełnione, tylko optymalizacja polegała na wcześniejszym zainicjowaniu tablicy:var non_pre_filled_array=[];

Prędkość spada o 58,23% w porównaniu z testem wstępnie wstępnie zryfikowanym 3.Tak więc wstępna inicjalizacja i / lub wstępne napełnianie tablicy podwaja prędkość.

Kod testu jest tutaj:

non_pre_filled_array[0]=0;
non_pre_filled_array[1]=1;
non_pre_filled_array[2]=2;
non_pre_filled_array[3]=3;
non_pre_filled_array[4]=4;
non_pre_filled_array[5]=5;
non_pre_filled_array[6]=6;
non_pre_filled_array[7]=7;
non_pre_filled_array[8]=8;
non_pre_filled_array[9]=9;

5. pre_filled_array [i] (7,10%):

Potem do pętli. Najszybsza metoda pętli w tym teście. Tablica została wstępnie zainicjowana i wstępnie wypełniona.

Spadek prędkości w porównaniu z wersją inline (test 3) wynosi 64,44%. To jest tak niezwykła różnica, że ​​powiedziałbym, nie zapętlaj, jeśli nie jest potrzebna. Jeśli rozmiar tablicy jest niewielki (nie wiem, jak mały, należy go przetestować oddzielnie), używanie przypisań wbudowanych zamiast pętli jest mądrzejsze.

A ponieważ spadek prędkości jest tak ogromny i naprawdę potrzebujemy pętli, dobrze jest znaleźćlepsza metoda zapętlania (na przykład.while(i--)).

Kod testu jest tutaj:

for(var i=0;i<10;i++)
{
  pre_filled_array[i]=i;
}

6. non_pre_filled_array [i] (5,26%):

Jeśli nie wstępnie zainicjalizujemy i nie wstępnie wypełnimy tablicy, prędkość zmniejszy się o 25,96%. Ponownie, wstępne inicjowanie i / lub wstępne wypełnianie przed pętlami krytycznymi dla prędkości jest mądre.

Kod jest tutaj:

for(var i=0;i<10;i++) 
{
  non_pre_filled_array[i]=i;
}

7. Obliczenia matematyczne (1,17%):

Każdy test musi być punktem odniesienia. Funkcje matematyczne są uważane za wolne. Test składał się z dziesięciu „ciężkich” obliczeń matematycznych, ale teraz przychodzi kolejna rzecz, która uderzyła mnie w tym teście. Spójrz na prędkość 8 i 9, gdzie wypychamy dziesięć liczb całkowitych do tablicy w pętli. Obliczanie tych 10 funkcji matematycznych jest o ponad 30% szybsze niż wypychanie dziesięciu liczb całkowitych do macierzy w pętli. Być może łatwiej jest przekonwertować niektóre pchnięcia tablicy na wstępnie zdefiniowane nie-tablice i zachować te trygonometrie. Oczywiście, jeśli istnieje setka lub tysiące obliczeń na ramkę, dobrze jest użyć np. sqrt zamiast sin / cos / tan i używaj odległości taksówek do porównań odległości ikąty diamentu (t-radiany) dla porównań kątowych, ale nadal głównym wąskim gardłem może być gdzie indziej: pętla jest wolniejsza niż wstawianie, pchanie jest wolniejsze niż bezpośrednie przypisanie z wstępną filtracją i / lub wstępnym napełnianiem, logika kodu, algorytmy rysowania i dostęp DOM mogą być powolne. Wszystkie nie mogą być zoptymalizowane w JavaScript (musimy zobaczyć coś na ekranie!), Ale wszystko jest łatwe i znaczące, co możemy zrobić, jest mądre. Ktoś tutaj w SO powiedział, że kod jest dla ludzi, a czytelny kod jest bardziej istotny niż szybki kod, ponieważ koszt utrzymania jest największym kosztem. Jest to ekonomiczny punkt widzenia, ale odkryłem, że optymalizacja kodu może uzyskać zarówno elegancję, jak i czytelność oraz wydajność. A jeśli osiągnięty zostanie wzrost wydajności o 5%, a kod jest bardziej prosty, to daje dobre samopoczucie!

Kod jest tutaj:

non_array_0=Math.sqrt(10435.4557);
non_array_1=Math.atan2(12345,24869);
non_array_2=Math.sin(35.345262356547);
non_array_3=Math.cos(232.43575432);
non_array_4=Math.tan(325);
non_array_5=Math.asin(3459.35498534536);
non_array_6=Math.acos(3452.35);
non_array_7=Math.atan(34.346);
non_array_8=Math.pow(234,222);
non_array_9=9374.34524/342734.255;

8. pre_filled_array.push (i) (0,8%):

Push jest zły! Wciśnij połączony do pętli jest złym złem! Jest to z jakiegoś powodu bardzo powolna metoda przypisywania wartości do tablicy. Test 5 (bezpośrednie przypisania w pętli) jest prawie 9 razy szybszy niż ta metoda, a obie metody robią dokładnie to samo: przypisz liczbę całkowitą 0-9 do wstępnie zdefiniowanej i wstępnie wypełnionej tablicy. Nie testowałem, czy ta zła push-for-loop jest spowodowana pchaniem lub zapętlaniem lub kombinacją obu lub liczby pętli. W JSPERF.COM znajdują się inne przykłady, które dają sprzeczne wyniki. Mądrzej jest przetestować tylko rzeczywiste dane i podejmować decyzje. Ten test może nie być zgodny z innymi danymi niż ten, który został użyty.

A oto kod:

for(var i=0;i<10;i++)
{
  pre_filled_array.push(i);
}

9. non_pre_filled_array.push (i) (0,74%):

Ostatnia i najwolniejsza metoda w tym teście jest taka sama jak test 8, ale tablica nie jest wstępnie wypełniona. Trochę wolniej niż 9, ale różnica nie jest znacząca (7,23%). Ale weźmy przykład i porównajmy tę najwolniejszą metodę z najszybszą.Prędkość tej metody wynosi 0,74% prędkości metody 1, co oznacza, że ​​metoda 1 jest 135 razy szybsza. Zastanów się więc uważnie, jeśli w konkretnym przypadku tablice są w ogóle potrzebne. Jeśli jest to tylko jeden lub kilka naciśnięć, całkowita różnica prędkości nie jest zauważalna, ale z drugiej strony, jeśli jest tylko kilka naciśnięć, są one bardzo proste i eleganckie do konwersji na zmienne inne niż tablicowe.

To jest kod:

for(var i=0;i<10;i++)
{
  non_pre_filled_array.push(i);
}

I wreszcie obowiązkowe pytanie SO:

Ponieważ różnica prędkości zgodnie z tym testem wydaje się być tak duża między przypisaniami do zmiennych bez tablic i przydziałami tablic, czy jest jakaś metoda, aby uzyskać szybkość przypisywania zmiennych bez tablicy i dynamiki tablic?

Nie mogę użyćvar variable_$i = 1 w pętli, tak że $ i jest konwertowany na jakąś liczbę całkowitą. Muszę użyćvar variable[i] = 1 który jest znacznie wolniejszy niżvar variable1 = 1 jak udowodnił test. Może to być krytyczne tylko wtedy, gdy istnieją duże tablice iw wielu przypadkach są.

EDIT: Zrobiłem nowy test, aby potwierdzić powolność dostępu do tablic i próbowałem znaleźć szybszy sposób:

http://jsperf.com/read-write-array-vs-variable

Array-read i / lub array-write są znacznie wolniejsze niż przy użyciu normalnych zmiennych. Jeśli niektóre operacje są wykonywane na elementach tablicy, mądrzej jest zapisać wartość elementu tablicy do zmiennej temp, uczynić te operacje zmienną temp i ostatecznie zapisać wartość w elemencie array. I chociaż kod staje się większy, znacznie szybsze jest wykonywanie tych operacji w linii niż w pętli.

Wniosek: tablice vs zmienne normalne są analogiczne do dysku względem pamięci. Zwykle dostęp do pamięci jest szybszy niż dostęp do dysku, a dostęp do normalnych zmiennych jest szybszy niż dostęp do tablicy. I może być, że operacje łączenia są również szybsze niż użycie zmiennych pośrednich, ale to sprawia, że ​​kod jest trochę nieczytelny.

questionAnswers(1)

yourAnswerToTheQuestion