Wydajność UNSET vs. NULL

Przeprowadziłem małe testy dwóch sposobów zwalniania zasobów i czasu który zajmuje taki proces. Czytałem na kilku portalach czy powinno się unsetować zmienne czy nullować. Wiele komentarzy polegało na sprzeczaniu się która operacja trwa dużej i jest bardziej pamięciożerna. Nie będę oceniał co jest bardzie przydatne czy w jaki sposób powinno się to robić. Wszystko zależy od tego w jakiej sytuacji się znajdujemy i które zasoby są dla nas kluczowe.

Podczas zmieniania wartości zmiennej $foo = null; uzyskasz efekt szybszego zwolnienia pamięci RAM, ale zabierze to kilka cykli procesora, co może znacząco wydłużyć czas realizacji zadania. Funkcja unset() natomiast nie zwolni pamięci RAM natychmiastowo, jednak nie obciąży on w żaden sposób procesora. W celu zobrazowania problemu przygotowałem prosty skrypt z jedną pętlą. Test polega na szybkim tworzeniu i zwalnianiu zmiennej tekstowej oraz na ciągłym dopisywaniu do istniejącej zmiennej kolejnych linii tekstu. Każdy test da nam 2 wyniki zajętości pamięci.

Skrypt który ustawia wartości na NULL.

<?php
echo memory_get_usage() . "\n";
function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

$time_start = microtime_float();

echo memory_get_usage() . "\n";
$b = str_repeat("Hello", 100);
echo memory_get_usage() . "\n";
echo "===================================================================\n";
$i = 0;
while ($i < 1000) {
    echo memory_get_usage() . "\n";
    $a = str_repeat("Hello", 999999);
    echo memory_get_usage() . "\n";
    $b .= str_repeat("Hello", 9999);
    echo memory_get_usage() . "\n";
    $i++;
    echo memory_get_usage() . "\n";
    $a = null;
    echo memory_get_usage() . "\n";
}
echo "===================================================================\n";
echo memory_get_usage() . "\n\r";
$b  = null;
echo memory_get_usage() . "\n";
$time_end = microtime_float();
echo memory_get_usage() . "\n";
$time = $time_end - $time_start;
echo memory_get_usage() . "\n";
echo "time: " . $time;
echo memory_get_peak_usage(true) . "\n";

Skrypt który unsetuje wartości.

<?php
echo memory_get_usage() . "\n";
function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

$time_start = microtime_float();

echo memory_get_usage() . "\n";
$b = str_repeat("Hello", 100);
echo memory_get_usage() . "\n";
echo "===================================================================\n";
$i = 0;
while ($i < 1000) {
    echo memory_get_usage() . "\n";
    $a = str_repeat("Hello", 999999);
    echo memory_get_usage() . "\n";
    $b .= str_repeat("Hello", 9999);
    echo memory_get_usage() . "\n";
    $i++;
    echo memory_get_usage() . "\n";
    unset($a);
    echo memory_get_usage() . "\n";
}
echo "===================================================================\n";
echo memory_get_usage() . "\n\r";
unset($b);
echo memory_get_usage() . "\n";
$time_end = microtime_float();
echo memory_get_usage() . "\n";
$time = $time_end - $time_start;
echo memory_get_usage() . "\n";
echo "time: " . $time;
echo memory_get_peak_usage(true) . "\n";

Kod uruchamiałem z poziomu konsoli a wyniki zapisywałem do plików CSV w celu późniejszej analizy komendą:

php memory.php > memoryNULL.csv
php memory2.php > memoryUNSET.csv

Obraz pokazuje zużycie pamięci RAM w czasie. Jak widać różnice nie są widoczne „gołym okiem”. Dla wskazanej próbki wykresy zwalniania zasobów nakładają się na siebie.

RAMRezerwacja ogólnie wykorzystanej pamięci (PHP memory limit) jest dla obu testów jednakowe i wyniosło 55574528 bajtów (55.574528 mb).

Znacząco różnił się jednak czas wykonywania obliczeń. Dla techniki $foo = null; wyniósł on aż 24.9018 natomiast na unset() 18.2118. Jest to różnica ponad 6 sec.

Kasztelan Paweł

Programista samouk, zakochany w ZF i Laravel, szerzący opinię że PHP + JS + HTML + CSS to są języki w których może zostać stworzona aplikacja równie dobra, a nawet lepsza od twardego klienta.