Bagaimana cara menentukan footprint memori (ukuran) suatu variabel?


102

Apakah ada fungsi dalam PHP (atau ekstensi PHP) untuk mengetahui berapa banyak memori yang digunakan variabel tertentu? sizeofhanya memberi tahu saya jumlah elemen / properti.

memory_get_usagemembantu karena memberi saya ukuran memori yang digunakan oleh seluruh skrip. Apakah ada cara untuk melakukan ini untuk satu variabel?

Perhatikan bahwa ini ada di mesin pengembangan, jadi memuat ekstensi atau alat debug dapat dilakukan.


Diedit - 5 tahun kemudian, dan beberapa masalah masih agak belum terpecahkan :(
Piskvor meninggalkan gedung

Jawaban:


46

Anda Mungkin membutuhkan Memory Profiler. Saya telah mengumpulkan informasi dari SO tetapi saya telah menyalin beberapa hal penting yang dapat membantu Anda juga.

Seperti yang mungkin Anda ketahui, Xdebug menghentikan dukungan profil memori sejak versi 2. *. Silakan cari string "fungsi yang dihapus" di sini: http://www.xdebug.org/updates.php

Fungsi yang dihapus

Menghapus dukungan untuk Profil memori karena tidak berfungsi dengan benar.

Opsi Profiler Lainnya

php-memory-profiler

https://github.com/arnaud-lb/php-memory-profiler . Inilah yang saya lakukan di server Ubuntu saya untuk mengaktifkannya:

sudo apt-get install libjudy-dev libjudydebian1
sudo pecl install memprof
echo "extension=memprof.so" > /etc/php5/mods-available/memprof.ini
sudo php5enmod memprof
service apache2 restart

Dan kemudian di kode saya:

<?php
memprof_enable();
// do your stuff
memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));

Terakhir buka callgrind.outfile dengan KCachegrind

Menggunakan Google gperftools (disarankan!)

Pertama-tama instal Google gperftools dengan mengunduh paket terbaru di sini: https://code.google.com/p/gperftools/

Kemudian seperti biasa:

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install

Sekarang dalam kode Anda:

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));

Kemudian buka terminal Anda dan luncurkan:

pprof --web /tmp/profile.heap

pprof akan membuat jendela baru di sesi browser Anda yang sudah ada dengan sesuatu seperti yang ditunjukkan di bawah ini:

Profil memori PHP dengan memprof dan gperftools

Xhprof + Xhgui (menurut saya yang terbaik untuk profil cpu dan memori)

Dengan Xhprof dan Xhgui Anda dapat membuat profil penggunaan cpu juga atau hanya penggunaan memori jika itu masalah Anda saat ini. Ini adalah solusi yang sangat lengkap, ini memberi Anda kendali penuh dan log dapat ditulis baik di mongo atau di sistem file.

Untuk lebih jelasnya lihat disini .

Api hitam

Blackfire adalah profiler PHP oleh SensioLabs, orang-orang Symfony2 https://blackfire.io/

Jika Anda menggunakan boneka untuk mengatur mesin virtual Anda, Anda akan senang mengetahui bahwa itu didukung ;-)

Xdebug dan melacak penggunaan memori

XDEBUG2 adalah ekstensi untuk PHP. Xdebug memungkinkan Anda untuk mencatat semua panggilan fungsi, termasuk parameter dan mengembalikan nilai ke file dalam format yang berbeda. Ada tiga format keluaran. Satu dimaksudkan sebagai jejak yang dapat dibaca manusia, yang lain lebih cocok untuk program komputer karena lebih mudah untuk diurai, dan yang terakhir menggunakan HTML untuk memformat jejak. Anda dapat beralih di antara dua format berbeda dengan pengaturan. Contoh akan tersedia di sini

forp

forp sederhana, tidak mengganggu, berorientasi pada produksi, profiler PHP. Beberapa fiturnya adalah:

  • pengukuran waktu dan memori yang dialokasikan untuk setiap fungsi

  • penggunaan CPU

  • file dan nomor baris pemanggilan fungsi

  • keluaran sebagai format Peristiwa Jejak Google

  • keterangan fungsi

  • pengelompokan fungsi

  • alias fungsi (berguna untuk fungsi anonim)

DBG

