[PHP] performance verbeteren while loops

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Anoniem: 76551

Topicstarter
Tweakers,

Ik ben bezig met een intranet waarop o.a. kmstanden berekend moeten worden. Nu heb ik 1 grote pagina waarin een arrays - van db queries - d.m.v. while loops doorlopen worden. Zie onderstaande voorbeeld.

Het probleem is nu dat de pagina enorm slecht performed. Ik heb al wat laadtijden per onderdeel verzameld en er zitten 2 stukken in die erg lang duren, resp. 7 en 22 seconden. Onderstaande stuk code duurt tussen de 6-7 om uit te voeren.

Ik heb een aantal indexen aangebracht, maar dat maakte niet veel verschil (maakt voor onderstaande code helemaal niets uit, omdat ik op basis van id's de tabel induik). Ik las ook ergens dat het nogal kan schelen als for i.p.v. while gebruikt, alleen met for ben ik niet zo bekend.

Maakt dit echt veel verschil en is het bijvoorbeeld mogelijk om m.b.v. een for loop een array )op basis van een selectie) te doorlopen?

Ik hoop dat jullie me wat tips kunnen geven over hoe ik onderstaande - en soortgelijke -code sneller kan maken.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$query_ritten        = "SELECT  rit_id, aantal_km
                                        FROM    ritten
                                        WHERE   auto_id = '$auto_id'
                                        AND         aantal_km is not null
                                        ORDER BY datum, ritnr ASC";
$result = mysql_query($query_ritten);               

while($kmstanden = mysql_fetch_array($result)){                     
    $eindstand = $beginstand + $kmstanden[aantal_km]; // de 1e keer is beginstand de in 1e instantie opgehaalde beginstand
    $query_update_kmstand        = "UPDATE ritten 
                                                            SET      beginstand = '$beginstand',
                                                                         eindstand =  '$eindstand' 
                                                            WHERE    rit_id = '$kmstanden[0]'
                                                            AND      auto_id = '$auto_id'";
    $result3        = mysql_query($query_update_kmstand);                   
    $beginstand = $eindstand; // de eindstand wordt de beginstand voor de volgende rit                      
}


Bedankt alvast!

Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
of je een for of een while gebruikt maakt echt _niks_ uit.

de rede dat de queries zo langzaam gaan komt ook niet door php maar door je queries.. ik vind je structuur erg langzaam over komen. je voert nu enorm veel queries uit, wat volgens mij veel sneller kan

edit:
ik ken je DB strucuur niet, en weet niet wat het doel is van deze query, maar mocht je op de een of andere manier de query niet simpeler kunnen maken is dit ook een oplossing:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$query_ritten = "SELECT rit_id, aantal_km
                        FROM       ritten
                        WHERE     auto_id = '$auto_id'
                        AND            aantal_km is not null
                        ORDER BY datum, ritnr ASC";
$result = mysql_query($query_ritten);                

$q = "";
while($kmstanden = mysql_fetch_array($result)){                     
    $eindstand = $beginstand + $kmstanden[aantal_km]; // de 1e keer is beginstand de in 1e instantie opgehaalde beginstand
    $q .= "UPDATE ritten
              SET         beginstand = '$beginstand',
              eindstand =  '$eindstand' 
              WHERE     rit_id = '$kmstanden[0]'
              AND         auto_id = '$auto_id';";
    $beginstand = $eindstand; // de eindstand wordt de beginstand voor de volgende rit                        
} 
$result3 = mysql_query($q);

zo stuur je iig maar 1x een signaal naar je sql server, dit maakt zeker een boel vershil als je sql server niet lokaal staat.
anders is het slechts een klein verschil

[ Voor 78% gewijzigd door BasieP op 28-04-2006 23:56 ]

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

Anoniem: 76551

Topicstarter
Ok.

Maar afgaande op bovenstaand voorbeeld; wat zou je er anders kunnen dan? Ik zal in dit geval toch op basis van de rit die nieuwe begin en eindstand moeten inserten, omdat deze waarden per rit berekend en bijgewerkt moeten worden.

Acties:
  • 0 Henk 'm!

  • Blorgg
  • Registratie: Juni 2001
  • Niet online
Wat je nu doet is eigenlijk het resultaat van een berekening in een database opslaan. Dat is iets wat je eigenlijk nooit zou moeten doen.

Je zou beter alleen de kilometer standen kunnen opslaan, en als je tussenstanden wilt kan je die gewoon opvragen/berekenen. Je weet immers hoeveel KM elke rit is, dus je kan heel eenvoudig COUNT(aantal_km) doen om dat te achterhalen.

Als je bij elke rit ook nog zoiets als een datum veld hebt kan je zelfs heel eenvoudig je KM standen per dag/week/maand/jaar weergeven. Gewoon je DB laten rekenen is altijd het snelste, tenzij je echt belachelijk veel records hebt. Maar dat lijkt me hier niet het geval.

