Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [PHP] Jak zoptymalizować tą pętlę i jej rekurencję
jajcarzd1
post
Post #1





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Witam

Mam w bazie danych zapisane kategorie gdzie tabela ta posiada między innymi takie nagłówki jak idCategories,idParent,name itp.

Potrzebuję wyciągnąć wszystkie kategorie wraz z ich potomkami niezależnie od stopnia zagnieżdzenia aby potem móc zrobić sobie menu np. takie http://jquery.bassistance.de/treeview/demo/?1 czyli wszystkie elementy pociągnięte od razu a nie w trakcie kliknięcia na danego potomka. Wiec konstrukcje <ul> z zagłębieniami

W tabeli kategorii jest około 1400 kategorii na chwilę obecną około 14 pierwszego poziomu czyli z idparent = 0, 165 drugiego i jakieś 1300 trzeciego poziomu. Elementy pobieram rekurencyjnie ale to nie jest za bardzo problemem a ilość pętli jaka musi być wykonana i jest ich w sumie w chwili obecnej około 240 000, czas wykonywania tej operacji to około 0,4 sekundy i tak nie mam za bardzo pomysłu co tu można jeszcze poprawić



  1. /* tu pobieram wszystkie elementy z idparent = 0 i tworzę tablicę obiektów
  2.   bo normalnie to mi zwraca tablicę dwuwymiarową. Na każdym poziomie tworzę obiekty
  3.   gdyż nie bardzo widziałem rozwiązanie aby kombinować z określaniem kolejnego poziomu zagłębienia tablicy
  4.   a tak przekazuję referencję obiektu */
  5.  
  6. $result = $this->classMysql->ta($sql);
  7. $r = array();
  8.  
  9. foreach ($result as $item) {
  10. $object = new stdClass();
  11. $object->idCategories = $item['idCategories'];
  12. $object->item = $item['name'];
  13. $object->nodes = array();
  14. $r[] = $object;
  15. }
  16.  
  17. $this->dbGetNodes($r);
  18.  
  19.  
  20. private function dbGetNodes(&$result) {
  21.  
  22. $this->allCategories = $this->classMysql->ta("SELECT idCategories,idParent,name FROM categories ORDER BY sequence");
  23. $allIdParents = array();
  24. $this->allIdParentsAsKeys = array();
  25.  
  26. foreach ($this->allCategories as $cat) {
  27. $allIdParents[] = $cat['idParent'];
  28. }
  29.  
  30. $allIdParents = array_unique($allIdParents);
  31.  
  32. foreach ($allIdParents as $item) {
  33. $this->allIdParentsAsKeys[$item] = 1;
  34. }
  35.  
  36. for($i = 0, $ii = count($result); $i<$ii; $i++) {
  37.  
  38. $this->createNodes($result[$i]->idCategories,$result[$i]);
  39.  
  40. }
  41.  
  42.  
  43. }
  44.  
  45.  
  46. private function createNodes($idCategories,$object) {
  47.  
  48. $this->iteration++;
  49.  
  50.  
  51. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {
  52.  
  53. $this->iteration2++;
  54.  
  55. if($this->allCategories[$i]['idParent'] == $idCategories) {
  56.  
  57. $ob = new stdClass();
  58. $ob->idCategories = $this->allCategories[$i]['idCategories'];
  59. $ob->item = $this->allCategories[$i]['name'];
  60. $ob->nodes = array();
  61.  
  62. $object->nodes[] = $ob;
  63.  
  64. /*
  65. if(in_array($allCategories[$i]['idCategories'],$allIdParents)) {
  66. $this->createNodes($allCategories[$i]['idCategories'],$ob,$allCategories,$allIdParents);
  67. }
  68. */
  69.  
  70. /* szybsze niż in_array */
  71.  
  72. if(isset($this->allIdParentsAsKeys[$this->allCategories[$i]['idCategories']])) {
  73. $this->createNodes($this->allCategories[$i]['idCategories'],$ob);
  74. }
  75.  
  76.  
  77. }
  78.  
  79.  
  80. }
  81.  
  82. }


