Python týden Geek tričko zdarma
Tričko zdarma! Stačí před dobitím bodů použít kód TRIKO15. Více informací zde
Pouze tento sleva až 80% na kurzy Python

Diskuze: Optimalny (efektivnejsi) kod... PHP

Aktivity (2)
Avatar
joon5
Člen
Avatar
joon5:14. května 14:00

Snad mi odpustite amatersky dotaz. Zaujimalo by ma, ktora varianta kodu je efektivnejsia z hladiska rychlosti skriptu a pamatovych narokov.

Varianta 1:

$poleData[][]; // 40 tisic riadkov, 35 stlpcov
// nejaky kod na naplnenie pola datami zo suboru

$pocet = count($poleData);
for ($i = 0; $i < $pocet; $i++)
{

$pr1 = $poleData[$i][$key1];
$pr2 = $poleData[$i][$key2];
$pr3 = $poleData[$i][$key3];
..
..
..
$pr10 = $poleData[$i][$key10];
$Vysledok = vypocitaj($pr1, $pr2, $pr3, $pr4, $pr5, $pr6, $pr7, $pr8, $pr9, $pr10);
}

Varianta 2:

$poleData[][]; // 40 tisic riadkov, 35 stlpcov
// nejaky kod na naplnenie pola datami zo suboru

$pocet = count($poleData);
for ($i = 0; $i < $pocet; $i++)
{
$Vysledok = vypocitaj($poleData, $i);
}

Varianta 3:

$poleData[][]; // 40 tisic riadkov, 35 stlpcov
// nejaky kod na naplnenie pola datami zo suboru

$pocet = count($poleData);
for ($i = 0; $i < $pocet; $i++)
{
$Vysledok = vypocitaj($i);
}

function vypocitaj($i)
{
global $poleData;

$pr1 = $poleData[$i][$key1];
$pr2 = $poleData[$i][$key2];
$pr3 = $poleData[$i][$key3];
..
..
..
$pr10 = $poleData[$i][$key10];
// dalsie spracovanie dat
}

Vytvaranie globalnych premennych (v poslednom priklade $poleData) sa vo vseobecnosti nedoporuceje. Predavanie celeho pola ako parameter vo funkcii - neviem ci nieje narocne na pamat.

Chci docílit: Chcem dosiahnut (napisat) optimalny kod :-)

 
Odpovědět 14. května 14:00
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:14. května 14:22
  • Tezko rici, nezname ucel.
  • Neni mi jasne, proc si ukladas pointery zvlast do promenych $pr1 = $poleData[$i][$ke­y1];.
  • slo by misto toho pouzit list ($a, $b, $c) = $poleData[$i]
  • misto global bych pouzil and. function vypocitaj(&$my­array, $i)
  • ty potrebujes k necemu cele to pole uvnitr? Pokud ne, predal bych mu jen tu cast, kterou potrebujes vypocitaj( $poleData[$i])
Editováno 14. května 14:23
 
Nahoru Odpovědět  +1 14. května 14:22
Avatar
joon5
Člen
Avatar
joon5:14. května 18:02

$poleData obsahuje jednotlive riadky z CSV suboru, potrebujem z tych cca 35 stlpcov vybrat iba niektore podla danych kriterii, povedzme tak 10-15 stlpcov. Skript prechadza jednotlive riadky pola $poleData a podla kriterii vybera jednotlive polozky ako napr. $meno = $poleData[$ri­adok]['meno'] atd. . Dalej z nich vytvara nieco ako osobnu kartu podla sablony (v kode vyssie to je ta funkcia vypocitaj, ktorej sa predavaju jednotlive premenne )

 
Nahoru Odpovědět 14. května 18:02
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na joon5
patrik.valkovic:14. května 18:50

Hlavní věc, na kterou si musíš dát pozor je, že PHP předává pole hodnotou (tj. celé pole se překopíruje). https://stackoverflow.com/…by-reference
Tj. druhá varianta je nejhorší
Jinak zbývající varianty jsou cca ekvivalentní (nebo alespoň zanedbatelně rozdílné). Obdobně by to bylo u druhé varianty, kdyby jsi použil předání referencí.

Nahoru Odpovědět 14. května 18:50
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Tomáš Novotný:14. května 18:58