[ Voor 27% gewijzigd door Blorgg op 28-04-2006 23:58 ]


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 04-07 15:03

NMe

Quia Ego Sic Dico.

BasieP, het maken van één lange string met meerdere queries erin zal maar een marginaal verschil opleveren. Het sturen van een query naar de database server duurt zo lang niet, het ophalen wel.

Verder ben ik het met Blorgg eens dat dit soort berekenbare data helemaal niet in de database thuishoort. Je kan het nu berekenen, dus je kan het ook berekenen op het moment dat je de data uit de database trekt.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 08:13

crisp

Devver

Pixelated

Mwa, redundant data kan elders natuurlijk behoorlijke performancewinst betekenen ;)

Wat ik wel zie hier is dat er telkens dingen herberekend worden die mogelijk al eerder berekend zijn. Om dat te voorkomen zou je bijvoorbeeld een indicator kunnen inbouwen die aangeeft of iets al berekend is of niet.
Hier wat (ongeteste) voorbeelden - ik had ook de 'eindstand' als indicator kunnen gebruiken (als die 0 is is hij ws niet berekend), maar voor de duidelijkheid heb ik een kolom 'done' gebruikt:

per auto:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
$laatste_eindstand  = " SELECT
                    eindstand
                FROM
                    ritten
                WHERE
                    auto_id = '" . $auto_id . "'
                AND
                    aantal_km IS NOT NULL
                AND
                    done = 1
                ORDER BY
                    ritnr DESC
                LIMIT
                    1";

$result = mysql_query($laatste_eindstand);
if ($row = mysql_fetch_array($result))
    $beginstand = $row['eindstand'];
else
    $beginstand = 0;

$query_ritten       = " SELECT
                    rit_id, aantal_km
                FROM
                    ritten
                WHERE
                    auto_id = '" . $auto_id . "'
                AND
                    aantal_km IS NOT NULL
                AND
                    done = 0
                ORDER BY
                    ritnr ASC";

while ($kmstanden = mysql_fetch_array($result))
{                     
    $eindstand = $beginstand + $kmstanden['aantal_km'];
    $query_update_kmstand   = " UPDATE
                        ritten 
                    SET
                        beginstand = '" . $beginstand . "',
                        eindstand = '" . $eindstand . "',
                        done = 1
                    WHERE
                        rit_id = '" . $kmstanden['rit_id'] . "'
                    AND
                        auto_id = '" . $auto_id . "'";
    $result3 = mysql_query($query_update_kmstand);                    
    $beginstand = $eindstand;                      
}


of voor alle auto's:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
$query_ritten       = " SELECT
                    auto_id, rit_id, aantal_km
                FROM
                    ritten
                WHERE
                    aantal_km IS NOT NULL
                AND
                    done = 0
                ORDER BY
                    auto_id ASC, ritnr ASC";

if ($kmstanden = mysql_fetch_array($query_ritten))
{
    do
    {
        $auto_id = $kmstanden['auto_id'];

        $laatste_eindstand  = " SELECT
                            eindstand
                        FROM
                            ritten
                        WHERE
                            auto_id = '" . $auto_id . "'
                        AND
                            aantal_km IS NOT NULL
                        AND
                            done = 1
                        ORDER BY
                            ritnr DESC
                        LIMIT
                            1";

        $result = mysql_query($laatste_eindstand);
        if ($row = mysql_fetch_array($result))
            $beginstand = $row['eindstand'];
        else
            $beginstand = 0;

        do
        {
            $eindstand = $beginstand + $kmstanden['aantal_km'];
            $query_update_kmstand   = " UPDATE
                                ritten 
                            SET
                                beginstand = '" . $beginstand . "',
                                eindstand = '" . $eindstand . "',
                                done = 1
                            WHERE
                                rit_id = '" . $kmstanden['rit_id'] . "'
                            AND
                                auto_id = '" . $auto_id . "'";
            $result3 = mysql_query($query_update_kmstand);
            $beginstand = $eindstand;
        }
        while (($kmstanden = mysql_fetch_array($query_ritten)) && $kmstanden['auto_id'] == $auto_id)
    }
    while($kmstanden);
}


Het is natuurlijk eenvoudiger als je de niet-berekende ritten in een aparte tabel opslaat en pas overheveld naar de 'ritten' tabel tijdens het berekenen - dat scheelt je sowieso indices op de 'done' kolom en je pakt eenvoudiger alleen de auto's die daadwerkelijk ritten hebben gemaakt sinds de laatste berekening ;) (voor overzichten met nog niet geaggrereerde gegevens kan je dan altijd nog een UNION gebruiken).

