Wygląda na to, że Mathworks posiada specjalne kwadratowe kwadraty w swojej funkcji mocy (niestety, wszystkie są wbudowane w zamknięte źródło, którego nie widzimy). W moich testach na R2013b wygląda na to, że używają tego samego algorytmu, co w przypadku wersji .^
, power
i realpow
. Jeśli chodzi o kwadraty, uważam, że mają one specjalny przypadek x.*x
.
1.0x (4.4ms): @()x.^2
1.0x (4.4ms): @()power(x,2)
1.0x (4.5ms): @()x.*x
1.0x (4.5ms): @()realpow(x,2)
6.1x (27.1ms): @()exp(2*log(x))
W przypadku kostek historia jest inna. Nie są już specjalnymi przypadkami. Znowu .^
, power
i realpow
wszystkie są podobne, ale znacznie wolniej tym razem: skok
1.0x (4.5ms): @()x.*x.*x
1.0x (4.6ms): @()x.*x.^2
5.9x (26.9ms): @()exp(3*log(x))
13.8x (62.3ms): @()power(x,3)
14.0x (63.2ms): @()x.^3
14.1x (63.7ms): @()realpow(x,3)
Przejdźmy do 16 potęgi, aby zobaczyć jak to skala algorytmy:
1.0x (8.1ms): @()x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x
2.2x (17.4ms): @()x.^2.^2.^2.^2
3.5x (27.9ms): @()exp(16*log(x))
7.9x (63.8ms): @()power(x,16)
7.9x (63.9ms): @()realpow(x,16)
8.3x (66.9ms): @()x.^16
Więc: .^
, power
i realpow
wszystkie działają w stałym czasie w odniesieniu do wykładnika, chyba że był to specjalny przypadek (-1 również wydaje się być specjalnym cased). Używanie triku exp(n*log(x))
to także stały czas w odniesieniu do wykładnika i szybszy. Jedynym rezultatem nie do końca rozumiem, dlaczego powtarzanie kwadratu jest wolniejsze niż mnożenie.
Zgodnie z oczekiwaniami zwiększenie rozmiaru x
o współczynnik równy 100 zwiększa czas podobnie dla wszystkich algorytmów.
A więc, moralny z historii? Podczas używania skalarnych wykładników całkowitych, zawsze rób to samemu. Jest mnóstwo sprytów w power
i znajomych (wykładnik może być zmiennoprzecinkowy, wektor itd.). Jedynymi wyjątkami są sytuacje, w których Mathworks przeprowadził optymalizację. W 2013b wydaje się być x^2
i x^(-1)
. Mam nadzieję, że dodadzą więcej w miarę upływu czasu. Generalnie jednak potęgowanie jest trudne, a mnożenie łatwe. W przypadku kodu wrażliwego na wydajność, nie sądzę, że możesz popełnić błąd, pisząc zawsze: x.*x.*x.*x
. (Oczywiście, w przypadku wykonaj porady Luis` i wykorzystania wyników pośrednich w ramach każdego terminu!)
function powerTest(x)
f{1} = @() x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x;
f{2} = @() x.^2.^2.^2.^2;
f{3} = @() exp(16.*log(x));
f{4} = @() x.^16;
f{5} = @() power(x,16);
f{6} = @() realpow(x,16);
for i = 1:length(f)
t(i) = timeit(f{i});
end
[t,idxs] = sort(t);
fcns = f(idxs);
for i = 1:length(fcns)
fprintf('%.1fx (%.1fms):\t%s\n',t(i)/t(1),t(i)*1e3,func2str(fcns{i}));
end
Optymalizacja który przypuszczalnie wykonane na 'x. * X. * X. * X' zachowuje dziwnie. Próbowałem 'x. *. X. * .... * X' z różną liczbą" x "od 2 do 8, a czas jest mniej więcej liniowo rosnący. Spodziewałbym się wstrząsów; na przykład przypadek "8" (=> x.^2.^2.^2': trzy operacje zasilania) powinien zająć mniej czasu niż "7" (=> więcej operacji zasilania) –
@LuisMendo Nie wiem jak zweryfikować, ale mogę sobie wyobrazić, że robi tylko 1 krok (bez zagnieżdżonej optymalizacji). Dla 7 zmniejszyłoby się to do czegoś takiego: 'x.^2 * x.^2 * x.^2. * x' które nie byłoby wolniejsze niż' x.^2 * x.^2 * x.^2 . * x.^2' dla 8. Jeśli wykonanie 8 było szybsze niż wykonanie 7 w ten sposób, Mathworks prawdopodobnie wprowadziłby taką optymalizację funkcji mocy. –
Tak, to może być wytłumaczenie: nie ma gniazdowania –