Dzisiaj do wyścigu przystępują:
popularna funkcja REGEXP_REPLACE
oraz stary dobry TRANSLATE.
TOR- usunięcie ze stringu wszystkich wartości numerycznych.
3 RUNDY na dystansach:
1000 rekordów
10000 rekordów
100000 rekordów
Kto wygra wyścig? Która funkcja szybciej wytnie wszystkie cyferki? (kod źródłowy na końcu posta).
Wyścig nr 1
Runda 1
DYSTANS: 1000 rekordów
Pierwszy na metę wpada TRANSLATE, ale dosłownie tuż po nim REGEXP_REPLACE.
Czas:
translate 1000: 0,9 sec
regexp_replace: 1000: 0,25 sec
Runda 2
DYSTANS: 10000 rekordów
Zwiększenie ilości rekordów nie wpłynęło zbytnio na wyniki wyścigu, znów obie funkcję wpadły na metę łeb w łeb, choć tym razem o sekundę szybszy był TRANSLATE.
Czas:
translate 10000: 1 sec
regexp_replace 10000: 2 sec
Runda 3
DYSTANS: 100 000 rekordów
Zwiększenie dystansu do 100 000 nie wyłoniło ostatecznego zwycięzcy, znów wyniki były bardzo zbliżone.
translate 1000000: 19 sec
regexp_replace 1000000: 23 sec
Wyścig nierozstrzygnięty.
Czyżby obie funkcję w tym zadaniu były tak samo szybkie?
Postanowiłam ponowić wyścig, tym razem zmieniając nieco warunki zewnętrzne, ze słonecznej pogody na deszcz...
Wyścig nr 2
Runda 1
DYSTANS: 1000 rekordów
Pierwszy na metę wpada TRANSLATE wyprzedzając REGEXP'a.
translate 1000: 0,2
regexp_replace 1000: 4,7
Runda 2
DYSTANS: 10000 rekordów
TRANSLATE zdecydowanie wygrywa tę rundę, REGEXP zadyszany wpada na metę daleko za rywalem.
translate 10000: 1 sec
regexp_replace 10000: 45 sec
Runda 3
DYSTANS: 100 000 rekordów
TRANSLATE nie daje szans rywalowi! Do czasu, gdy REGEXP wpada na linie mety, TRANSLATE zdarzył wziąć prysznic i odpocząć przy kolorowym drinku!
translate 100000: 19 sec
regexp_replace 100000: 454 sec
W wyścigu nr 2 czas funkcji REGEXP_REPLACE wydłużył się dwudziestokrotnie!
Kto tak obstawiał?
Ale co się zmieniło? Dlaczego w pierwszym teście TRANSLATE i REGEXP_REPLACE szły łeb w łeb a w teście drugim TRANSLATE zostawił rywala daleko w tyle? Co wpłynęło na tak drastyczne spowolnienie REGEXP_REPLECE'a?
Czas na rozwiązanie: w wyścigu nr 2 zmieniłam jedynie ustawienia NLS_LANGUAGE sesji.
Pierwszy test, gdzie obie funkcje miały podobne czasy, został przeprowadzony przy ustawieniu NLS_LANGUAGE = ENGLISH, drugi zaś, gdzie TRANSLATE zdecydowanie zwyciężył, został przeprowadzony dla NLS_LANGUAGE = POLISH. Jak widać, wydajność funkcji REGEXP zależy w bardzo dużym stopniu od ustawionego języka!
Jeśli używacie funkcji REGEXP - zwróćcie uwagę na ustawienia NLS_LANGUAGE - być może zmiana tych ustawień lub rezygnacja z funkcji REGEXP na rzecz np. standardowej funkcji TRANSLATE pozwoli wam oszczędzić cenne minuty!
Skrypt testowy:
Testy przeprowadziłam na bazie Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production.
Testy przeprowadziłam na bazie Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production.
set serveroutpu on
DECLARE
TYPE tab IS TABLE OF VARCHAR2(4000);
t tab;
cnLoops constant NUMBER := 100;
nLoops number := 0;
vLang varchar2(20);
vTestLang varchar2(20);
vChar varchar2(4000);
cursor cur is
SELECT dbms_random.string('x',2000)
FROM dual
CONNECT BY level <= nLoops;
cnt number := 0;
nTimeStart number := dbms_utility.get_time;
nTimeEnd number;
BEGIN
for ncnt in 1..3 loop
nLoops := case ncnt when 1 then 1000
when 2 then 10000
when 3 then 100000
end ;
nTimeStart := dbms_utility.get_time;
open cur;
fetch cur bulk collect into t;
nTimeEnd := dbms_utility.get_time;
DBMS_OUTPUT.PUT_LINE('Fetch '|| nLoops|| ': '|| trunc( (nTimeEnd - nTimeStart)/100,2) );
for vTestLang in 1..2 loop
if vTestLang = 2 then
vLang := 'POLISH';
else
vLang := 'ENGLISH';
end if;
execute immediate 'ALTER SESSION SET NLS_LANGUAGE = '||vLang;
cnt := 0;
nTimeStart := dbms_utility.get_time;
FOR i IN 1..t.count LOOP
vchar := translate(t(i),' 0987654321',' ');
cnt := cnt + 1;
END LOOP;
nTimeEnd := dbms_utility.get_time;
DBMS_OUTPUT.PUT_LINE('Translate '|| cnt ||';' || vLang || ';'|| nLoops|| ': '|| trunc( (nTimeEnd - nTimeStart)/100,2)) ;
cnt := 0;
nTimeStart := dbms_utility.get_time;
FOR i IN 1..t.count LOOP
vchar := regexp_replace(t(i),'\d','');
cnt := cnt + 1;
END LOOP;
nTimeEnd := dbms_utility.get_time;
DBMS_OUTPUT.PUT_LINE('regexp '|| cnt ||';' || vLang || ';'|| nLoops|| ': '|| trunc((nTimeEnd - nTimeStart)/100,2));
end loop;
close cur;
end loop;
exception when others then
dbms_output.put_line(sqlerrm);
END;
/
Komentarze
Prześlij komentarz