Entries for tag autoload

Thu Jan 07 2010

SplClassLoader php extension benchmarks

With ApacheBench

Filed under PHP, Programming. 2 comments

UPDATE (2010-01-09T22....Z): a small change (yet to be properly benchmarked) yields linear performance gains: the extension brings about 30% more RPS than the user code in all tests. /UPDATE

Ok, the C implementation of SplClassLoader is here on github. I am afraid I won't have much time in the coming days to follow what is going on. Anyway I took the time to do some benchmarks with ApacheBench. I will present the results in this post.

In order to ease things, I wrote a python script that generates a dummy directories subtree filed with php files containing blank classes with their proper namespace. The script also generates a php file named batch_instances.php (by default) which is responsible for instantiating all the generated classes in a row. It looks like that:

$a = new \vendor0\AA;
$a = new \vendor0\AAA;
$a = new \vendor0\B_B;
$a = new \vendor0\B_BB;
$a = new \vendor0\sub0\AA;
$a = new \vendor0\sub0\AAA;
...
$a = new \vendor1\sub0\AA;
$a = new \vendor1\sub0\AAA;
...
$a = new \vendor1\sub1\sub1\AA;
$a = new \vendor1\sub1\sub1\AAA;
$a = new \vendor1\sub1\sub1\B_B;
$a = new \vendor1\sub1\sub1\B_BB;

That php file is then included for the various benchmarks. The python script (fs_gen.py) is located under tests/for_bench but it is not integrated with the extension tests suite.

Setup

  • MacBook Pro 2.53ghz 4gb ram
  • nginx 0.8.27
  • PHP_5_3_FPM trunk compiled with --disable-all --enable-maintainer-zts. Unix sockets.

Note: APC was not loaded (nor any other accelerator.. I would have to install it btw).

Protocol

I did 3 kind of tests:

  • SplClassLoader() used without namespaces nor path arguments. We have to configure the include_path.
  • SplClassLoader('vendorN') where N goes from 0 to 3 at maximum. It means we have several 'vendor' namespaces. Note that the second argument (the path) is not passed to the constructor.
  • SplClassLoader('vendorN','/pathN') where the path is absolute. The php include_path is not used.

For example, the second test bootstrap file looks like this:

<?php
// 'vendorN' directories are under lib/
set_include_path(
   __DIR__.PATH_SEPARATOR
  .__DIR__.DIRECTORY_SEPARATOR.'lib'.PATH_SEPARATOR
  .get_include_path() // mine is empty btw
);

$v0 = new SplClassLoader('vendor0');
$v0->register();

$v1 = new SplClassLoader('vendor1');
$v1->register();

include 'batch_instances.php';

Since both implementations declare the same class SplClassLoader be it a user script or an extension, the php implementation has been renamed to SplClassLoaderUser.

I noted each result returned by ApacheBench. For each test, a few warm ups then 10 runs in a row. Results in the following tables are in requests per second. Also, the lowest and the highest numbers were removed prior calculating the mean.

The exact arguments passed to ab are not known at that time ;) Depending on the number of classes loaded, sometimes it was -n 3000 or -n 1000 but always -c 1 and of course always the same for a given test.

Results

24 classes under a unique 'vendor0' namespace containing several sub namespaces:

        test 1     test 2      test 3
        simple     with ns     with ns+path

user    496        429         449
ext     554        543         576
ratio   1.117      1.266       1.282

70 classes under two namespaces containing several sub namespaces:

        test 1     test 2      test 3
        simple     with ns     with ns+path

user    221        182         192
ext     232        228         246
ratio   1.050      1.252       1.281

Note: there were no underscore characters in the classes names used for the above bench.

126 classes under two namespaces containing several sub namespaces:

        test 1     test 2      test 3
        simple     with ns     with ns+path

user    119         99         104
ext     126        124         133
ratio   1.059      1.253       1.279

Conclusions

Results are indeed extremely regular. In fact I performed several other tests with different directories layouts but ratios were roughly the sames.

Internally, the C implementation does nothing more than the php implementation. spl_autoload_register() is called through the Zend Engine. There is no magic door. When more computations are involved as in the test number 3 (comparing namespaces and appending paths) the C implementation is ahead.

In fact these numbers suggest that we better have to pass both a namespace and an absolute path to the SplClassLoader constructor when using the extension. On the contrary the php implementation performs better with no argument at all, ie. no namespace comparison. Low level string manipulations are faster in C. All in all, both implementations call include() through the ZE.

One advantage of course concerning the extension is that the class is registered at module initialization (MINIT). The class remains in the engine between requests (if the sapi allows it).

PS. Why did I write that extension? I was looking for a simple (no dependencies) extension to illustrate a tutorial I am supposed to write (second part) for this blog (in french). I came up with SplClassLoader but I am by no way a ZE specialist.

Sat Dec 19 2009