Podobnou otázku jsem řešil u tvorby výkazu práce a docházky jako podkladu pro fakturaci a výplaty. Měla několik vrstev informací a pro různé lidi platily mezi vrstvami různé podmínky výpočtu. Pak do toho vlezl náhled do historie kvůli dovolené, svátkům tj. souvstažnostem ročního charakteru. Podmínek bylo již tolik, že jsem vstupní data nakonec rozsekal na minutové úseky, kde mi šlo situaci lépe vyřešit. Např. někomu se třeba započítává doba před smluveným modelem pracovní doby jinému ne, ale pokud tam má třeba vrstvu domluvené zakázky tak zase ano. Nebo existují jiné sazby pro víkendy, nebo držení pohotovosti kdy evidovaná v pracovní době není bonusově proplácena, ale mimo ano.. atd. atd... Tím samo vzniklo obrovské množství dat a zdroje serveru, virtuální mašina, která plnila hromady dalších úkolů díky nekonečným cron úlohám, nebyly zrovna neomezené.
Přesto na problém s pamětí jsem nenarazil. Je fakt, že jsem to přechroupal postupně.
Tj. ze surových data vybral a poskládal do struktury, která se bude dobře procházet. Myšlenka je taková, že se pracuje už jen s relevantními daty v dané úloze, výběr ze surových dat se provede jen jednou.

Ale konkrétně. Na zpracování textových vstupů tj. well-formed CSV (nevím jak řešíš validitu vstupu) se mi osvědčily regulární výrazy než nějaké for cykly. Takže csv bych projel regulárem a vytáhl informace, které potřebuji. Převést na objekt nebo assoc pole, ale dle mne máš pořešeno.

Nahoru Odpovědět 14. května 18:58
∞ ... the exact amount of possibilities how to deal with the situation ... so by calm, your solution is one of many
Avatar
joon5
Člen
Avatar
Odpovídá na patrik.valkovic
joon5:14. května 18:59

Aktualne pouzivam tretiu variantu (globalne pole) , ta prva varianta mi pride chaoticka, v skutocnosti predavam tej funkcii az 15 roznych premennych, pride mi to neprehladne a skor nachylne na chybu. Druhu variantu som vylucil, tusil som ze predavat cele (velke) pole bude narocne na pamat a asi aj na rychlost ale bol som zvedavi na nazor. Este ma napadlo, z tych 15 premennych vytvorit pole a predat iba to jedno pole?

 
Nahoru Odpovědět 14. května 18:59
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na joon5
patrik.valkovic:14. května 19:18

Třetí varianta není taktéž nic moc, protože se spoléháš na nějaký globální objekt. Nejlepší mi příjde předat pole referencí (pokud ho máš již v paměti).
Úplně nejlepší by bylo nečíst celé CSV ale jen části, které potřebuješ.

Nahoru Odpovědět 14. května 19:18
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
joon5
Člen
Avatar
Odpovídá na Tomáš Novotný
joon5:14. května 19:18

Diky. Mne to funguje - zatial iba na localhoste, ked to nahram na server neviem ci nebude problem s memory_limit u PHP. Moja uloha nieje zlozita, nieco ako dochadzkovy system - v CSV subore moze byt az 40 tisic riadkov z toho asi 70-80% sa opakuje (rovnake meno+priezvisko), kde sa meni udaj iba v par stlpcoch ako napr. 'prichod', 'odchod', 'odpracovane hodiny', a pod. Skript z toho povodneho pola $poleData (v podstate RAW data z CSV suboru) vytahuje jednotlive unikatne zaznamy a uklada do noveho pola kde su uz spracovane aj ostatne vysledky podla roznych kriterii a nakoniec sa tie data (to nove pole) zobrazi v tabulke v html...

 
Nahoru Odpovědět 14. května 19:18
Avatar
Tomáš Novotný:14. května 19:37

Jasně, takže pokud chápu správně jde skoro vždy až o 80% řádků a pak cca 30% sloupců, které reálně potřebuješ? 1/3 sloupců může někde znamenat úsporu až 2/3 nároků...
No musel bys to napsat více konkrétně... vstupní data vypadají nějak tak a má z toho vypadnout tento formulář, kdy podmínky výběru jsou tyto a takto vypadá typický dotaz, který je potřeba vyřešit. V teoretické rovině, bych řekl, že je tu nápadů dost :-).