DBG adalah debugger php berfitur lengkap, alat interaktif yang membantu Anda men-debug skrip php. Ini bekerja pada server WEB produksi dan / atau pengembangan dan memungkinkan Anda men-debug skrip Anda secara lokal atau jarak jauh, dari IDE atau konsol dan fitur-fiturnya adalah:

  • Debugging jarak jauh dan lokal

  • Aktivasi eksplisit dan implisit

  • Tumpukan panggilan, termasuk panggilan fungsi, panggilan metode dinamis dan statis, dengan parameternya

  • Menavigasi melalui tumpukan panggilan dengan kemampuan untuk mengevaluasi variabel di tempat (bertingkat) yang sesuai

  • Step in / Step out / Step over / Run to cursor fungsionalitas

  • Breakpoint bersyarat

  • Breakpoint global

  • Logging untuk kesalahan dan peringatan

  • Beberapa sesi simultan untuk debugging paralel

  • Dukungan untuk GUI dan CLI front-end

  • Mendukung jaringan IPv6 dan IPv4

  • Semua data yang ditransfer oleh debugger dapat secara opsional dilindungi dengan SSL


2
Itu persis informasi yang saya cari, terima kasih.
Piskvor meninggalkan gedung

93

Tidak ada cara langsung untuk mendapatkan penggunaan memori variabel tunggal, tetapi seperti yang disarankan Gordon, Anda dapat menggunakannya memory_get_usage. Itu akan mengembalikan jumlah total memori yang dialokasikan, sehingga Anda dapat menggunakan solusi dan mengukur penggunaan sebelum dan sesudah untuk mendapatkan penggunaan variabel tunggal. Ini agak hacky, tetapi seharusnya berhasil.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

Perhatikan bahwa ini sama sekali bukan metode yang dapat diandalkan, Anda tidak dapat memastikan bahwa tidak ada yang menyentuh memori saat menetapkan variabel, jadi ini hanya boleh digunakan sebagai perkiraan.

Anda sebenarnya dapat mengubahnya menjadi fungsi dengan membuat salinan variabel di dalam fungsi dan mengukur memori yang digunakan. Belum menguji ini, tetapi pada prinsipnya, saya tidak melihat ada yang salah dengannya:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}

14
$tmp = $varakan membuat salinan dangkal. Ini tidak akan mengalokasikan lebih banyak memori sampai $ tmp diubah.
Gordon

@ Gordon, Anda benar, saya agak mengabaikan hal itu. Karena saya tidak dapat menemukan cara yang tepat untuk memodifikasi variabel tanpa mengubah jenis atau ukurannya, saya biarkan saja. Mungkin seseorang bisa mendapatkan ide yang tepat :)
Tatu Ulmanen

7
bagaimana dengan $tmp = unserialize(serialize($var)); Ini akan menggabungkan pendekatan Aistina di atas.
Gordon

3
juga, karena $varsudah menjadi salinan dangkal atau referensi dari apa yang diteruskan ke fungsi, Anda tidak perlu $tmp, tetapi dapat menetapkan ulang $var. Ini menyimpan referensi internal dari $tmpke $var.
Gordon

Apakah tidak ada cara yang lebih elegan untuk dereference $tmpdari $var?
Tomáš Zato - Kembalikan Monica

24

Tidak, tidak ada. Tapi Anda bisa serialize($var)dan memeriksa strlenhasilnya untuk perkiraan.


Ini adalah pendekatan yang jauh lebih baik, karena menghindari hal GC secara keseluruhan.
Gleno

12
Ini perkiraan yang buruk. Setiap item dalam array di PHP adalah ~ 80 byte, namun strlen(serialize(array(1,2,3)))tetap 30.
gsnedders

2
@Aistina, -1. Anda mengukur hal yang salah. Variabel dan variabel serial adalah dua hal yang sangat berbeda, dan akan memberikan hasil yang sangat berbeda.
Pacerier

1
Tidak hanya itu, tetapi akan gagal sepenuhnya pada struktur data non-serializable tertentu, misalnya referensi melingkar.
senja-tidak aktif-

20

Sebagai jawaban atas jawaban Tatu Ulmanens:

Perlu dicatat, itu $start_memorysendiri akan memakan memori ( PHP_INT_SIZE * 8).

Jadi keseluruhan fungsinya harus menjadi:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $var = unserialize(serialize($var));
    return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8;
}

Maaf menambahkan ini sebagai jawaban tambahan, tapi saya belum bisa mengomentari jawaban.

Pembaruan: * 8 tidak pasti. Itu bisa tergantung pada versi php dan mungkin pada 64/32 bit.