A propos du PHP Standards Working Group

Et notamment SplClassLoader

Filed under Programming. 0 comment

L'arrivée de php 5.3 et son support pour les namespaces, les lambda functions et autres joyeusetés a le mérite de fédérer au sein du PHP Standards Working Group le gratin des lead developers des "grands" frameworks que sont Zend Framework, Symfony, PEAR, Solar, j'en passe et des meilleurs.

De l'interopérabilité Technique

Un des objectifs du Working Group est de s'accorder sur une interopérabilité technique (technical interoperability). De quoi s'agit-il exactement ? Personne ne le sait, c'est pourquoi dans un premier temps tout du moins, les protagonistes se focalisent sur une interopérabilité du mécanisme d'autoloading dans un contexte namespaces-aware.

PSR-0 Final Proposal

Le Working Group a publié le PSR-0 Final Proposal concernant l'autoloading. Ce document décrit les conditions que les auteurs de librairies et frameworks se doivent de respecter en terme de convention de nommage et de hiérarchie de fichiers afin d'assurer cette interopérabilité. Au passage, on notera les conditions sévères qu'il est nécessaire de respecter pour espérer avoir un droit de vote ou même de participation aux débats.

En résumé, la convention est la suivante:

\<Vendor Name>\(<Namespace>\)*<Class Name>

Dans la partie \<Vendor Name>\(<Namespace>\)*, les séparateurs de namespaces seront transformés en DIRECTORY_SEPARATOR. Pour la partie <Class Name>, les underscores seront transformés en DIRECTORY_SEPARATOR. Cette dernière partie est donc backward compatible avec la convention à la Horde / PEAR.

The standards we set here should be the lowest common denominator for painless autoloader interoperability.

Et voici l'implémentation proposée de SplClassLoader

J'affirme que non seulement ce standard est inutile, mais qu'en plus il pose plus de problèmes qu'il n'en résoud.

Larry Garfield de Drupal ou Robert Lemke de TYPO3/FLOW3, tous deux membres du Working Group, expriment leur inquiétudes quant à la possible adhésion de leur projet respectif au seul PSR-0 (comprendre, la faisabilité même). Voir leurs interventions ici et ici. Le Working Groupe s'est mis dans la tête de ne travailler qu'avec une seule implémentation d'autoloader. A partir de ce moment là, soit un projet est compatible avec la convention de nommage et la structure de fichiers, soit il ne l'est pas. Au delà d'une convention, c'est toute une architecture logicielle qui est impactée, comme le montrent Garfield et Lemke.

Afin de bien comprendre, et de rigoler un peu, prenons le cas de TYPO3. Chez eux, pour un composant donné, les fichiers de classes sont localisés dans un sous répertoire "Classes" mais ce dernier n'apparait bien évidemment pas explicitement dans le nom complètement qualifié:

class: \TYPO3\Fluid\Core\Parser
file : Packages/Framework/TYPO3/Fluid/Classes/Core/Parser.php

Fluid/
     Classes/
     Core/
         Parser.php
     Meta/
         Package.xml
     Documentation/
         Manual/
               en/
                 Index.xml    

TYPO3 doit donc être entièrement repensé et réécrit. Dommage pour eux. En revanche, Symfony, Doctrine ou ZF s'en sortent bien. Il faut dire que leur représentants sont influents. Par exemple, Jonathan H. Wage (Doctrine - Sensio) est à l'origine de SplClassLoader. Matthew Weier O'Phinney (ZF - Zend) a réussi à imposer la règle spéciale qui consiste à transformer les underscores dans les noms de class en DIRECTORY_SEPARATOR. Cela complexifie fortement l'implémentation de SplClassLoader, mais bon, il s'agit de Zend tout de même!

Ce qu'il faudrait faire

La seule chose importante pour une interopérabilité d'autoloading est l'unicité de la partie <Vendor Name> dans:

\<Vendor Name>\(<Namespace>\)*<Class Name>

Tout le reste n'est que contraintes inutiles. Si une librairie X veut mettre toutes ses classes dans un sous répertoire Classes, elle n'a qu'à fournir sa propre implémentation pour spl_autoload_register(). Dans un contexte de namespaces, l'unicité de <Vendor Name> suffit.

Au passage, les constantes __DIR__ et __NAMESPACE__ permettent potentiellement de se passer d'include_path lors des opérations d'inclusion de fichiers. Quoiqu'il en soit, les auteurs de la librairie X ont tout le loisir d'optimiser au maximum leur implémentation et se passer, par exemple, de la règles des underscores dans les noms de classes.

Mais quid d'une possible implémentation en C de SplClassLoader ? Et bien tant mieux! Rien n'empêche un projet d'adhérer à la lettre au PSR-0. Ce n'est aucunement incompatible avec la présence de plusieurs autoloader. SplClassLoader utilise déja spl_autoload_register.