Nahoru Odpovědět 14. května 19:37
∞ ... the exact amount of possibilities how to deal with the situation ... so by calm, your solution is one of many
Avatar
joon5
Člen
Avatar
Odpovídá na patrik.valkovic
joon5:14. května 19:37

jj, preto som sa pytal. Globalne premenne sa vo vseobecnosti nedoporucuje robit. ... CSV citat po castiach - neviem si to predstavit, kedze z neho musim vytiahnut unikatne zaznamy - prechadzam riadok po riadku a po spracovani riadku (...nejake vypocty apod.) sa ulozi do noveho pola ktore sa vo finale zobrazi v tabulke.... v PHP som toho moc nenapisal, musim este dostudovat :-)

 
Nahoru Odpovědět 14. května 19:37
Avatar
joon5
Člen
Avatar
Odpovídá na Tomáš Novotný
joon5:14. května 20:22

... uloha je jednoducha ale rieseni moze byt viacej :-)

Zjednoduseny priklad - format CSV suboru:

Meno; Priezvisko; Adresa; Datum narodenia; Nastup; Odpracovane hodiny; Prichod; Odchod; Odjazdene hodiny; SPZ; Trzba
Jan; Novak; Adresa 1; 01.01.1990; 05.05.2015; 7; 08:00; 15:00; 5; CGHU5; 174
Jan; Novak; Adresa 1; 01.01.1990; 05.05.2015; 4; 08:00; 12:00; 1; CGHU5; 151
Jan; Novak; Adresa 1; 01.01.1990; 05.05.2015; 3; 08:00; 15:00; 2; CGHU5; 114
Ivo; Klaun; Adresa 5; 01.05.1993; 01.01.2014; 6; 08:00; 15:00; 5; AFU1; 188
Jan; Novak; Adresa 1; 01.01.1990; 05.05.2015; 8; 08:00; 16:00; 2; CGHU5; 119
Ivo; Klaun; Adresa 5; 01.05.1993; 01.01.2014; 5; 08:00; 13:00; 1; AFU1; 120
Jan; Novak; Adresa 1; 01.01.1990; 05.05.2015; 6; 08:00; 14:00; 5; CGHU5; 174
Ivo; Klaun; Adresa 5; 01.05.1993; 01.01.2014; 7; 08:00; 15:00; 6; AFU1; 171
Ivo; Klaun; Adresa 5; 01.05.1993; 01.01.2014; 6; 08:00; 14:00; 2; AFU1; 120
Jan; Novak; Adresa 1; 01.01.1990; 05.05.2015; 7; 08:00; 15:00; 4; CGHU5; 154

Uloha je vytvorit zoznam s unikatnymi osobami, kde su spocitane celkove odpracovane hodiny, celkove odjazdene hodiny, celkova trzba a priemerna trzba za hodinu. (+ osobna karta kazdej osoby kde sa podla roznych podmienok bude vyhodnocovat jej stav apod.)
Cize z povodneho zoznamu kde je 10 riadkov vznikne zoznam iba s 2. riadkami.

 
Nahoru Odpovědět 14. května 20:22
Avatar
Odpovídá na joon5
Tomáš Novotný:14. května 20:59

Tak podle tohoto, mi vyvstalo na mysli více dotazů než odpovědí :-D
Zase budu trochu vycházet z toho s čím jsem se již setkal...

1. který z údajů bereš za jedinečný? z uvedených podle mne není ani jeden, ikdyž je pravda, že u některých je malá pravděpodobnos­t...ale nikdy nevíš... takže nějaká kombinace vícero sloupců?
možná ta SPZ, pokud si nepůjčují mezi sebou auta
2 . mluvil si o 35 sloupcích? tu je jen 11? což podle mne ovlivňuje způsob výběru dat... ty další jsou kde?
3. ještě je to jednorázový proces zpracování dat? nebo se s tím pracuje vícekrát? náhled a zpětná analýza, jiná období apod..?

pokud popisuješ, že je tam několik funkcí pro osobní kartu atd.... to už vidím spíše na nějakou databázi, pokud ji z nějakého důvodu nechceš, tak by bylo vhodné alespoň csv rozsekat podle těch 'unikátních osob' na samostatná csv (osobně preferuji spíše nějaký xml-like format)
protože jinak osobní karta člověka s třeba 400 řádky bude i tak vždy zpracovávat CSV se 40tis řádky což teda fuj...