4
Bisakah Anda menjelaskan mengapa * 8? Terima kasih!
sierrasdetandil

@sierrasdetandil Tampaknya $ start_memory tidak hanya menggunakan PHP_INT_SIZEbyte, tetapi PHP_INT_SIZE*8. Anda dapat mencobanya dengan memanggil fungsi ini, itu harus mengembalikan 0:function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para

8tampaknya tidak konstan. Mengikuti fungsi komentar Anda di sistem dev saya (PHP 5.6.19), ia kembali -16. Juga, yang menarik, dari php -a, memanggil dua baris fungsi memberikan berbagai nilai yang berbeda.
Paul DelRe

@PaulDelRe ya, mungkin tergantung pada versi / 64bit hal semacam ini.
para

sekarang kesalahan fatal terjadi pada panggilan unserialize (). Itu tidak membantu! Jika sebuah variabel sangat besar sehingga kehabisan memori, memanggil fungsi pada variabel itu akan menggunakan lebih banyak memori. :(
john ktejik

4

Lihat:

Perhatikan bahwa ini tidak akan memberi Anda penggunaan memori dari variabel tertentu. Tetapi Anda dapat melakukan panggilan ke fungsi ini sebelum dan sesudah menetapkan variabel dan kemudian membandingkan nilainya. Itu akan memberi Anda gambaran tentang memori yang digunakan.

Anda juga dapat melihat Memtrack ekstensi PECL , meskipun dokumentasinya agak kurang, jika tidak bisa dikatakan, hampir tidak ada.


Iya. Ini dapat digunakan secara tidak langsung untuk menjawab pertanyaan.
Notinlist

3

Anda dapat memilih untuk menghitung perbedaan memori pada nilai balik panggilan balik. Ini adalah solusi yang lebih elegan yang tersedia dalam PHP 5.3+.

function calculateFootprint($callback) {
    $startMemory = memory_get_usage();
    $result = call_user_func($callback);
    return memory_get_usage() - $startMemory;
}

$memoryFootprint = calculateFootprint(
    function() {
        return range(1, 1000000);
    }
);

echo ($memoryFootprint / (1024 * 1024)) . ' MB' . PHP_EOL;

3

Anda tidak dapat secara retrospektif menghitung footprint variabel yang tepat karena dua variabel dapat berbagi ruang yang dialokasikan sama di memori

Mari kita coba membagi memori antara dua larik, kita melihat bahwa mengalokasikan larik kedua menghabiskan setengah dari memori larik pertama. Saat kami membatalkan setelan yang pertama, hampir semua memori masih digunakan oleh yang kedua.

echo memory_get_usage()."\n"; // <-- 433200
$c=range(1,100);
echo memory_get_usage()."\n"; // <-- 444348 (+11148)
$d=array_slice($c, 1);
echo memory_get_usage()."\n"; // <-- 451040 (+6692)
unset($c);
echo memory_get_usage()."\n"; // <-- 444232 (-6808)
unset($d);
echo memory_get_usage()."\n"; // <-- 433200 (-11032)

Jadi kita tidak bisa menyimpulkan daripada array kedua menggunakan setengah memori, karena menjadi salah ketika kita membatalkan yang pertama.

Untuk gambaran lengkap tentang bagaimana memori dialokasikan dalam PHP dan untuk kegunaan apa, saya sarankan Anda untuk membaca artikel berikut: Seberapa besar array (dan nilai) PHP sebenarnya? (Petunjuk: BESAR!)

The Dasar Referensi Counting dalam dokumentasi PHP juga memiliki banyak informasi tentang penggunaan memori, dan referensi menghitung sampai segmen data bersama.

Solusi berbeda yang diekspos di sini bagus untuk perkiraan tetapi tidak ada yang dapat menangani manajemen halus memori PHP.

  1. menghitung ruang yang baru dialokasikan

Jika Anda menginginkan ruang yang baru dialokasikan setelah tugas, maka Anda harus menggunakan memory_get_usage()sebelum dan sesudah alokasi, karena menggunakannya dengan salinan memberi Anda pandangan yang salah tentang kenyataan.

// open output buffer
echo "Result: ";
// call every function once
range(1,1); memory_get_usage();

echo memory_get_usage()."\n";
$c=range(1,100);
echo memory_get_usage()."\n";

Ingatlah bahwa jika Anda ingin menyimpan hasil pertama memory_get_usage(), variabel harus sudah ada sebelumnya, dan memory_get_usage()harus dipanggil lain waktu sebelumnya, dan setiap fungsi lainnya juga.

Jika Anda ingin menggemakan seperti pada contoh di atas, buffer keluaran Anda harus sudah dibuka untuk menghindari memori akuntansi yang diperlukan untuk membuka buffer keluaran.

  1. menghitung ruang yang dibutuhkan

Jika Anda ingin mengandalkan fungsi untuk menghitung ruang yang diperlukan untuk menyimpan salinan variabel, kode berikut menangani pengoptimalan yang berbeda:

<?php
function getMemorySize($value) {
    // existing variable with integer value so that the next line
    // does not add memory consumption when initiating $start variable
    $start=1;
    $start=memory_get_usage();
    // json functions return less bytes consumptions than serialize
    $tmp=json_decode(json_encode($value));
    return memory_get_usage() - $start;
}

// open the output buffer, and calls the function one first time
echo ".\n";
getMemorySize(NULL);

// test inside a function in order to not care about memory used
// by the addition of the variable name to the $_GLOBAL array
function test() {
    // call the function name once 
    range(1,1);

    // we will compare the two values (see comment above about initialization of $start)
    $start=1;
    $start=memory_get_usage();
    $c=range(1,100);
    echo memory_get_usage()-$start."\n";
    echo getMemorySize($c)."\n";
}
test();

// same result, this works fine.
// 11044
// 11044

Perhatikan bahwa ukuran nama variabel penting dalam memori yang dialokasikan.

  1. Periksa kode Anda !!

Variabel memiliki ukuran dasar yang ditentukan oleh struktur C bagian dalam yang digunakan dalam kode sumber PHP. Ukuran ini tidak berfluktuasi dalam hal angka. Untuk senar, itu akan menambah panjang senar.

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

Jika kita tidak memperhitungkan inisialisasi nama variabel, kita sudah tahu berapa banyak variabel yang digunakan (dalam hal angka dan string):

44 byte dalam kasus angka

+ 24 byte untuk string

+ panjang string (termasuk karakter NUL terakhir)

(angka-angka itu bisa berubah tergantung pada versi PHP)

Anda harus membulatkan hingga kelipatan 4 byte karena penyelarasan memori. Jika variabel berada di ruang global (bukan di dalam fungsi), itu juga akan mengalokasikan 64 byte lagi.

Jadi jika Anda ingin menggunakan salah satu kode di dalam halaman ini, Anda harus memeriksa apakah hasilnya menggunakan beberapa kasus uji sederhana (string atau angka) cocok dengan data tersebut dengan mempertimbangkan setiap indikasi dalam posting ini (array $ _GLOBAL, panggilan fungsi pertama, buffer keluaran, ...)


1
... dan itu bahkan sebelum kita masuk ke internal zvalue, is_refdan copy-on-write. Terima kasih.
Piskvor meninggalkan gedung

1
Terima kasih kepada Anda, saya melewatkan halaman itu di Manual PHP. Saya menambahkan tautan untuk melengkapi jawaban saya (tapi saya rasa Anda sudah membacanya).
Adam

2

Saya memiliki masalah serupa, dan solusi yang saya gunakan adalah menulis variabel ke file kemudian menjalankan filesize () di atasnya. Secara kasar seperti ini (kode yang belum diuji):

function getVariableSize ( $foo ) 
{
    $tmpfile = "temp-" . microtime(true) . ".txt";
    file_put_contents($tmpfile, $foo);
    $size = filesize($tmpfile);
    unlink($tmpfile);
    return $size;
}

Solusi ini tidak terlalu cepat karena melibatkan IO disk, tetapi akan memberi Anda sesuatu yang jauh lebih tepat daripada trik memory_get_usage. Itu hanya tergantung pada seberapa banyak presisi yang Anda butuhkan.


Perlu dicatat bahwa solusi ini hanya berfungsi untuk string dan array dimensi tunggal dari string dan penggunaannya strlenakan lebih mudah.
Adam


1
function mesure($var){
    $start = memory_get_usage();
    if(is_string($var)){
        $newValue = $var . '';
    }elseif(is_numeric($var)){
        $newValue = $var + 0;
    }elseif(is_object($var)){
        $newValue = clone $var;
    }elseif(is_array($var)){
        $newValue = array_flip($var, []);
    }
    return memory_get_usage() - $start;
}

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.