Overigens neem ik aan dat een hoger ritnr nooit een eerdere datum kan hebben dan een lager ritnr, dus de datum kan je uit de sortering halen.
Verder vraag ik me af of aantal_km wel NULL zou mogen/kunnen zijn.

[ Voor 12% gewijzigd door crisp op 29-04-2006 01:55 ]

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • Blorgg
  • Registratie: Juni 2001
  • Niet online
crisp schreef op zaterdag 29 april 2006 @ 01:38:
Mwa, redundant data kan elders natuurlijk behoorlijke performancewinst betekenen ;)
Als je het over tientallen miljoenen records hebt misschien. Maar tegen de tijd dat er ergens zoveel ritten gemaakt worden die bijgehouden worden is er ook vast wel meer dan genoeg budget beschikbaar voor een wat professionelere oplossing dan een eenvoudig PHP scriptje ;)

Ik zou de TS toch echt aanraden om de manier van berekenen te herzien ipv een fundamenteel 'foute' methode proberen op te lappen. Tuurlijk zal het uiteindelijk allemaal wel gaan werken. Zeker als je je scripts gewoon laat draaien zonder de standaar 30 seconden time-out. Maar het is misschien wel net zo leuk voor de leerervaring om het gelijk goed te doen :)

Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 08:13

crisp

Devver

Pixelated

Blorgg schreef op zaterdag 29 april 2006 @ 02:12:
[...]

Ik zou de TS toch echt aanraden om de manier van berekenen te herzien ipv een fundamenteel 'foute' methode proberen op te lappen. Tuurlijk zal het uiteindelijk allemaal wel gaan werken. Zeker als je je scripts gewoon laat draaien zonder de standaar 30 seconden time-out. Maar het is misschien wel net zo leuk voor de leerervaring om het gelijk goed te doen :)
Dergelijke scripts draai je uiteraard al niet vanuit een browser, en daarnaast - indien je weet dat het langdurige scripts zijn - pas je daar de time-limit op aan.
Als het scripts zijn die 1x per dag draaien zou ik me overigens om die paar tiental seconden absoluut nog niet druk maken ;)

Intentionally left blank


Acties:
  • 0 Henk 'm!

Anoniem: 118860

[b][message=25661959,noline]
Je zou beter alleen de kilometer standen kunnen opslaan, en als je tussenstanden wilt kan je die gewoon opvragen/berekenen. Je weet immers hoeveel KM elke rit is, dus je kan heel eenvoudig COUNT(aantal_km) doen om dat te achterhalen.
SUM() natuurlijk he.

Acties:
  • 0 Henk 'm!

Anoniem: 76551

Topicstarter
Hier kan ik wel wat mee. Ik heb al gekeken naar de berekening van kilometerstanden en dit heb ik er nu al uitgehaald en verwerkt in de overzichts pagina. Ik ga ook nog naar de andere loops kijken of ik daar dingen niet onnodig doe. Bedankt voor het duwtje in de juiste richting!

Heb ik nog wel een andere vraag. Op de planners pagina (die even moet laden) wil ik een progressbar (animated gif) weergeven zodat de gebruiker ziet dat de pagina iets aan het doen is. Probleem is echter dat hij deze gif pas laat zien als de hele pagina verwerkt is.

Is er een manier om het eerste gedeelte van de pagina alvast te tonen, terwijl ondertussen de rest van de pagina uitgevoerd wordt?

Het gaat overigens wel over een website en niet over een script zoals hierboven wellicht gesuggereerd wordt.

Acties:
  • 0 Henk 'm!

  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 02-07 12:14
Met de PHP functies flush en ob_flush kun je de output buffer versturen zoals die op dat moment is. Houd er ook rekening mee dat sommige browsers, zoals IE zelf ook bufferen, zodat je een veelvoud van 512 of van 4096 bytes moet versturen voordat er gerenderd wordt. Ook worden sommige stukken HTML in sommige browsers pas gerenderd wanneer de einde tag van een element is gevonden, bijvoorbeeld <table>.

Acties:
  • 0 Henk 'm!

Anoniem: 76551

Topicstarter
_js_ schreef op donderdag 04 mei 2006 @ 09:29:
Met de PHP functies flush en ob_flush kun je de output buffer versturen zoals die op dat moment is. Houd er ook rekening mee dat sommige browsers, zoals IE zelf ook bufferen, zodat je een veelvoud van 512 of van 4096 bytes moet versturen voordat er gerenderd wordt. Ook worden sommige stukken HTML in sommige browsers pas gerenderd wanneer de einde tag van een element is gevonden, bijvoorbeeld <table>.
Bedankt. Nog een vraag. Ik heb ob_start() bovenaan elke pagina staan. Dit is omdat ik een aantal keren gebruik maak van de header functie van php (anders kreeg ik foutmeldingen van output already started enz.). Ik weet dat het niet netjes is maar het werkt wel.

