Jump to content
php.lv forumi

Foreach optimizācija


DarkSide
 Share

Recommended Posts

Sveiki!

 

Vai ir kādas idejas kādēļ lieli foreach cikli norij nenormāli daudz servera (Apache procesa) RAM atmiņas.

Lietoju, Apache 1.3.x, PHP 4.x, Win2K.

 

Skripts ir piemēram šāds:

foreach($arrData['DEPARTMENTS'] as $did=>$dvalue) {
foreach($dvalue['CONTRACTS'] as $cid=>$cvalue) {
	foreach($cvalue['ORDERS'] as $oid=>$ovalue) {
		echo "$did, $cid, $oid";
	}
}
}

Ideja tāda, ka man ir viens liels strukturēts masīvs $arrData, ko man ir jāparsē cauri un kautkas ar tiem datiem jāizdara (piemēram, jāizdrukā uz ekrāna). Ejot cauri šiem foreachiem Apache aprij ~200Mb RAM!! :-))) Ko tā? Reāli taču RAMu apēst nevajadzēja praktiski nemaz, jo katrā ciklu solī es papildus jau esošajam $arrData masīvam izveidoju principā tikai vienu mainīgo... lai gan piemēram $dvalue sanāk riktīgi liels... hmm... ok - padomāšu pats vēl par šo tēmu, bet

 

anyway varbūt ir kādi noderīgi komentāri par foreach optimizāciju (uz PHP4).

Link to comment
Share on other sites

  • Replies 39
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Pēc ātrdarbības foreach bija ātrākais paņēmiens, lai izskrietu caur masīvu. Man gan liekās, ka vajadzētu filtrēt datus jau db līmenī. 200Mb ir toč par traku.

 

Mēģini šādi:

 

$a1 = array_keys($arrData['DEPARTMENTS']);
foreach($a1 as $did) {
$a2 = array_keys($arrData['DEPARTMENTS'][$did]['CONTRACTS']);
foreach($a2 as $cid) {
	$a3 = array_keys($arrData['DEPARTMENTS'][$did]['CONTRACTS'][$cid]['ORDERS']);
	foreach($a3 as $oid) {
		echo $did . ', ' . $cid . ', ' . $oid;
	}
}
}
unset($a1, $a2, $a3);

 

Ja tie tev ir objekti, tad var izmantot sintaksi foreach ($obj as $k => &$v).

Link to comment
Share on other sites

Daži parametri:

strlen(serialize($arrData)) = ~10'000'000 tas ir apmēram 10Mb.

 

Grozies kā gribi, pat ja foreach patiešām būtu pilnīgi garām un taisītu 10 kopijas no pilnīgi visa $arrData tad tik un tā nesanāktu ~200Mb.

 

Saskaitīju kopā ar šo pašu funkciju strlen(serialize($foo)) pilnīgi visus mainīgos kas izmantoti šais foreachos un tik un tā sanāca kopā apmēram 25'000'000 jeb 25Mb...

 

P.S. Tur nav objektu - tas ir pliks masīvs tikai ar samērā dziļu struktūru. un foreachi īstenībā ir 5 gab. nevis 3 kā parādīju piemērā.

 

Cik tev daudz elementu aptuveni ir katrā masīvā dimensijā?

Departments kādi 10-20,

Contracts kādi 1000-5000,

Orders arī +/- tikpat cik Contracts

Link to comment
Share on other sites

kaut kur dzirdēju, ka foreach taisa masīva kopiju un tāpēc vietās kur ar for vai while var iztikt labāk izmantot tos.

bet 200MB vienalga tur nesanāks, kauč 3kopijas taisa.

pieņemu ka struktūra nav uz 100 megabaitiem.

Pat ja būtu visprastākā kopija - nu labi - tad 2x paņemtu RAM, bet struktūra ir uz apmēram 10Mb nevis 100Mb.

 

Arī ja tie nav objekti, tad der pamēģināt references.
Laikam man rokas līkas, bet foreach($array as $key => &$value) man nešansē. Piesienas pie &. Kurā brīdī Tu domāji tās references izmantot?
Link to comment
Share on other sites

Izmēģināji manis ieteikto variantu ar array_keys?

 

