Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Testy szybkości i ciekawe wyniki
Forum PHP.pl > Forum > PHP
gWd
Wykonałem krótkie testy porównujące szybkości działania funkcji z parametrem przekazanym przez wartość, referencję i obiekt. Musze przyznać, że wyniki troche mnie zaskoczyły.

Środowisko:
PHP 5.2.1RC2
Apache 2.2.4

Przykład 1
  1. <?php
  2. class C1
  3. {
  4. public $data=array();
  5.  
  6. public function UstawZTablicy ($tab)
  7. {
  8. foreach ($tab as $key => $value)
  9. {
  10. $this->data[$key] = $value;
  11. }
  12. }
  13. public function ObjMet()
  14. {
  15. echo $this->data['pole8455'];
  16. }
  17. }
  18.  
  19. function Tab ($tab)
  20. {
  21. echo $tab['pole8455'];
  22. }
  23.  
  24. function TabRef (&$tab)
  25. {
  26. echo $tab['pole8455'];
  27. }
  28.  
  29. function ObjFunc ($obj)
  30. {
  31. echo $obj->pole8455;
  32. }
  33.  
  34. $tab = array();
  35. for ($ptl=1; $ptl<10000; $ptl++)
  36. {
  37. $tab['pole'.$ptl] = $ptl;
  38. }
  39. $obj = new C1();
  40. $obj->UstawZTablicy($tab);
  41.  
  42. $timeTests = array();
  43.  
  44. $timeTests[0]['czas'] = microtime(true);
  45. $timeTests[0]['znak'] = 'start';
  46.  
  47. //test funkcji z parametrem wartosciowym
  48. for ($ptl=0; $ptl<4000; $ptl++)
  49. {
  50. Tab($tab);
  51. }
  52.  
  53. $timeTests[4]['czas'] = microtime(true);
  54. $timeTests[4]['znak'] = 'Tab';
  55.  
  56. //test funkcji z przekazaniem jej parametru: obiekt
  57. for ($ptl=0; $ptl<4000; $ptl++)
  58. {
  59. ObjFunc($obj);
  60. }
  61.  
  62. $timeTests[1]['czas'] = microtime(true);
  63. $timeTests[1]['znak'] = 'ObjFunc';
  64.  
  65. //test metody obiektu
  66. for ($ptl=0; $ptl<4000; $ptl++)
  67. {
  68. $obj->ObjMet();
  69. }
  70.  
  71. $timeTests[2]['czas'] = microtime(true);
  72. $timeTests[2]['znak'] = 'ObjMet';
  73.  
  74. //test funkcji z parametrem referencyjnym
  75. for ($ptl=0; $ptl<4000; $ptl++)
  76. {
  77. TabRef($tab);
  78. }
  79.  
  80. $timeTests[3]['czas'] = microtime(true);
  81. $timeTests[3]['znak'] = 'TabRef';
  82.  
  83.  
  84. echo '<br />';
  85. $timePrev = 0;
  86. foreach ($timeTests as $time)
  87. {
  88. echo $time['znak'].': '.($time['czas'] - $timePrev).'<br />';
  89. $timePrev = $time['czas'];
  90. }
  91. ?>


Średnie wyniki z 10 pomiarów:
ObjFunc: 0,03630816936492920
ObjMet: 0,01625814437866210
Tab: 0,02549026012420650
TabRef: 0,01591291427612310

Opis wyników
ObjFunc jest 2x wolniejsze niż wywołanie metody obiektu ObjMet
Tab jest 1.6x wolniejsze od funkcji z parametrem referencyjnym!!!

Wnioski
1. Okazuje się, że wbrew dokumentacji PHP, przekazanie parametu przez referencję jest dużo szybsze niż przekazanie wartości. Należy się domyślać, że dotyczy to raczej zmiennych o dość rozmiarach.
2. Wywołanie metody obiektu jest znacznie szybsze niż (trochę dziwna) funkcja.

Drugi test jest jeszcze ciekawszy!
Tym razem modyfikujemy zmienną przekazaną do funkcji/metody.