Nahoru Odpovědět 14. května 20:59
∞ ... the exact amount of possibilities how to deal with the situation ... so by calm, your solution is one of many
Avatar
joon5
Člen
Avatar
Odpovídá na Tomáš Novotný
joon5:14. května 21:23

Prosim neber to doslovne. To je iba zjednoduseny priklad, ale podstata je ta ista. Povodny subor tu zverejnit nemozem.
Ten CSV subor je pevne dany a ja stym nemozem nic spravit. Kazdym dnom pribuda jeho velkost (pribudaju nove riadky)

  1. jedinecny udaj mam ID, na tom priklade to moze byt datum narodenia.
  2. v originale mam stlpcov 35, urcite pouzivat budem 15, dalsich cca 8-10 stlpcov prilezitostne, podla podmienok.
  3. je to jednorazovy proces, PHP skript posle vysledne pole (JSON) do html tabulky, tam uz filtrovanie funguje na klientovom PC.

V podstate z asi max. 40 tisic riadkov skript vygeneruje iba 200-300 riadkov, v ktorych uz mam vyfiltrovane (vypocitane) vysledky (jeden riadok = jedna osoba).

Osobna karta sa vygeneruje jednorazovo (ako string s HTML tagmi) a ta sa zobrazi v HTML tabulke (u klienta) v prvom stlpci ... Osobna karta je vlastne iba meno+priezvis­ko+adresa+datum narodenia apod., a potom nejake dynamicke udaje ako pohlavie, hodnotenie podla trzby, hodnotenie podla odpracovanych hodin, pozicia vo firme apod..

 
Nahoru Odpovědět 14. května 21:23
Avatar
Odpovídá na joon5
Tomáš Novotný:14. května 23:00

Právě že jsem chtěl tímto naznačit, že ta podstata být stejná nemusí, zvláště když používáš primárně 30% sloupců. V příkladu co si dal používáš patrně většinu sloupců a to je dle mne docela zásadní rozdíl hlavně v té paměti jak se v začátku ptáš. Rozdíl 100MB vs. 300MB je dle mne zásadní.
Nikdo nepsal, že sem máš dát reálná data, ale hlavičku s pseuodo - příkladovými daty určitě nikoho nezabije ve formátu, který tam skutečně je.
No, takže za mně bych to řešil strčením do nějaké db třeba SQLite, pokud nemáš k dispozici db server. Protože by za tebe vyřešila dost věcí, viz primárně ty součty nad vybranou skupinou dat.
Píšeš, že je to jednorázově což teda musí být masakr. To totiž znamená, že všechna data pro následné filtrování máš pak v prohlížeči pro všech 250 lidí jak pro sumáře tak jejich detaily + osobní údaje :-O.
Pokud db fakt ne a ikdyž pro sumář, procházíš stejně celý soubor, jsem pro to regulární výrazem přes ID vytáhnout řádky. Pak třeba explodem přes ';' rozskat a vytáhnout si jen co potřebuješ.
Jinou možností, co mne napadá je využít parser, který je defaultně pro csv v php a bude-li problém s pamětí tak vstupní soubor rozdělit na více menších.
Jinak to 'jednorázově' se mi moc nepozdává, když píšeš, že se data mění - přibývají každý den - tj. si vykládám, že je možné, že se na ně každý den někdo podívá a provádí se znovu výpočty ze včera + se přidají ty dnešní.
Jednorázovým jsem měl na mysli třeba import bankovních výpisů... ty uděláš jednou a pak už ne.

Nahoru Odpovědět 14. května 23:00
∞ ... the exact amount of possibilities how to deal with the situation ... so by calm, your solution is one of many
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:15. května 8:13

Jak pisi kluci. Take bych primo z toho csv parseru tahal jen, co potrebuji. A k tomu tak ci tak je lepsi pouzit funkci, jakou pises. Jakoze ty mas nejspis kod:

$csv  = file_get_contents($path);
$table = parsujCSV($csv); // function parsujCSV(&$csv);
// parsujCSV($csv, $table); // function parsujCSV(&$csv, &$table);
$table = filtruj($table, $kriteria);