Cik zinu, ar masīviem nestrādā paņēmiens foreach ($array as $key => &$value). Vai varbūt jaunākajās versijās strādā?

Šitais variants nekādu lielo ieguvumu nedod (varbūt pāris Mb) - skat.zemāk ko atradu...

 

Hm.. nestrādā? Varbūt arī nē, jāpamēģina ;)

 

Vai ar each() fju arī mēģināji apstaigāt masīvus?

each() līdzīgi kā ar array_keys - nekāds lielais ieguvums (varbūt pāris nepamanāmi Mb). Skat.zemāk...

 

Lūk ko atradu - tikai to nu es vēl vairāk nesaprotu :-)

 

Lūk nedaudz papildināts mans kods(skatīt to $first_row):

$row_id = 0;
foreach($arrData['DEPARTMENTS'] as $did=>$dvalue) {
foreach($dvalue['CONTRACTS'] as $cid=>$cvalue) {
	$first_row = true;
	foreach($cvalue['ORDERS'] as $oid=>$ovalue) {
		if($first_row) $first_row=false; else $row_id++;
		echo "$row_id, $did, $cid, $oid";
	}
}
}

Tas gan arī nav pilnīgi viss kods, bet pati sāls tur ir.

Tad nu lūk, līdzko es izkomentēju ārā to rindiņu if($first_row) $first_row=false; else $row_id++;, tā Apache vairs apēd nevis 200Mb RAM bet gan tikai 100Mb. Vot tas jau man pavisam nepielec. Sanāk, ka problēma bija ne tik daudz tajos foreachos, bet gan ar kautkādu IFu??? Sviests. Kādas idejas?

Link to comment
Share on other sites

Lielos ciklos vajaga censties maksimāli vienkāršot kodu. Labāk sadali pēdējo ciklu divās daļās.
Hmm.... sadalīt to ciklu es nevaru, jo tas echo tur ir tikai kā piemērs, īstenībā ir bišķi sarežģītāk... Visu kodu te nelikšu - sanāks pārāk gari...

BET viena ideja man radās, anyway šitie foreachi apēd kādus 100Mb, bet otri 100Mb rodas pēc šiem foreachiem, kad izmantojot to $row_id (tāpēc itkā bremzēja ifs, jo row_id palika stipri lielāks nekā bez tā ifa) es pēc tam ģenerēju izdrukas tabulu. Savukārt, šī izdrukas tabula ir objekts, kas acīmredzot uz PHP4 kautkā gļukaini norij RAMu.

Statistika - izdrukas tabulas objekts strlen(serialize(objekc)) = ~9Mb, bet tad es to tabulu "normalizēju", respektīvi aizpildu tukšās celles, sakārtoju colspanus utml. un pēc tās "normalizēšanas" izdrukas tabulas objekts jau aizņem strlen(serialize(objekc)) = ~17Mb... Ja pieņemam, ka PHP4 kautko vēl ar objektiem nogļuči, tad varbūt var arī sanākt tie apmēram 100Mb... :(

Vienvārdsakot, paēdīšu "pusdienas" un izpētīšu līdz galam. Tad jau došu ziņu ko jaunu būšu noskaidrojis :)

Paldies Jums!

Edited by DarkSide
Link to comment
Share on other sites

Nū... ka jau teica.. jāiet cauri ar array_keys... Un nevis vienam līmenim, bet visiem trīs..

 

for ( i=0; i<keysCnt; i++ )
{
 keysJ = array_keys( $arr[keys[i]] );
 for ( j=0; j<keysJCnt; j++ )
 {
	  ...
 }
}

 

PS: kaut gan laiku patērē vairāk...

Edited by Delfins
Link to comment
Share on other sites

Koroč, viss tev tur galīgi ***** vecīt, domā ar galvu - masīvu tīklotā cilpošana vispār paņem daudz resursus! Meklē, mēģini, dalies. Vispirms - protams, sameklē google.lv pēc "array loops php" - atrodi kādus variantus, izmēģini iespējamos variantus. Tālāk - nu ja nesanāk, ta nav lemts! :D

Link to comment
Share on other sites

 Share


×
×
  • Create New...