Przykład 2
  1. <?php
  2. class C1
  3. {
  4. public $data=array();
  5.  
  6. public function UstawZTablicy ($tab)
  7. {
  8. foreach ($tab as $key => $value)
  9. {
  10. $this->data[$key] = $value;
  11. }
  12. }
  13. public function ObjMet()
  14. {
  15. $this->data['pole8455'] = 34534;
  16. }
  17. }
  18.  
  19. function Tab ($tab)
  20. {
  21. $tab['pole8455'] = 34534;
  22. }
  23.  
  24. function TabRef (&$tab)
  25. {
  26. $tab['pole8455'] = 34534;
  27. }
  28.  
  29. function ObjFunc ($obj)
  30. {
  31. $obj->pole8455 = 34534;
  32. }
  33.  
  34. $tab = array();
  35. for ($ptl=1; $ptl<10000; $ptl++)
  36. {
  37. $tab['pole'.$ptl] = $ptl;
  38. }
  39. $obj = new C1();
  40. $obj->UstawZTablicy($tab);
  41.  
  42. $timeTests = array();
  43.  
  44. $timeTests[0]['czas'] = microtime(true);
  45. $timeTests[0]['znak'] = 'start';
  46.  
  47. //test funkcji z parametrem wartosciowym
  48. for ($ptl=0; $ptl<4000; $ptl++)
  49. {
  50. Tab($tab);
  51. }
  52.  
  53. $timeTests[4]['czas'] = microtime(true);
  54. $timeTests[4]['znak'] = 'Tab';
  55.  
  56. //test funkcji z przekazaniem jej parametru: obiekt
  57. for ($ptl=0; $ptl<4000; $ptl++)
  58. {
  59. ObjFunc($obj);
  60. }
  61.  
  62. $timeTests[1]['czas'] = microtime(true);
  63. $timeTests[1]['znak'] = 'ObjFunc';
  64.  
  65. //test metody obiektu
  66. for ($ptl=0; $ptl<4000; $ptl++)
  67. {
  68. $obj->ObjMet();
  69. }
  70.  
  71. $timeTests[2]['czas'] = microtime(true);
  72. $timeTests[2]['znak'] = 'ObjMet';
  73.  
  74. //test funkcji z parametrem referencyjnym
  75. for ($ptl=0; $ptl<4000; $ptl++)
  76. {
  77. TabRef($tab);
  78. }
  79.  
  80. $timeTests[3]['czas'] = microtime(true);
  81. $timeTests[3]['znak'] = 'TabRef';
  82.  
  83.  
  84. echo '<br />';
  85. $timePrev = 0;
  86. foreach ($timeTests as $time)
  87. {
  88. echo $time['znak'].': ';
  89. echo ($time['czas'] - $timePrev).'<br />';
  90. $timePrev = $time['czas'];
  91. }
  92. ?>


Średnie wyniki z 10 pomiarów:
ObjFunc: 0,00535948276519776
ObjMet: 0,00558812618255616
Tab: 12,12336094379430000
TabRef: 0,00451698303222655

Opis wyników
ObjFunc i ObjMet mają bardzo zbliżone wyniki.
Zaskakuje natomiast funkcja Tab z wynikiem ponad 2000x gorszym od innych sposobów!!!! w tym od funkcji z parametrem referencyjnym.

Wnioski
1. Porównując wyniki z Przykładu 1 i 2 widzimy, że PHP wykonując funkcję z parametrem wartościowym nie tworzy kopii tego paremetru za każdym razem. Taka kopia generowana jest tylko wtedy, gdy zmienna ulega zmianie wewnątrz funkcji!
2. Przekazywanie do funkcji dużych zmiennych przez wartość jest wolniejsze niż przekazanie ich przez referencję, nawet jeśli zmienna nie ulega modyfikacji wewnątrz funkcji!
3. Wywoływanie i działanie fukcji/metod na obiektach może być nieco wolniejsze od funkcji na tablicach, ale różnica nie jest duża.
mike
Jaka wersja PHP?
gWd
Teraz wykonamy wersję testu 1, ale tym razem z 10x mniejszą zmienną.

Przykład 3
  1. <?php
  2. class C1
  3. {
  4. public $data=array();
  5.  
  6. public function UstawZTablicy ($tab)
  7. {
  8. foreach ($tab as $key => $value)
  9. {
  10. $this->data[$key] = $value;
  11. }
  12. }
  13. public function ObjMet()
  14. {
  15. echo $this->data['pole845'];
  16. }
  17. }
  18.  
  19. function Tab ($tab)
  20. {
  21. echo $tab['pole845'];
  22. }
  23.  
  24. function TabRef (&$tab)
  25. {
  26. echo $tab['pole845'] = 34534;
  27. }
  28.  
  29. function ObjFunc ($obj)
  30. {
  31. echo $obj->pole845;
  32. }
  33.  
  34. $tab = array();
  35. for ($ptl=1; $ptl<1000; $ptl++)
  36. {
  37. $tab['pole'.$ptl] = $ptl;
  38. }
  39. $obj = new C1();
  40. $obj->UstawZTablicy($tab);
  41.  
  42. $timeTests = array();
  43.  
  44. $timeTests[0]['czas'] = microtime(true);
  45. $timeTests[0]['znak'] = 'start';
  46.  
  47. //test funkcji z parametrem wartosciowym
  48. for ($ptl=0; $ptl<4000; $ptl++)
  49. {
  50. Tab($tab);
  51. }
  52.  
  53. $timeTests[4]['czas'] = microtime(true);
  54. $timeTests[4]['znak'] = 'Tab';
  55.  
  56. //test funkcji z przekazaniem jej parametru: obiekt
  57. for ($ptl=0; $ptl<4000; $ptl++)
  58. {
  59. ObjFunc($obj);
  60. }
  61.  
  62. $timeTests[1]['czas'] = microtime(true);
  63. $timeTests[1]['znak'] = 'ObjFunc';
  64.  
  65. //test metody obiektu
  66. for ($ptl=0; $ptl<4000; $ptl++)
  67. {
  68. $obj->ObjMet();
  69. }
  70.  
  71. $timeTests[2]['czas'] = microtime(true);
  72. $timeTests[2]['znak'] = 'ObjMet';
  73.  
  74. //test funkcji z parametrem referencyjnym
  75. for ($ptl=0; $ptl<4000; $ptl++)
  76. {
  77. TabRef($tab);
  78. }
  79.  
  80. $timeTests[3]['czas'] = microtime(true);
  81. $timeTests[3]['znak'] = 'TabRef';
  82.  
  83.  
  84. echo '<br />';
  85. $timePrev = 0;
  86. foreach ($timeTests as $time)
  87. {
  88. echo $time['znak'].': ';
  89. echo ($time['czas'] - $timePrev).'<br />';
  90. $timePrev = $time['czas'];
  91. }
  92. ?>


