Czy wektory indeksowania w programie MATLAB są nieefektywne?

tło

Moje pytanie jest motywowane prostymi obserwacjami, które podważają przekonania / założenia często utrzymywane / tworzone przez doświadczonych użytkowników MATLAB:

MATLAB jest bardzo dobrze zoptymalizowany, jeśli chodzi o wbudowane funkcje i podstawowe funkcje językowe, takie jak indeksowanie wektorów i macierzy.Pętle w MATLABie są powolne (pomimo JIT) i generalnie należy ich unikać, jeśli algorytm można wyrazić w natywny, „wektorowy” sposób.

Najważniejsze: podstawowa funkcjonalność MATLAB jest wydajna, a próba przewyższenia jej za pomocą kodu MATLAB jest trudna, jeśli nie niemożliwa.

Badanie wydajności indeksowania wektorów

Przykładowe kody pokazane poniżej są tak fundamentalne, jak to możliwe: przypisuję wartość skalarną do wszystkich wpisów wektorowych. Po pierwsze, przydzielam pusty wektorx:

tic; x = zeros(1e8,1); toc
Elapsed time is 0.260525 seconds.

Mającyx Chciałbym ustawić wszystkie jego wpisy na tę samą wartość. W praktyce robiłbyś to inaczej, np.x = value*ones(1e8,1), ale tutaj chodzi o zbadanie wydajności indeksowania wektorów. Najprostszym sposobem jest napisanie:

tic; x(:) = 1; toc
Elapsed time is 0.094316 seconds.

Nazwijmy to metodą 1 (od wartości przypisanej dox). Wydaje się być bardzo szybki (szybszy co najmniej niż przydział pamięci). Ponieważ jedyne, co tutaj robię, to operowanie pamięcią, mogę oszacować wydajność tego kodu, obliczając uzyskanyefektywna przepustowość pamięci i porównanie go doprzepustowość pamięci sprzętowej mojego komputera:

eff_bandwidth = numel(x) * 8 bytes per double * 2 / time

W powyższym, mnożę się przez2 ponieważ jeśli nie używa się strumieniowania SSE, ustawienie wartości w pamięci wymaga, aby wektor był zarówno odczytywany, jak i zapisywany w pamięci. W powyższym przykładzie:

eff_bandwidth(1) = 1e8*8*2/0.094316 = 17 Gb/s

Przepustowość pamięci porównywana ze STREAM mojego komputera wynosi około 17,9 Gb / s, więc rzeczywiście - w tym przypadku MATLAB zapewnia niemal szczytową wydajność! Jak na razie dobrze.

Metoda 1 jest odpowiednia, jeśli chcesz ustawićwszystko elementy wektorowe do pewnej wartości. Ale jeśli chcesz uzyskać dostęp do wszystkich elementówstep wpisy, musisz zastąpić: np. z1:step:end. Poniżej znajduje się bezpośrednie porównanie prędkości z metodą 1:

tic; x(1:end) = 2; toc
Elapsed time is 0.496476 seconds.

Chociaż nie spodziewałbyś się, że będzie ona działać inaczej, metoda 2 jest wyraźnie dużym problemem: spowolnienie czynnika 5 bez powodu. Podejrzewam, że w tym przypadku MATLAB wyraźnie alokuje wektor indeksu (1:end). Jest to nieco potwierdzone przez użycie wyraźnego rozmiaru wektora zamiastend:

tic; x(1:1e8) = 3; toc
Elapsed time is 0.482083 seconds.

Metody 2 i 3 działają równie źle.

Inną możliwością jest jawne utworzenie wektora indeksuid i użyj go do indeksowaniax. Zapewnia to najbardziej elastyczne możliwości indeksowania. W naszym przypadku:

tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc
Elapsed time is 1.208419 seconds.

To naprawdę coś - 12 razy spowolnienie w porównaniu z metodą 1! Rozumiem, że powinien działać gorzej niż metoda 1 z powodu dodatkowej pamięci używanejid, ale dlaczego jest o wiele gorzej niż metody 2 i 3?

Spróbujemy spróbować pętli - beznadziejnie, jak mogłoby się wydawać.

tic;
for i=1:numel(x)
    x(i) = 5;
end
toc
Elapsed time is 0.788944 seconds.

Duża niespodzianka - pętla bijevectorized metoda 4, ale nadal jest wolniejsza niż metody 1, 2 i 3. Okazuje się, że w tym konkretnym przypadku można to zrobić lepiej:

tic;
for i=1:1e8
    x(i) = 6;
end
toc
Elapsed time is 0.321246 seconds.

I to jest chyba najbardziej dziwaczny wynik tego badania -zapisana w MATLAB pętla znacznie przewyższa natywne indeksowanie wektorowe. Z pewnością tak nie powinno być. Należy zauważyć, że pętla JIT jest wciąż 3 razy wolniejsza niż pik teoretyczny prawie otrzymany metodą 1. Zatem nadal jest dużo miejsca na poprawę. To po prostu zaskakujące (bardziej odpowiednie byłoby silniejsze słowo) niż zwykłe indeksowanie wektoryzacyjne (1:end) jest jeszcze wolniejszy.

pytania

to proste indeksowanie w MATLABbardzo nieefektywny (metody 2, 3 i 4 są wolniejsze niż metoda 1), czy coś przegapiłem?dlaczego jest metoda 4(tyle) wolniej niż metody 2 i 3?dlaczego używa1e8 zamiastnumel(x) w pętli przyspieszyć kod o współczynnik 2?

Edytować Po przeczytaniu komentarza Jonasa, oto inny sposób na zrobienie tego za pomocą indeksów logicznych:

tic;
id = logical(ones(1, 1e8));
x(id) = 7;
toc
Elapsed time is 0.613363 seconds.

Znacznie lepiej niż metoda 4.

Dla wygody:

function test

tic; x = zeros(1,1e8); toc

tic; x(:) = 1; toc
tic; x(1:end) = 2; toc
tic; x(1:1e8) = 3; toc

tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc

tic;
for i=1:numel(x)
    x(i) = 5;
end
toc

tic;
for i=1:1e8
    x(i) = 6;
end
toc

end

questionAnswers(2)

yourAnswerToTheQuestion