Hardcoded

free(DOM);

Archive for March 2nd, 2010

Static call versus Singleton call in PHP

with 40 comments

Introduction

In the past several months I’ve been working with a rather large application built with symfony. I noticed that symfony makes heavy use of the Singleton pattern (other frameworks, like Zend do that too); everywhere in the code there were pieces like this one:

<?php
// ...
sfSomething::getInstance();
// ...
?>

I know that in more than half of the situations, you can write your code using plain static classes, with some initialize() method, as an alternative to writing singletons. For example, this is a dummy Singleton:

<?php
class DummySingleton {
    private function __construct(){}
    private function __clone(){}
    public static function getInstance(){
        if(self::$__instance == NULL) self::$__instance = new DummySingleton;
        return self::$__instance;
    }
    public function foo(){
        echo 'I am DummySingleton::foo()';
    }
}
?>

Now this is a completely useless class, but it suits our purpose of illustrating the Singleton. Notice the amount of code needed by the Singleton pattern. Except the foo() method, all the code in the class makes sure you have only one instance at any time during the execution.

Now let’s write a static class that does the same thing as the Singleton:

<?php
class DummyStatic {
    static public function foo(){
        echo 'I am DummyStatic::foo()';
    }
}
?>

This is much cleaner, as we don’t need the extra code the Singleton needs, and can focus on our task at hand.

Performance comparison

Let’s compare the performance of the two approaches. I’ve written a small test script that looks like this:

<?php

/**
* A singleton class
*/
class TestSingleton {
    // singleton code
    private static $__instance = NULL;
    private function __construct(){}
    private function __clone(){}
    static public function getInstance(){
        if(self::$__instance == NULL) self::$__instance = new TestSingleton;
        return self::$__instance;
    }

    // our actual code
    public $val = 0;
    public function test(){
        for($i=0;$i<30;$i++) $this->val += $i;
    }
}

/**
* a plain static class (all members are static)
*/
class TestStatic {
    static public $val = 0;
    static public function test(){
        for($i=0;$i<30;$i++) self::$val += $i;
    }
}

// how many runs
$runs = 500000;

// benchmarking Singleton
$start = microtime(TRUE);
for($i=0;$i<$runs;$i++) TestSingleton::getInstance()->test();
$run1 = microtime(TRUE) - $start;

// benchmarking static
$start = microtime(TRUE);
for($i=0;$i<$runs;$i++) TestStatic::test();
$run2 = microtime(TRUE) - $start;

echo '<pre>';
echo 'test singleton: '.number_format($run1, 8)." s\n";
echo 'test static:    '.number_format($run2, 8).' s';
?>

Basicly, I put together the two types of classes. Both have a method called test(), which does some arithmetic operations, just to have something that spends some execution time.

I’ve ran this script for various values for the $runs variable: 100, 1k 10k, 100k, 200k, 300k, 500k and 1M.

Test results

Number of runs Singleton call time (s) Static call time (s)
100 0.004005 0.001511
1,000 0.018872 0.014552
10,000 0.174744 0.141820
100,000 1.643465 1.431564
200,000 3.277334 2.812432
300,000 5.079388 4.419048
500,000 8.086555 6.841494
1,000,000 16.189018 13.696728

I have also done some spreadsheet magic, and generated this chart:

As you can see, for a relatively small number of runs (<1k), the Static code is significantly faster than the Singleton, an than it stabilizes arround 15% faster than Singleton, as I expected. This is because every function/method call involves some operations (symbol lookup, stack manipulation etc.), and each call to the Singleton method, in fact, also calls the getInstance() static method.

Conclusion

It may not be that obvious that making extensive use of Singletons has this kind of side effect; however, if your code has more that 100 or 1,000 calls to some getInstance() method of a Singleton class, you might want to consider caching the reference to the object it returns, or even refactoring the code to use only static method calls.

You might say that you need an object because you do stuff in the constructor. That can be easily achieved with some kind of static initialize() method, that should be called once in your code, just before usage. If you have some auto loading mechanism in place, you could call it just after loading the class, for example, if you want to automate the initialization process. But keep in mind that this is not a 100% replacement for Singletons; you need an object if you want to serialize/unserialize it (for caching, some RPC call, etc.).

Update.

Tested with Facebook’s HPHP compiler:

I’ve tested the script using the HPHP compiler, and the results are spectacular. While keeping the same time ratio between the Singleton and Static calls, what stroke me is the huge difference (HPHP is ~ 200 times faster):

Number of calls Time spent (Apache) Time spent (HPHP)
Apache Singleton call Apache Static call HPHP Singleton call HPHP Static call
100 0.004005 0.001511 0.00001502 0.00000906
1,000 0.018872 0.014552 0.00008988 0.00007486
10,000 0.174744 0.141820 0.00075102 0.00063801
100,000 1.643465 1.431564 0.00829983 0.00795388
200,000 3.277334 2.812432 0.01839614 0.01339102
300,000 5.079388 4.419048 0.02502608 0.01932502
500,000 8.086555 6.841494 0.04114008 0.03280401
1,000,000 16.189018 13.696728 0.07872796 0.06373119

Happy coding.

Written by Doru Moisa

March 2, 2010 at 1:39 am

Posted in Development, php

Tagged with , , , ,