Forum Clubic

[PHP5] Performances des Singleton et Static

Bonsoir,

Dans mes site Internet je me mets à utiliser beaucoup de classes liées au fonctionnement du système (le moteur site) et non pas au métier (les données du site).
Notamment pour une connexion avec la base de données, mais également pour logger les événements, pour charger des classes (contenue dans des fichiers spécifiques) ou des objets linéarisés (serialized).

Jusqu’à présent, pour ce genre de classes Manager/Service, je faisais des classes statiques afin qu’elle soient accessible facilement depuis n’importe quel pan de code, puis j’ai découvert les singletons qui sont aussi facilement accessible mais, au contraire d’une classe statique, peuvent être étendue (héritage).

Je me suis demandé ce qui de la classe statique (ne contenant que des attributs et méthodes static) ou du Singleton était le plus rapide à l’utilisation.

J’ai donc fait le script suivant :

<?php

class Timer {
    private $start_mtimestamp = NULL;
    private $stop_mtimestamp = NULL;
    
    public function __construct() {}

    public function start() {    $this->start_mtimestamp = microtime(true);    }
    public function stop() {    $this->stop_mtimestamp = microtime(true);    }

    public function getDuration() {
        if($this->start_mtimestamp == NULL) { // Si le compteur n'a même pas été lancé
            return -1;
        }
        else {
            if($this->stop_mtimestamp == NULL) { // Si le compteur est en cours
                $this->stop(); // On le stoppe
            }
            return strtr(sprintf('%0.3f', ($this->stop_mtimestamp - $this->start_mtimestamp)), '.', ',');
        }
    }
};

class ClasseNormale {
    private $var1;
    private $var2;

    public function __construct() {}

    public function setVar1($var1) {    $this->var1 = $var1;    }
    public function setVar2($var2) {    $this->var2 = $var2;    }

    public function getVar1() {            return $this->var1;        }
    public function getVar2() {            return $this->var2;        }
};

class ClasseSingletonne {
    private static $instance = NULL;

    private $var1;
    private $var2;

    public function __construct() {}

    public static function _getInstance() {
        if(self::$instance == NULL) {
            self::$instance = new ClasseSingletonne();
        }
        return self::$instance;
    }

    public function setVar1($var1) {    $this->var1 = $var1;    }
    public function setVar2($var2) {    $this->var2 = $var2;    }

    public function getVar1() {            return $this->var1;        }
    public function getVar2() {            return $this->var2;        }
};

class ClasseStatique {
    private static $var1;
    private static $var2;


    public static function setVar1($var1) {        self::$var1 = $var1;    }
    public static function setVar2($var2) {        self::$var2 = $var2;    }

    public static function getVar1() {            return self::$var1;        }
    public static function getVar2() {            return self::$var2;        }
};

$timerClasseNormale = new Timer();
$timerClasseNormale->start();
for($i = 0; $i <= 10000; $i++) {
    $classeNormale = new ClasseNormale();
    $classeNormale->setVar1('A');
    $classeNormale->setVar2('Z');
    $classeNormale->setVar1($classeNormale->getVar1() . $classeNormale->getVar2());
    $classeNormale->setVar2(':)');
}
$timerClasseNormale->stop();

$timerClasseSingletonne = new Timer();
$timerClasseSingletonne->start();
for($i = 0; $i <= 10000; $i++) {
    ClasseSingletonne::_getInstance()->setVar1('A');
    ClasseSingletonne::_getInstance()->setVar2('Z');
    ClasseSingletonne::_getInstance()->setVar1(ClasseSingletonne::_getInstance()->getVar1() . ClasseSingletonne::_getInstance()->getVar2());
    ClasseSingletonne::_getInstance()->setVar2(':)');
}
$timerClasseSingletonne->stop();