Jakby ktoś miał jakis pomysł jak to zoptymalizować to byłbym wdzięczny.
Pozdrawiam
Go to the top of the page
+Quote Post
wookieb
post
Post #2





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




Zastosować inny rodzaj drzewa w bazie danych. Left right, bądź IP
Go to the top of the page
+Quote Post
jajcarzd1
post
Post #3





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Cytat(wookieb @ 19.05.2010, 15:43:33 ) *
Zastosować inny rodzaj drzewa w bazie danych. Left right, bądź IP


Zmiany w bazie odpadają dlatego pytam o optymalizację tego co jest.
Go to the top of the page
+Quote Post
wookieb
post
Post #4





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




Jak już to:
1) Cache - dla każdego rodzica listę ich dzieci
2) Dlaczego pobierasz WSZYSTKIE elementy a dopiero potem je "wyszukujesz" w duzej tablicy?
Załóż klucz "index" na pole "idParent" w swojej bazie i wyszukuj tylko te rekordy, które potrzebujesz
Go to the top of the page
+Quote Post
jajcarzd1
post
Post #5





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Cytat(wookieb @ 19.05.2010, 15:57:35 ) *
2) Dlaczego pobierasz WSZYSTKIE elementy a dopiero potem je "wyszukujesz" w duzej tablicy?
Załóż klucz "index" na pole "idParent" w swojej bazie i wyszukuj tylko te rekordy, które potrzebujesz


Po to pobieram wszystkie i przelatuję w pętli aby ograniczać ilośc zapytań do bazy (których przy założeniu że kategorie trzeciego poziomu już nie mają potomków) będzie 165. Ta sama wersja ale oparta na pobieranie kategorii z bazy za kazdym razem wykonuję mi sie prawie 1,5 sekundy więc to tez odpada.
Go to the top of the page
+Quote Post
wookieb
post
Post #6





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




A stworzyłeś klucze tak jak mówiłem?
Pozostaje Ci cache jak nic innego nie chcesz robić.
Go to the top of the page
+Quote Post
taktu
post
Post #7





Grupa: Zarejestrowani
Postów: 89
Pomógł: 7
Dołączył: 19.05.2008

Ostrzeżenie: (0%)
-----


Można trochę udoskonalić kod, wywalić foreach czy wyciągnąć count przed pętle - to tak na pierwszy rzut oka. Może tutaj znajdziesz coś co pomoże.
Go to the top of the page
+Quote Post
jajcarzd1
post
Post #8





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Cytat(wookieb @ 20.05.2010, 09:48:10 ) *
A stworzyłeś klucze tak jak mówiłem?


No pewnie ale tak jak napisałem za dużo zapytań by było.


Cytat(taktu @ 20.05.2010, 11:46:46 ) *
Można trochę udoskonalić kod, wywalić foreach czy wyciągnąć count przed pętle - to tak na pierwszy rzut oka. Może tutaj znajdziesz coś co pomoże.


No a co mi da wywalenie pętli jak ja jej potrzebuję. Count jest odpalany raz na początku pętli a nie za każdą iteracją. Natomiast zastosowanie tylko jednego count-a i przypisanie go do właściwości klasy aby uniknąć ponownego odpalania w rekurencji nie poprawiło efektywności. Zerknę jeszcze na link który zapodałeś
Go to the top of the page
+Quote Post
wookieb
post
Post #9





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




No to nie ma opcji. Przebudowa drzewa albo siedzisz dodatkowych pare godzin nad cache.
Go to the top of the page
+Quote Post
croc
post
Post #10





Grupa: Zarejestrowani
Postów: 706
Pomógł: 108
Dołączył: 12.03.2010

Ostrzeżenie: (0%)
-----


Cytat(jajcarzd1 @ 20.05.2010, 12:02:06 ) *
Count jest odpalany raz na początku pętli a nie za każdą iteracją.

