pátek 22. března 2013

PHP - rozdělení kódu do více souborů vs. jeden velký soubor (agregace kódu)

Zajímalo mě, zda je v PHP výhodné mít soubory rozdělené do více malých souborů (jde hlavně o třídy) nebo jestli se vyplatí udělat agregaci nejčastěji používaných tříd do "balíčků", čímž myslím jeden velký soubor PHP.

Hlavní rozdíl je samozřejmě v paměti, pokud PHP načítá třídy přes autoload jen aktuálně potřebné třídy, obsazená pomět bude vždy menší, než když načteme všechny třídy najednou. Ale předpokládejme, že pamět nás netrápí anebo, že naše aplikace načítá beztak skoro všechno. Jak s si tím tedy PHP poradí ? Hmm.

Testovací prostředí
  • centos, php-fpm 5.4.13, apache prefork, APC  3.1.14
Co testuji
vzorek 1: 50 php tříd o velikosti 10KB, tedy 50 souborů celkem 500KB
vzorek 2: 1 php soubor, spojení všech tříd přes "cat", tedy 1 soubor o velikosti 500KB,

Závěr z měření:

  • bez APC parsování tříd zabere více času - 80x více ??  ale jo klidně :) 
  • bez APC PHP scripty zaberou znatelně více paměti (v mém testu je úspora s APC přes 500%!)
    opcode je sice uložen ve sdílené paměti, ale tato paměť se zabere 1x, nikoliv při každém zpracování php scriptu (requestu)
  • nemá cenu se za každou cenu snažit sjednocovat scripty do jednoho, úspora je minimální, sice to vypadá procentuelně jako hodně velké zrychlení (100% )
  • autoload nabídne mnohem větší flexibilitu,  nepožere tolik paměti, opět za předpokladu, že naše aplikace nepotřebuje všechny třídy/includes
  • agregaci do jednoho souboru bych volil v případě, kdy víme, že aplikace bude potřebovat pro každý request více jak 50% komponent anebo když máme pomalý disk a chceme ušetřit IO (stat souboru) při includování, nebo je pomalý autoloader (to je jiná věc)
  • zajímavý postup může být agregování  globálně sdílených komponent do jednoho souboru, může jít často řekněme o 20-30 souborů (tříd či konfigurační soubory), tohle řešení je ale třeba potom ošetřit v situaci, kdy se komponenty mění, v SVN by to šlo přes nějaký hook. Celková úspora bude ale beztak  v jednotkách ms, což za případné problémy s aktualizací nemusí stát.


A výsledek měření ...

vzorek 1 (50 kids):

1. run (ještě není v APC cache)
took 115.624ms
memory_get_usage:      4 194 304

každý další run, průměrné časy  - APC již pracuje
(v APC opcode zabere všech 50 souborů celkem 3 301 600 bytes)

took 2.5239ms
memory_get_usage:      786 432

vzorek 2 (1 big daddy):

1. run (ještě není v APC cache)
took 87.9078ms
memory_get_usage:      4 194 304

každý  další run s APC - průměrné časy 
(v APC opcode zabere 3 170 576 bytes)

took 1.2119ms
memory_get_usage:      786 432