To povazuji za spravny a prehledny postup. (podobne filtruji csv v js - uloz si to jako uplnou stranku, i s js kodem a pres prochazet si tam to sve csv otevri https://webapp.fpf.slu.cz/…as3-view.htm)

Ale kdybys to chtel rychlejsi, nebo mozna jen uspornejsi na pamet, tak by ten filtr musel byt v parseru. Minimalne usetris jeden cyklus.

function parsujCSV(&$csv);
{
$parser = new mujCsvParser();
$table = array();
while ($parser->end)
   {
   $row = $parser->next($csv)
   $row = filtruj($row);
    if ($row) $table[] = $row;
    }
return $table;
}

A uplne idealni, kdybys to dal do parseru, pokud je to tvuj vlastni kod. Ale, to je asi vedlejsi.

  • Co se tyce zpracovani 30.000 radku, to by melo probehnout v pohode. Kdyz mi stiha ten javascript asi 2000 radku, 30.000 by pro nej nebyl problem.
  • Pokud to csv nahravas pres web-upload, tam se celkem vyplati pouzit zipovani pro velke soubory.
  • Ty radky bych tam filtroval jako $new_row = array(); $new_row[] = $row[2]; $new_row[] = $row[5];
  • Co jsem upravoval jedne firme parser i o zipovani, tak manik rikal, ze predtim cela operace nasoukani csv do db trvala asi 30s, po uprave asi 2-3s. Tam mel dost strasidelny a neusporny kod a taky nahraval 17MB csv, ktere se krasne ziplo asi na 500k :)
  • Jinak by slo to csv primo nasoukat do db a db to dokaze rychle filtrovat. Pokud by s tim php melo problem, mohlo by to byt tak o 30-50% rychlejsi.
 
Nahoru Odpovědět  +1 15. května 8:13
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:15. května 8:25

Jo, a pokud mas pocit, ze je neco pomalejsi, a chces zjistit co, muzes si zaznamenavat mirotime float a na konci to odecist a zjistit, ktera ta cast je pomala. Ale to umi urcite i nejak php.
https://www.php.net/…icrotime.php (Example #1)
nebo...

$time = array();
$time[] = microtime();
$csv  = file_get_contents($path);
$time[] = microtime();
$table = parsujCSV($csv); // function parsujCSV(&$csv);
$time[] = microtime();
$table = filtruj($table, $kriteria);
$time[] = microtime();
zpracujTimeAZobraz($time);
// nebo
$time[] = array('parsujCSV start', microtime());
$table = parsujCSV($csv); // function parsujCSV(&$csv);
$time[] = array('parsujCSV end', microtime());
 
Nahoru Odpovědět 15. května 8:25
Avatar
joon5
Člen
Avatar
joon5:16. května 2:42

Pani dakujem za hodnotne informacie.

Aktualny kod na precitanie CSV suboru mam takyto:

if (!empty($_FILES['csv_file']['name']))
{
        $dataCSV = array();
        if (($file_data = fopen($_FILES['csv_file']['tmp_name'], 'r')) === false)
        {
        throw new Exception('There was an error loading the CSV file.');
        }
        else
        {
        $headers=fgetcsv($file_data,600,";");

        while (!feof($file_data))  {
        $line = fgetcsv($file_data, 1000,";");
        if (!empty($line)) $dataCSV[] = array_combine($headers, $line);
        }
        fclose($file_data);
        }
}

Urcite to neni optimalny sposob...zbytocne vytvaram tak velke pole v ktorom je vela nepotrebnych informacii.
Asi by sa mali ukladat len vyfiltrovane riadky hned pri nacitavani CSV suboru, asi takto:

$line = fgetcsv($file_data, 1000,";");
if (!empty($line)) $dataCSV[] = filtruj($headers, $line);

Ale hned tu vidim problem, ako vyhladat duplicitny riadok v tom istom poly :-) Predat pole do funkcii filtruj ... $dataCSV[] = filtruj($headers, $line, &$dataCSV) asi nebude spravna volba.

 
Nahoru Odpovědět 16. května 2:42
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17. května 9:04

$dataCSV[] = array_combine($he­aders, $line);
Ok, ale, pokud ta data z csv dal zpracovavas, tak bych tento radek zakomentoval a pouzival ciselny index. A az nazaver, vystup, bych pridal textove indexy. Nebo je tam vubec nedaval. Zalezi na tom, jaky by byl casovy rozdil zpracovani proti ciselnym indexum. Ja bych videl rozdil tak mas 10-20%. Pri 3s bezvyznamne, pri 10s (cas celeho kodu) trochu problem.

 
Nahoru Odpovědět 17. května 9:04
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 18 zpráv z 18.