Met betrekking tot bovenstaande. Als ik het goed begrijp zou ik om al bepaalde output te laten zien ob_flush na het stukje ouput moeten zetten wat ik wil laten zien. Klopt dit of werkt het anders?

Ik heb zelf ook al php.net gekeken, alleen de logica achter de verschillende functie in relatie tot hoe zich dat vertaalt op de php pagina is mij niet helemaal duidelijk.

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 03-07 11:38

Janoz

Moderator Devschuur®

!litemod

Het hele ob is gewoon een soort buffer. Normaal wordt, bij het verwerken van de php, alles direct doorgestuurd. Eventueel kan de webserver en de browser nog wel wat bufferen, maar over het algemeen kun je er vanuit gaan dat alles gelijk die kant op gaat.

Wanneer je ob_start gebruikt wordt de output niet gelijk verstuurd, maar als het ware in een grote string opgeslagen. Dat is de buffer. Dit wordt pas helemaal aan het einde verstuurd of op het moment dat je een flush doet.

ob_start gebruiken bij 'header laready sent' problemen werkt omdat een header commando helemaal aan het begin moet staan van de verstuurde data. Door alle output in een buffer te zetten kan de header er nog voorgezet worden. Als het al verstuurd is lukt dat niet meer en krijg je de bekende melding.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Anoniem: 76551

Topicstarter
Ok, duidelijk. Dan klopt het dus als ik ob_start() aan het begin van de pagina heb staan en ik wil een bepaald gedeelte alvast als output versturen dat ik ob_flush na het betreffende gedeelte moet plaatsen.

Wat ik overigens nooit begrepen heb, is hoe men het oplost als men een bepaalde gedeelte van een pagina wil laten uitvoeren en aan het eind van die pagina de gebruiker door wil sturen d.m.v. een header, zonder ob_start() te gebruiken. Je kunt de header dan toch niet aan het begin zetten, omdat dan de rest niet uitgevoerd wordt?

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 03-07 11:38

Janoz

Moderator Devschuur®

!litemod

Waarom weet je pas helemaal aan het einde van een pagina dat de gebruiker doorgestuurd moet worden? Binnen mijn applicaties weet ik al alle benodigde gegevens nog voor ik ook maar een html open tag gestuurd heb. Die 'headers already sent' problematiek wordt vooral veroorzaakt door een complete verstrengeling van layout code en buisness logic.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Het is nog niet gevraagd, maar je hebt natuurlijk wel een gecombineerde index op "auto_id, rit_id" zodat je tig updates iig niet gehinderd worden door onnodige table-scans ?

Of je rit_id moet al de PK zijn (waarbij ik niet snap dat je dan auto_id uberhaupt nog meeneemt in de where).

[ Voor 25% gewijzigd door ACM op 04-05-2006 12:04 ]


Acties:
  • 0 Henk 'm!

Anoniem: 76551

Topicstarter
Janoz schreef op donderdag 04 mei 2006 @ 09:59:
Waarom weet je pas helemaal aan het einde van een pagina dat de gebruiker doorgestuurd moet worden? Binnen mijn applicaties weet ik al alle benodigde gegevens nog voor ik ook maar een html open tag gestuurd heb. Die 'headers already sent' problematiek wordt vooral veroorzaakt door een complete verstrengeling van layout code en buisness logic.
Ik weet ook wel aan het begin van de pagina al dat de gebruiker doorgestuurd moet worden, maar het aan het begin van de pagina plaatsen van de header zorgt er toch voor dat de gebruiker meteen wordt doorgestuurd - zonder dat de pagina uitgevoerd wordt - toch? Of begrijp ik het nu verkeerd.

Rit_id is trouwens de pk dus dat is geen probleem. Hadden jullie ook nog ideeen over onderstaande:
Als ik het goed begrijp zou ik om al bepaalde output te laten zien ob_flush na het stukje ouput moeten zetten wat ik wil laten zien. Klopt dit of werkt het anders?

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 03-07 11:38

Janoz

Moderator Devschuur®

!litemod

Dan moet je er voor zorgen dat de dingen die je uitgevoerd wilt hebben voor dat je de gebruiker doorstuurt ook daadwerkelijk voor dat moment uitgevoerd worden. Daarnaast lijkt het me dat je datgene dat je hiervboven in bold neerzet natuurlijk ook makkelijk even zelf kunt proberen. Over het algemeen werkt dat een stuk sneller dan het telkens maar te vragen. Je mag best zelf nog wel een beetje gaan doen hoor ;).

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 02-07 12:14
Je kunt javascript gebruiken om bezoekers na het laden van de pagina door te sturen.
Pagina: 1