Class with constructor is slower in PHP - php

I have created two classes:
class TestClass
{
public $id;
public $name;
public $time;
}
class TestClassCtor
{
public $id;
public $name;
public $time;
public function __construct()
{
}
}
Now I run a simple code:
$tt = microtime(true);
$data = array();
for ($i = 0; $i < 10000; $i++)
{
$t = new TestClass();
$t->id = rand();
$t->name = rand();
$t->time = rand();
$data[] = $t;
}
echo microtime(true) - $tt;
echo "\n";
$tt = microtime(true);
$data1 = array();
for ($i = 0; $i < 10000; $i++)
{
$t = new TestClassCtor();
$t->id = rand();
$t->name = rand();
$t->time = rand();
$data1[] = $t;
}
echo microtime(true) - $tt;
Now, the second code with TestClassCtor is around 30% slower. Why? (tested with PHP 5.6 and 7.1).
Edit:
The similar difference can be spotted with (in this case, I can understand it - passing arguments to method may be slower).
class TestClassCtorFill
{
public $id;
public $name;
public $time;
public function __construct($id, $name, $time)
{
$this->id = $id;
$this->name = $name;
$this->time = $time;
}
}
In this case, I can create class in one line as
for ($i = 0; $i < 10000; $i++)
{
$data1[] = new TestClassCtorFill(rand(), rand(), rand());
}
Yes, this is safer, because user must set all three parameters and he wont forget to set something.
However, when I use this inside my internal framework class, I can omit the ctor entirely and set members directly as with TestClass to save some time. For a few instances, this wont be a much of a difference. But if I create tousands of them, it could be.
Edit 2
I know about cost of a function call. However, constructor is called upon new, so if I write or not one, some constructor should be called. If there is no constructor provided by user a default one is caled. Memory for object must be allocated either way.

Related

Access outer variables from anonymous class