$timerClasseStatique = new Timer();
$timerClasseStatique->start();
for($i = 0; $i <= 10000; $i++) {
    ClasseStatique::setVar1('A');
    ClasseStatique::setVar2('Z');
    ClasseStatique::setVar1(ClasseStatique::getVar1() . ClasseStatique::getVar2());
    ClasseStatique::setVar2(':)');
}
$timerClasseStatique->stop();


echo '<h2>ClasseNormale</h2>' . "\n";
echo 'Var1 : ' . $classeNormale->getVar1() . '<br />' . "\n";
echo 'Var2 : ' . $classeNormale->getVar2() . '<br />' . "\n";
echo 'Timer : ' . $timerClasseNormale->getDuration() . '<br />' . "\n";

echo '<h2>ClasseSingletonne</h2>' . "\n";
echo 'Var1 : ' . ClasseSingletonne::_getInstance()->getVar1() . '<br />' . "\n";
echo 'Var2 : ' . ClasseSingletonne::_getInstance()->getVar2() . '<br />' . "\n";
echo 'Timer : ' . $timerClasseSingletonne->getDuration() . '<br />' . "\n";

echo '<h2>ClasseStatique</h2>' . "\n";
echo 'Var1 : ' . ClasseStatique::getVar1() . '<br />' . "\n";
echo 'Var2 : ' . ClasseStatique::getVar2() . '<br />' . "\n";
echo 'Timer : ' . $timerClasseStatique->getDuration() . '<br />' . "\n";

?>

Ce qui me donne les résultats suivants :[quote=""]
ClasseNormale
Var1 : AZ
Var2 : :slight_smile:
Timer : 0,160
ClasseSingletonne
Var1 : AZ
Var2 : :slight_smile:
Timer : 0,723
ClasseStatique
Var1 : AZ
Var2 : :slight_smile:
Timer : 0,215
[/quote]
Il semble donc qu’une classe statique est plus rapide qu’un singleton.
On remarque également que la classe normale (ni statique, ni singleton) est encore plus rapide : cependant, pour ce servir d’un Manager/Service implémenté avec une classe normale il faut, à tout moment, avoir un pointeur vers l’instance de cette classe, ce qui obligerait à faire un global (dans fonctions et méthodes) dont j’ignore le coût processeur.

Qu’en pensez-vous ?

Je sais pas si chercher l’optimisation à tout prix t’aidera hein. “premature optimisation is root of evil” comme dirait l’autre.

Une classe statique, c’est pas très POO et polymorphique : que se passe-t’il si tu veux changer le code à la volée? t’es forcé d’aller rechercher toutes les instances ou pire, de modifier ta classe statique pour changer le code.

Le singleton permet d’éviter ça.

La classe statique est plus rapide, c’est normal : le code n’est pas le même , dans un cas tu vérifie si ClasseSingletonne::$instance vaut pas null (au passage: c’est === qu’il faut utiliser, là tu teste toute la classe, d’où des lenteurs).

Dans la classe statique, tu n’as pas lieu de le faire.

Une classe statique est plus rapide, c’est normal.
Demande-toi ce que tu veux faire de ta classe avant de chercher à l’optimiser.
C’est tout simplement pas les mêmes objectifs.

Un singleton, c’est fait pour ne pas avoir à instancier plus d’une fois une classe. Pour ne pas avoir plusieurs instances donc… plusieurs copies d’un objet… alors que dans une classe classique, il n’y a aucune instance : tu travailles directement sur ta classe. A la limite, c’est comme si tu avais un ensemble de fonctions à ta disposition avec leurs variables globales dans un package propre.

Donc si tu considères que ton objet doit pouvoir être réutilisable, utilise un singleton. Si c’est simplement une librairie ou un code complètement utilitaire, tu peux la passer en statique.

Me demande si une méthode statique n’est pas plus rapide qu’une fonction (taille de la table des symbôles des fonctions, etc)

J’en sais rien :confused:
Mais à mon avis la différence doit pas se voir du tout.