Średnie wyniki z 10 pomiarów:
ObjFunc: 0,04068577289581310
ObjMet: 0,03742690086364740
Tab: 0,01934800148010270
TabRef: 0,03726780414581290

Opis wyników
Tab jest 2x szybsze od TabRef

Wnioski
W porównaniu do przykładu 1, widzimy dużo lepsze wyniki funkcji Tab. Dlatego łatwo można wyciągnąć wniosek, że funkcja z parametrem wartościowym jest jednak szybsza od fukcji z parametrem referencyjnym. Przekazanie tablicy przez referencję jest porównywalnie szybkie jak przekazanie obiektu lub wywołanie metody obiektu.

I ostatni test
Tym razem zmiejszyłem rozmiar zmiennej do 10 pól integer (z 10000 w pierwszym teście) i wykonałem funkcje modyfikujące przekazaną zmienną, tym razem równierz funckaj Tab, która nieznacznie zmieniłem, aby zwracała zmienną o nowej wartości.

Przykład 4
  1. <?php
  2. class C1
  3. {
  4. public $data=array();
  5.  
  6. public function UstawZTablicy ($tab)
  7. {
  8. foreach ($tab as $key => $value)
  9. {
  10. $this->data[$key] = $value;
  11. }
  12. }
  13. public function ObjMet()
  14. {
  15. $this->data['pole8'] = 34534;
  16. }
  17. }
  18.  
  19. function Tab ($tab)
  20. {
  21. $tab['pole8'] = 34534;
  22. return $tab;
  23. }
  24.  
  25. function TabRef (&$tab)
  26. {
  27. $tab['pole8'] = 34534;
  28. }
  29.  
  30. function ObjFunc ($obj)
  31. {
  32. $obj->pole8 = 34534;
  33. }
  34.  
  35. $tab = array();
  36. for ($ptl=1; $ptl<10; $ptl++)
  37. {
  38. $tab['pole'.$ptl] = $ptl;
  39. }
  40. $obj = new C1();
  41. $obj->UstawZTablicy($tab);
  42.  
  43. $timeTests = array();
  44.  
  45. $timeTests[0]['czas'] = microtime(true);
  46. $timeTests[0]['znak'] = 'start';
  47.  
  48. //test funkcji z parametrem wartosciowym
  49. for ($ptl=0; $ptl<4000; $ptl++)
  50. {
  51. $tab = Tab($tab);
  52. }
  53.  
  54. $timeTests[4]['czas'] = microtime(true);
  55. $timeTests[4]['znak'] = 'Tab';
  56.  
  57. //test funkcji z przekazaniem jej parametru: obiekt
  58. for ($ptl=0; $ptl<4000; $ptl++)
  59. {
  60. ObjFunc($obj);
  61. }
  62.  
  63. $timeTests[1]['czas'] = microtime(true);
  64. $timeTests[1]['znak'] = 'ObjFunc';
  65.  
  66. //test metody obiektu
  67. for ($ptl=0; $ptl<4000; $ptl++)
  68. {
  69. $obj->ObjMet();
  70. }
  71.  
  72. $timeTests[2]['czas'] = microtime(true);
  73. $timeTests[2]['znak'] = 'ObjMet';
  74.  
  75. //test funkcji z parametrem referencyjnym
  76. for ($ptl=0; $ptl<4000; $ptl++)
  77. {
  78. TabRef($tab);
  79. }
  80.  
  81. $timeTests[3]['czas'] = microtime(true);
  82. $timeTests[3]['znak'] = 'TabRef';
  83.  
  84.  
  85. echo '<br />';
  86. $timePrev = 0;
  87. foreach ($timeTests as $time)
  88. {
  89. echo $time['znak'].': ';
  90. echo ($time['czas'] - $timePrev).'<br />';
  91. $timePrev = $time['czas'];
  92. }
  93. ?>