I was trying an alternative way of doing this :
public function index()
{
$faker = Faker\Factory::create('fr_FR');
$ideas = [];
for ($i = 1; $i <= rand(10, 50); $i++) {
$idea = new \stdClass;
$idea->id = $i;
$idea->author = $faker->name;
//...
$ideas[] = $idea;
}
}
Instead of creating object and assigning attributes in the loop, I would like to create the object from a class, and populate $ideas[] with the array_pad() function :
public function index()
{
$faker = Faker\Factory::create('fr_FR');
$ideas = [];
$idea = new class {
private $id;
private $author;
function __construct() {
$this->id = count($ideas) + 1;
$this->author = $faker->name;
}
};
array_pad($ideas, rand(10, 50), new $idea);
}
So I need to access $faker and $ideas from the anonymous class. I tried to pass them to the class like this :
$idea = new class($ideas, $faker) {
private $id;
private $author;
private $ideas
private $faker
function __construct($ideas, $faker) {
$this->id = count($ideas) + 1;
$this->author = $faker->name;
}
};
But I get a
Too few arguments to function class#anonymous::__construct(), 0 passed
Sad news: you cant use array_pad for this.
Here the fixes you need to apply to get rid of the error:
// array_pad($ideas, rand(10, 50), new $idea);
array_pad($ideas, rand(10, 50), $idea); // remove new
Since you did the new already here:
$idea = new class($ideas, $faker) {
Even though this will fill $ideas. It will store the same reference to your $idea over and over. Which means if you alter one element this change will be on all elements (i guess this is not desired).
In order to get this working you will have to use a loop, which creates a new $idea for every entry:
$faker = Faker\Factory::create('fr_FR');
$ideas = [];
for ($i = rand(10, 50); $i > 0; $i--) {
$ideas[] = new class($ideas, $faker) {
private $id;
private $author;
function __construct($ideas, $faker) {
$this->id = count($ideas) + 1;
$this->author = $faker->name;
}
};
}
Working example.
Additional information
Instead of doing this
for ($i = 1; $i <= rand(10, 50); $i++)
better do this
for ($i = rand(10, 50); $i > 0; $i--)
The reason is the comparison is getting called on every loop, so you generating a new random number on every loop. Example
This is problematic because you tend to get way more low numbers like this. For example to get 50 loops the random has to return > $i every time - which is very unlikely.
One more thing: array_pad returns the filled array, so you'd have to write
$ideas = array_pad(...

php crashes with too many objects

Description:
The test script below works fine for 10 iterations but crashes (Segmentation fault) for 400000 iterations while it shouldn't crash.
Uses Php 7.2 on docker (Version 17.09.0-ce-mac35 (19611))
with no extension.
Test script:
<?php
class Lim {
public $id;
public $inv;
public $fi;
function __construct($id) {
$this->id = $id;
$this->inv = new Inv($this);
}
};
class Inv {
public $inv;
public $fi;
function __construct($inv) { $this->inv = $inv; }
}
$max = 400000;
//$max = 10;
$lim0 = new Lim(0);
$limp = $lim0;
for ($i=1; $i<$max; $i++) {
$lim = new Lim($i);
$lim->fi = $limp->inv;
$limp->inv->fi = $lim;
$limp = $lim;
}
Does anyone have an idea why ?
Thanks
You are creating a new $lim but not destroying the old one. So you are running out of memory.
add this
unset($lim);
after
$limp = $lim;

Is it faster to create a new class inside a function or pass it as a parameter in PHP

I've got a class that is used in a few functions independently from one another. So my question is: is it faster to pass the initiated class as a parameter to the function or to create a new instance every time the function is called upon?
Like this:
$class_a = new Class_a;
function rnd_fun($class_a){
... do stuff...
}
Or like this:
function rnd_fun(){
$class_a = new Class_a;
... do stuff...
unset($class_a);
}
I tried to do a benchmark and these are the results by using this
very creative class
class Class_a {
public $first_name = "";
public $age = "";
public function setVar($first_name, $age){
$this->first_name = $first_name;
$this->age = $age;
}
public function wasteTime(){
$i = 0;
while($i < 100){
$this->age = $this->age * $i;
$this->age = $this->age / pi();
$i++;
}
}
}
By passing the class as a parameter I got something around 0.46s using this code:
$class_a = new Class_a;
$i = 0;
while($i < 10000){
test($class_a);
$i++;
}
function test($class_a){
$class_a->setVar("Geronimo", "72.1");
$class_a->wasteTime();
}
This is by initiating a new class inside the function:
$i = 0;
while($i < 10000){
test();
$i++;
}
function test(){
$class_a = new Class_a;
$class_a->setVar("Geronimo", "72.1");
$class_a->wasteTime();
unset($class_a);
}
I have to point out though that the second method took a slightly higher time to execute (around 0.47s).
However I believe that the difference between the two is negligible since both run at around the same speed for this many iteration and their delay won't be noticeable in an average piece of code.

Poor Man's Session ID Causes Parse Error

I created this little guy to get large session IDs for my own purposes:
// returns a 1024-byte hash for session IDs
class sha512_session {
private $IDvalue = '';
private $IDMAX_SALT = mt_getrandmax();
for ($i = 0; $i < 8; $i++) {
$seed = mt_rand(100, $IDMAX_SALT);
$IDvalue .= strtoupper(hash('sha512', $seed, false));
}
public function getID() {
return $IDvalue;
}
}
Outside of a class context, the for() loop works like it should. When I put this (working) code in the class above, PHP returns the following:
Parse error: syntax error, unexpected '(', expecting ',' or ';' in ../sha-iterator.php on line ... ( line # for "private $IDMAX_SALT = mt_getrandmax()" )
So, it works fine outside a class and breaks inside a class. Where did I mess up?
The For loop must be placed within a function. I think you should place it in a constructor.
class sha512_session {
private $IDvalue = '';
private $IDMAX_SALT;
function __construct() {
$this->IDMAX_SALT = mt_getrandmax();
for ($i = 0; $i < 8; $i++) {
$this->seed = mt_rand(100, $this->IDMAX_SALT);
$this->IDvalue .= strtoupper(hash('sha512', $this->seed, false));
}
}
public function getID() {
return $this->IDvalue;
}
}
Ok a comment on here answered my question. Class property initialization during declaration must be a constant at compile time, but mt_getrandmax() is runtime info, so it fails. The correct way here is to declare the variable, then initialize it separately:
class sha512_session {
private $IDvalue = '';
private $IDMAX_SALT;
private function getRandomMax() {
$this->IDMAX_SALT = mt_getrandmax();
return $this->IDMAX_SALT;
}
function __construct() {
for ($i = 0; $i < 8; $i++) {
$this->seed = mt_rand(100, $this->getRandomMax());
$this->IDvalue .= strtoupper(hash('sha512', $this->seed, false));
}
}
public function getID() {
return $this->IDvalue;
}
}
$sessionID = new sha512_session();
echo $sessionID->getID();
Or any variation on that theme.

Using a trait to change a variable in another class results in slower performance

I had some free time and decided to benchmark a couple of options for a custom logging system I will be implementing in my application. The point is to simply log events during execution from different classes and functions into an array that can later be examined.
While trying these out I saw something that puzzled me: Using a trait to modify a variable in another class takes longer than modifying a variable in "self". It also takes longer than modifying the variable directly.
I'm not exactly interested in miniscule performance gains, and have not yet decided on the final implementation either. I'm just curious about why this happens.
Here's the code to test it. I also did some other tests, but they were slower for obvious reasons.
class ExternalStore {
public static $log = [];
}
trait LoggerTrait {
public static function addLog($time, $event) {
return [$time, $event];
}
}
echo "<h1>Changing external variable directly</h1>";
class ExternalAppend {
public function doStuff() {
for ($i = 0; $i < 100000; $i++) {
ExternalStore::$log += [microtime(), "Stuff done"];
}
}
}
$ExternalAppend = new ExternalAppend;
$start = microtime(true);
$ExternalAppend->doStuff();
$time = microtime(true) - $start;
echo "Execution time: $time<hr>"; // ~0.18...
echo "<h1>Using a trait to change internal variable</h1>";
class TraitUser {
use LoggerTrait;
public static $log = [];
public function doStuff() {
for ($i = 0; $i < 100000; $i++) {
self::$log += self::addLog(microtime(), "Stuff done");
}
}
}
$TraitUser = new TraitUser();
$start = microtime(true);
$ExternalAppend->doStuff();
$time = microtime(true) - $start;
echo "Execution time: $time<hr>"; // ~0.18...
echo "<h1>Using a trait to change external variable</h1>";
class TraitUserExternal {
use LoggerTrait;
public function doStuff() {
for ($i = 0; $i < 100000; $i++) {
ExternalStore::$log += self::addLog(microtime(), "Stuff done");
}
}
}
$TraitUserExternal = new TraitUserExternal();
$start = microtime(true);
$TraitUserExternal->doStuff();
$time = microtime(true) - $start;
echo "Execution time: $time<hr>"; // ~0.30...
Finally had some time to look into this and the solution was simple. The problem was calling $ExternalAppend->doStuff() instead of $TraitUser->doStuff() on line 41. After fixing this the execution times of TraitUser and TraitUserExternal are similar. There is also a mistake of using $var += [$val1, $val2] instead of $var[] = [$val1, $val2], but that doesn't seem to affect the differences. Only the total execution time.
Slower performance in TraitUser andTraitUserExternal compared to ExternalAppend comes from the combined overhead of calling a function and using a return value.
The following code confirms the findings. Modifying a variable directly without calling any functions is the fastest way. Calling a function and modifying the variable there is the second fastest. Third (marginally slower than the second) way is modifying a variable with a value returned from a function.
The fourth function doStuff4() also shows a better way of using a trait to modify a variable in another class.
<?php
trait Logger {
protected static function modifyInternalValue($time, $event) {
self::$internalLog[] = [$time, $event];
}
protected static function returnValue($time, $event) {
return [$time, $event];
}
protected static function modifyExternalValue($time, $event) {
ExternalLogger::$externalLog[] = [$time, $event];
}
}
class ExternalLogger {
public static $externalLog = [];
public static function modifyValue($time, $event) {
self::$externalLog[] = [$time, $event];
}
}
class LoggerUser {
use Logger;
public static $internalLog = [];
protected static $iterations = 10000;
public function doStuff1() {
echo "<h1>1 - Directly modifying an internal variable</h1>";
for ($i = 0; $i < self::$iterations; $i++) {
self::$internalLog[] = [microtime(), "Stuff done"];
}
}
public function doStuff2() {
echo "<h1>2 - Trait returns a value to internal variable</h1>";
for ($i = 0; $i < self::$iterations; $i++) {
self::$internalLog[] = self::returnValue(microtime(), "Stuff done");
}
}
public function doStuff3() {
echo "<h1>3 - Trait modifies a variable inside a function</h1>";
for ($i = 0; $i < self::$iterations; $i++) {
self::modifyInternalValue(microtime(), "Stuff done");
}
}
public function doStuff4() {
echo "<h1>4 - Trait modifies a variable of an external class</h1>";
for ($i = 0; $i < self::$iterations; $i++) {
self::modifyExternalValue(microtime(), "Stuff done");
}
}
public function doStuff5() {
echo "<h1>5 - External class modifies a variable in itself</h1>";
for ($i = 0; $i < self::$iterations; $i++) {
ExternalLogger::modifyValue(microtime(), "Stuff done");
}
}
}
function profileFunction($function) {
$LoggerUser = new LoggerUser();
$start = microtime(true);
$LoggerUser->$function();
$time = microtime(true) - $start;
echo "Execution time: $time<hr>";
}
profileFunction("doStuff1"); // Fast | Direct modification
profileFunction("doStuff2"); // Slowest | Function returns a value
profileFunction("doStuff3"); // Slower | Function modifies a value
profileFunction("doStuff4"); // Slower | Function modifies external class
profileFunction("doStuff5"); // Slower | External class modifies itself

Categories