Niestety mylisz się. Jak masz count( ) w środkowej części for to odpala się co iterację i tak musi być, bo przecież mógłbyś sobie dodawać/usuwać w pętli elementy tablicy i wtedy cache'owanie tej wartości byłoby bez sensu.
Go to the top of the page
+Quote Post
jajcarzd1
post
Post #11





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Cytat(croc @ 20.05.2010, 12:38:17 ) *
Niestety mylisz się. Jak masz count( ) w środkowej części for to odpala się co iterację i tak musi być, bo przecież mógłbyś sobie dodawać/usuwać w pętli elementy tablicy i wtedy cache'owanie tej wartości byłoby bez sensu.


Czegoś chyba nie kumam mówisz o tej części ?

  1.  
  2. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {
  3.  


Przecież tu mam odpalanie counta raz dla danej rekurencji. Przecież gdyby było co iterację to by było

  1.  
  2. for($i = 0, $ii < count($this->allCategories); $i++) {
  3.  
Go to the top of the page
+Quote Post
marcio
post
Post #12





Grupa: Zarejestrowani
Postów: 2 291
Pomógł: 156
Dołączył: 23.09.2007
Skąd: ITALY-MILAN

Ostrzeżenie: (10%)
X----


@cron ma racje wywal tego count() nad petle, przypisz to co zwraca do jakiejs zmiennej i to ja przekazuj do petli.
Go to the top of the page
+Quote Post
jajcarzd1
post
Post #13





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Cytat(marcio @ 20.05.2010, 13:06:26 ) *
@cron ma racje wywal tego count() nad petle, przypisz to co zwraca do jakiejs zmiennej i to ja przekazuj do petli.


Napisałem wcześniej że nie widziałem praktycznie żadnej zmiany wydajności na plus gdy dałem wynik count-a do właściwości klasy

  1. for($i = 0, $i < $this->quantity; $i++)
Go to the top of the page
+Quote Post
phpion
post
Post #14





Grupa: Moderatorzy
Postów: 6 072
Pomógł: 861
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




@jajcarzd1:
Nie słuchaj ~crona ani ~marcio odnośnie tej pętli. Pierwotnie, czyli tak:
  1. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {

miałeś dobrze.
Go to the top of the page
+Quote Post
jajcarzd1
post
Post #15





Grupa: Zarejestrowani
Postów: 215
Pomógł: 19
Dołączył: 24.12.2003
Skąd: Przemyśl

Ostrzeżenie: (0%)
-----


Cytat(phpion @ 20.05.2010, 13:21:31 ) *
@jajcarzd1:
Nie słuchaj ~crona ani ~marcio odnośnie tej pętli. Pierwotnie, czyli tak:
  1. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {

miałeś dobrze.



No do tego rozwiązania jestem przekonany że miałem dobrze, ale oczywiście w każdej rekurencji pętla leci od nowa więc tam jest ponownie odpalany count(), ale tak jak napisałem przerzucenie jego wyniku do właściwości obiektu praktycznie nic nie dało bo to zbyt mały zysk.
Go to the top of the page
+Quote Post
taktu
post
Post #16





Grupa: Zarejestrowani
Postów: 89
Pomógł: 7
Dołączył: 19.05.2008

Ostrzeżenie: (0%)
-----


z wywaleniem foreach miałem na myśli zamianę na for/while ale wiele to nie da bo tam akurat masz mało iteracji.
Go to the top of the page
+Quote Post
croc
post
Post #17





Grupa: Zarejestrowani
Postów: 706
Pomógł: 108
Dołączył: 12.03.2010

Ostrzeżenie: (0%)
-----


Aha, zawarłeś to jednak w pierwszej części for, no to ok - liczy raz. Nie wczytałem się dokładnie w kod, sorry (IMG:style_emoticons/default/smile.gif) Ale uważam, że takie upychanie na siłę kodu nie jest zbyt dobrym pomysłem. Wprawdzie masz argument, że tego counta liczysz tylko na chwilę, dla pętli, ale chyba lepiej jak widać od razu poszczególne części for.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 17.09.2025 - 15:06