Door
Ozzie PHP
op 24-01-2023 22:35
gewijzigd op 24-01-2023 22:36
3.959 views
Ik vraag me iets af. Als je binnen een method meerdere keren dezelfde property aanroept, biedt het dan een voordeel om die property binnen die method in een variabele op te slaan? Met voordeel bedoel ik, is het efficiënter? Of maakt het weinig verschil?
Je kunt dus dit doen:
<?php
public function example() {
$this->foo->doThis();
$this->foo->doThat();
if ($this->foo->count() > 5) {
echo 'okay!';
}
}
?>
Of dit:
<?php
public function example() {
$foo = $this->foo;
$foo->doThis();
$foo->doThat();
if ($foo->count() > 5) {
echo 'okay!';
}
}
Ik heb zelf ook even getest met jouw voorbeeld en het gaat inderdaad om minimale verschillen. Als je het aantal aanroepen per method ophoogt, dan zie je dat de 2e methode met de gekopieerde variabele (meestal) net iets sneller is. Het gaat dan uiteraard om micro-optimalisatie.
Ik was hier wel nieuwsgierig naar. Het scheelt dus niet veel, maar toch iets. Je zal er de oorlog niet mee winnen, maar als je dus een property binnen een method meerdere keren aanroept, kan het in ieder geval geen kwaad om een lokale variabele te gebruiken.
Als je binnen een method meerdere keren dezelfde property aanroept, biedt het dan een voordeel om die property binnen die method in een variabele op te slaan?
Ik denk dat sommige Preciezen met opgeheven vinger naar het single-responsibility principle (SRP) zouden wijzen omdat zo'n methode al gauw te veel doet.
Ozzie PHP op 24/01/2023 22:35:31
Je kunt dus dit doen:
<?php
public function example() {
$this->foo->doThis();
$this->foo->doThat();
if ($this->foo->count() > 5) {
echo 'okay!';
}
}
?>
Een methode in een klasse is geen applicatie, maar daar lijkt dit voorbeeld wel op.
Gelukkig reken ik mezelf meer tot de Rekkelijken. ;-) Als je performance belangrijk vindt, stap dan om te beginnen af van naïeve getters en setters. De snelheidswinst van directe toegang tot class properties is veel groter.
Met variabelen lokaal dupliceren introduceer je ook een nadeel: je verbruikt meer geheugen. Als je dit met een grote array doet, kan het zelfs fataal aflopen.
Je kunt deze trade-offs eigenlijk niet (gemakkelijk) eerlijk testen. Je gooit er nu meer geheugen tegenaan voor de snelheidswinst, maar dat is nadelig voor processen die je buiten de test laat. Eén proces voor één client wordt sneller, maar in een multithreaded multiprocessing omgeving met meerdere clients betaal je de prijs dan elders.
Er zijn bijvoorbeeld "oplossingen" die WordPress sneller maken: bij 1 of 2 bezoekers wordt de snelheid dan bijvoorbeeld meer dan verdubbeld, maar bij 10 of meer gelijktijdige bezoekers stort het kaartenhuis al in.
>> Met variabelen lokaal dupliceren introduceer je ook een nadeel: je verbruikt meer geheugen.
Eens. De snelheidswinst is ook niet bepaald spectaculair te noemen. Wellicht merk je er iets van bij een miljoen aanroepen, maar dat is in de praktijk niet realistisch.
?Onbekende gebruiker
26-01-2023 21:09
Als je binnen een method meerdere keren dezelfde property aanroept, biedt het dan een voordeel om die property binnen die method in een variabele op te slaan? Met voordeel bedoel ik, is het efficiënter?
Het antwoord is ja, het is efficiënter.
Door $this->foo op te slaan in een tijdelijke variabele $foo voorkomt je dat PHP elke keer een extra lookup moet doen naar het geheugenadres dat je eigenlijk wilt gebruiken. Ten koste van een extra pointer, dat wel. Hoe vaker je het geheugen benadert, via de property of een method, hoe meer je bespaart.
Een plek waar ik het gebruikte was in een HTML5-object dat een DOM-object extends.
Natuurlijk moet je wel opletten dat je alleen de pointer kopieert, en niet een heel object waar Ward voor waarschuwt. Dus geen 'deep copy', je wilt alleen maar de 'reference' kopiëren. Bijvoorbeeld met een array:
<?php
$this->foo = [1, 2];
$foo = $this->foo; // deep copy
$foo[0] = 2; // verandert alleen $foo
var_dump($foo);
$foo = &$this->foo; // by reference
$foo[0] = 2; // verandert ook $this->foo
var_dump($foo);
?>
Hoewel het voor zich zou moeten spreken, zet ik het er voor de zekerheid toch maar even bij.
[size=xsmall]Toevoeging op 26/01/2023 21:15:43:[/size]
Met variabelen lokaal dupliceren introduceer je ook een nadeel: je verbruikt meer geheugen.
In dit geval gaat het om één geheugen pointer per variabele.
Ik heb het niet gecontroleerd voor PHP als geïnterpreteerde taal, maar normaal gesproken is een pointer gelijk aan de bitbreedte van je systeem. Een 64-bit machine gebruikt dan 8 bytes per pointer. Dat is echt geen probleem voor de hedendaagse machines met gigabytes aan werkgeheugen.
Hmmm, nu volg ik 'm niet meer helemaal. Als voorbeeld: stel je hebt een object en binnen dat object moet je database-bewerkingen kunnen uitvoeren. Je hebt een of andere database controller class gemaakt die je als property binnen het object instelt.
<?php
class Foo {
private $db;
public function bar($id) {
$db = $this->db;
$db->setSql('SELECT ...');
$db->setParam('id' => $id);
$result = $db->execute();
return $result;
}
}
?>
In dit voorbeeldje spreek ik 3 keer de database controller class aan. Ik sla die class op in de lokale variabele $db. Is die $db nu een pointer naar $this->db of is het een kopie van de database controller class? Ik zou denken dat laatste. Zodra de method is uitgevoerd, zou $db volgens mij weer opgeschoond moeten worden en is het geheugen weer leeg. Als ik by reference gebruik, dan verwijs ik toch nog steeds naar dezelfde pointer? Zou het dan juist niet performance kosten? Ik creëer dan namelijk een extra variabele, en bij iedere aanroep van die variabele moet de oorspronkelijke pointer (de class property) worden opgezocht zou ik denken.
[size=xsmall]Toevoeging op 27/01/2023 00:14:59:[/size]
UPDATE:
@Ward
>> Met variabelen lokaal dupliceren introduceer je ook een nadeel: je verbruikt meer geheugen.
Het lijkt erop dat als ik een variabele lokaal dupliceer het toch geen kopie betreft (en waarschijnlijk dus ook niet een verdubbeling van geheugen).
Ik heb hier een testje gemaakt met de onderstaande code:
<?php
class Bar {
private $msg;
public function test($msg) {
$this->msg = $msg;
}
public function shout() {
echo $this->msg;
}
}
class Foo {
private $bar;
public function __construct($bar) {
$this->bar = $bar;
}
public function say($msg) {
$bar = $this->bar;
$bar->test($msg);
$this->bar->shout();
}
}
$test_me = new Foo(new Bar);
$test_me->say('ha ha');
?>
Kijk naar de method 'say' in de class 'Foo'.
Ik kopieer hier d.m.v. $bar = $this->bar een class property naar een lokale variabele. Via deze lokale variabele stel ik de message ($msg) in. Vervolgens gebruik ik de class property (dus niet de lokale variabele) om de $msg weer te geven. Dat blijkt gewoon te werken. Klik hier voor het voorbeeld.
Blijkbaar verwijzen $bar en $this->bar naar hetzelfde object en wordt er dus helemaal niks gekopieerd. Of zie ik het nu verkeerd?