Średnie wyniki z 10 pomiarów:
ObjFunc: 0,01284565925598150
ObjMet: 0,01222741603851320
Tab: 0,03661031723022470
TabRef: 0,01396517753601080

Opis wyników
Tab jest (tylko) 2.6x wolniejsza od TabRef.

Wnioski
1. Jeżeli w wyniku działania funkcji zmienna ma ulec zmianie, to przekazanie do funkcji nawet stosunkowo małych zmiennych przez referencję jest korzystniejsze niż przekaznie ich przez wartość.
2. Dlatego może się opłacać zamienieie tablicy na obiekt, albo wogle korzystanie z samych obiektów.
devnul
wydaje mi się że w testach powinieneś uwzględnić czas tworzenia zmiennych/tablic/obiektów - bo co z tego że operacja jest szybsza skoro operacja plus utworzenie obiektu/tablicy jest już znacznie wolniejsza.
gWd
Cytat(devnul @ 18.04.2007, 13:40:18 ) *
wydaje mi się że w testach powinieneś uwzględnić czas tworzenia zmiennych/tablic/obiektów - bo co z tego że operacja jest szybsza skoro operacja plus utworzenie obiektu/tablicy jest już znacznie wolniejsza.

W testach chodziło o porównanie parametrów wartościowych i referencyjnych. Obiekty dodałem tylko dla porównania, czy ich stosowanie różni się od stosowania innych typów referencyjnych. Nie miałem zamiaru udowadniać wyższości programowania obiektowego czy strukturalnego.

Ale zawsze możesz na podstawie załączonego kodu wykonać własne testy i przedstawić wyniki.
devnul
Cytat(gWd @ 18.04.2007, 13:53:40 ) *
W testach chodziło o porównanie parametrów wartościowych i referencyjnych. Obiekty dodałem tylko dla porównania, czy ich stosowanie różni się od stosowania innych typów referencyjnych. Nie miałem zamiaru udowadniać wyższości programowania obiektowego czy strukturalnego.

Ale zawsze możesz na podstawie załączonego kodu wykonać własne testy i przedstawić wyniki.

nie zrozum mnie źle - nie chcę udowadniać wyższości jednego nad drugim. Twierdze tylko że nieuwzględnienie w testach czasu potrzebnego na zbudowanie danej zmiennej (czy to tablicy czy obiektu) nie jest miarodajne. Ogólni test bardzo fajny - wyniki faktycznie nieco zasakujące.
gWd
Test nie związany z tematem, ale mam nadzieję zaspokajający ciekawość devnula...

Tworzymy 1000 tablic, każda po 10 pól i 1000 obiektów każdy po 10 pól:

  1. <?php
  2. class C2
  3. {
  4. public $data = array();
  5. }
  6.  
  7. $timeTests = array();
  8.  
  9. $timeTests[0]['czas'] = microtime(true);
  10. $timeTests[0]['znak'] = 'start';
  11.  
  12. for ($ptl=0; $ptl<1000; $ptl++)
  13. {
  14. ${'tab'.$ptl} = array();
  15. for ($pole=0; $pole<10; $pole++)
  16. {
  17. ${'tab'.$ptl}[$pole] = $pole;
  18. }
  19. }
  20.  
  21. $timeTests[1]['czas'] = microtime(true);
  22. $timeTests[1]['znak'] = 'Tabele';
  23.  
  24. for ($ptl=0; $ptl<1000; $ptl++)
  25. {
  26. ${'obj'.$ptl} = new C2();
  27. for ($pole=0; $pole<10; $pole++)
  28. {
  29. ${'obj'.$ptl}->data[$pole] = $pole;
  30. }
  31. }
  32.  
  33. $timeTests[2]['czas'] = microtime(true);
  34. $timeTests[2]['znak'] = 'Obiekty';
  35.  
  36.  
  37. echo '<br />';
  38. $timePrev = 0;
  39. foreach ($timeTests as $time)
  40. {
  41. echo $time['znak'].': ';
  42. echo ($time['czas'] - $timePrev).'<br />';
  43. $timePrev = $time['czas'];
  44. }
  45. ?>


Wyniki
Tabele: 0.026139974594116
Obiekty: 0.031857967376709

Wnioski
Tabele zgodnie z oczekiwaniami są szybsze od obiektów, ale nie jest to różnica powalająca, zaledwie 22%.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.