2015-01-11 7 views
15

मैं यह पता लगाने की कोशिश कर रहा हूं कि PHP को स्मृति के लिए सरणी कैसे लोड करती है और जब कोई सरणी गुजरती है तो स्मृति का उपभोग करता है।PHP की सरणी मेमोरी उपयोग प्रबंधन कैसे काम करता है?

तो मैं कोड चल रहा है का यह छोटा सा मिल गया है: ध्यान दें कि इनपुट सरणी इस उदाहरण में कम महत्वपूर्ण है:

<?php 

echo $this->getMemoryUsage(); 
$arr = $query->result_array(); // array of arrays from codeigniter 
echo $this->getMemoryUsage(); 

यह स्मृति का बिल्कुल 250 KB की खपत, इसका मतलब है सरणी लगभग 250 है आकार में केबी, लगभग

तो मैं निम्नलिखित कोड भाग गया: मैं क्या पढ़ा के अनुसार

<?php 

echo $this->getMemoryUsage(); 
$arr = $query->result_array(); // array of arrays from codeigniter 

$arr[0]['id'] = 'changing this value'; 

$foo = $arr; 
$foo[2]['id'] = 'changing this value again'; 

$bar = $foo; 
$bar[4]['id'] = 'changing this value again and again'; 

$far = $bar; 
$far[5]['id'] = 'changing this value again and again and again'; 

echo $this->getMemoryUsage(); 

और बताया गया था, पीएचपी वास्तव में सरणी नकल नहीं है, यह केवल मूल सरणी, लेकिन का संदर्भ एक बार एक परिवर्तन है बनाया PHP को पूरे सरणी की प्रतिलिपि बनाना है।

मेरे आश्चर्य की कल्पना करें जब उपरोक्त कोड वास्तव में 500 केबी रैम का उपभोग करता है।

क्या कोई यह बता सकता है कि यहां क्या हो रहा है?

बस स्पष्ट होने के लिए, इन सभी इंडेक्स (0-5 और id) मूल सरणी में पहले से मौजूद हैं, मैं केवल मूल्य को संशोधित कर रहा हूं। मूल मान कुछ पूर्णांक है।

संपादित

बस $ इस- की भागीदारी स्पष्ट करने के लिए> परिणाम(); यहाँ एक और परीक्षण मैं आयोजित है या नहीं:

echo $this->getMemoryUsage(); 
    $arr = $query->result_array(); // array of arrays from codeigniter 
//$arr[0]['id'] = 'changing this value'; 

    $foo = $arr; 
    $foo[2]['id'] = 'changing this value again'; 

    //$bar = $foo; 
    //$bar[4]['id'] = 'changing this value again and again'; 
    // 
    //$far = $bar; 
    //$far[4]['id'] = 'changing this value again and again and again'; 

    echo $this->getMemoryUsage(); 

इस बार उत्पादन बिल्कुल 250 KB है - बस किसी भी परिवर्तन

संपादित करें # 2

के रूप में अनुरोध किया है, मैं बिना मूल परीक्षण की तरह बनाने के लिए मेरी सेटअप पर यहां से कोड दौड़ा लिया है, यकीन है कि परिणाम संगत कर रहे हैं: http://pastebin.com/cYNg4cg7

इन परिणामों हैं:

+०१२३५१६४१०६१

घोषणा: 4608 kB
अंतिम: 8904 kB
घोषणा करने के लिए DIFF: 4296 kB

तो भले ही घोषणा 4608 था और सरणी पारित कर दिया और 4 बार बदल दिया गया था, यह अभी भी केवल कम से कम स्मृति दोगुनी है पदचिह्न।

संपादित # 3

मैं प्रत्येक आवंटन के बाद स्मृति परिवर्तन भाग गया है:

घोषणा: 5144 kB
A0 आवंटन कहा: 144 kB
A1 आवंटन जोड़ा: 1768 kB
आवंटित ए 2 जोड़ा गया: 1768 केबी
आवंटित ए 3 जोड़ा गया: 1768 केबी
अंतिम: 10744 केबी
डीआईएफएफ से निपटने के लिए: 5600 केबी

पहली लागत के बाद प्रत्येक निम्नलिखित ऑपरेशन बिल्कुल वही है, जो संकेत मिलता है कि सटीक उसी आकार की प्रतिलिपि बनाई जा रही है। ऐसा लगता है कि ऑस्टिन के जवाब का समर्थन करता है, केवल एक चीज जो अब जोड़ती नहीं है वह आवंटित आकार है, लेकिन यह एक अलग सवाल है।

ऑस्टिन की गेंद पर लगता है, अगर कोई अन्य जवाब नहीं आता है तो मैं इसे स्वीकार करूंगा।

+0

बहुत कठिन प्रश्न है, तो आप निम्न आलेख में रुचि मैं कुछ दिन पहले पढ़ा हो सकता है: https: // nikic .github.io/2011/12/12/कैसे-बड़े-PHP-arrays-really-hint-BIG.html – Fleshgrinder

+0

मैंने कुछ हफ्ते पहले उस लेख को पढ़ लिया है, यह ईमानदारी से fasci है nating, लेकिन यह समझा नहीं है कि प्रतिलिपि कैसे काम करता है। – Patrick

+2

मुझे पता है, बस सोचा था कि आपको यह पसंद आएगा। मैं आपके प्रश्न का उत्तर नहीं दे सकता और मैं आपको एक लिंक नहीं दे सकता जो आपके प्रश्न का उत्तर दे। इसके बजाए मैंने आपके प्रश्न का अभिनय किया ताकि मैं इसका अनुसरण कर सकूं और अगर कोई उत्तर पोस्ट नहीं किया जाता है, तो मुझे यह बक्षीस मिलेगा, क्योंकि मुझे यह भी जानना अच्छा लगेगा। :) – Fleshgrinder

उत्तर

4

यहाँ पर क्या मुझे लगता है कि जा रहा है है:

पीएचपी सरणियों लिखने पर प्रतिलिपि कर रहे हैं के रूप में आप कहते हैं, लेकिन एक बहु-आयामी सरणी के प्रत्येक स्तर के लिए अलग से लिखने पर कॉपी कर रहा है। PHP एक बहु-आयामी सरणी के हिस्सों का पुन: उपयोग करने के बारे में बहुत स्मार्ट है और न केवल पूरी चीज। (यह कुछ फाइल सिस्टम है कि स्नैपशॉट का समर्थन, ZFS तरह के समान है।)

उदाहरण: कहते हैं कि हम इस सरणी

$x = array('foo' => array(1, 2, 3), 'bar' => array(4, 5, 6)); 

यह एक एकल हिस्सा के रूप में नहीं स्मृति में संग्रहीत किया जाता है, लेकिन अलग हिस्सा यहाँ के रूप में A लेबल, B, C, और $x:

:

array(1, 2, 3) //A 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
{pointer to C} //$x 

अब $x की एक प्रतिलिपि बनाने की सुविधा देता है

$y = $x; 

यह बहुत थोड़ा अतिरिक्त मेमोरी का उपयोग करता है क्योंकि C के लिए एक और सूचक बनाने है सब यह करने के लिए दिया गया है:

$y['foo'][0] = 10; 

यहाँ क्या ऐसा नहीं होता है:

array(1, 2, 3) //A 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
{pointer to C} //$x 
{pointer to C} //$y 

अब परिवर्तन $y की सुविधा देता है :

array(1, 2, 3) //A 
array(10, 2, 3) //A2 
array(4, 5, 6) //B 
array(4, 5, 6) //B2 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
array('foo' => {pointer to A2}, 'bar' => {pointer to B2}) //C2 
{pointer to C} //$x 
{pointer to C2} //$y 

ध्यान दें कि B और B2 समान हैं। वहाँ तो क्या वास्तव में होता यह है, दो बार एक ही बात रखने के लिए कोई ज़रूरत नहीं है:

array(1, 2, 3) //A 
array(10, 2, 3) //A2 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
array('foo' => {pointer to A2}, 'bar' => {pointer to B}) //C2 
{pointer to C} //$x 
{pointer to C2} //$y 

इस सरल मामले में, लाभ बहुत छोटा है, लेकिन कल्पना कीजिए कि तीन नंबर के बजाय, 'bar' सरणी संख्या पर हज़ारों । आप बड़ी मात्रा में स्मृति को बचाते हैं।

अपने मूल कोड से इसे संबंधित करने से, न केवल शुरुआत और अंत में स्मृति उपयोग को प्रिंट करने का प्रयास करें, बल्कि प्रत्येक नए सरणी असाइनमेंट के बाद।आप देखेंगे कि स्मृति चरण प्रत्येक चरण के बाद मूल सरणी के केवल एक अंश से बढ़ता है। ऐसा इसलिए है क्योंकि सरणी का केवल एक हिस्सा कॉपी किया जा रहा है, पूरी बात नहीं। विशेष रूप से, पहली-स्तरीय सरणी और आपके द्वारा परिवर्तित विशिष्ट उप सरणी की प्रतिलिपि बनाई जाती है, लेकिन अन्य उप सरणी कॉपी नहीं होती हैं।

तथ्य यह है कि उपयोग की गई स्मृति की अंतिम मात्रा दोगुनी है जितनी शुरुआती राशि आपके कोड के विशेष सेटअप और आपके द्वारा बनाए गए सरणी की प्रतियों की संख्या के कारण एक संयोग प्रतीत होती है।

(वास्तविकता में, पीएचपी मैं यहाँ क्या वर्णन से भी बेहतर कर सकते हैं (यह शायद 'foo' और 'bar', आदि) की केवल एक प्रतिलिपि रखना होगा, लेकिन सबसे अधिक भाग के लिए यह चाल का एक ही प्रकार के लिए निर्भर करता है।)

आप इस का एक और अधिक नाटकीय प्रदर्शन चाहते हैं, कुछ इस तरह करते हैं:

$base = memory_get_usage(); 
$x = array('small' => array('this is small'), 'big' => array()); 
for ($i = 0; $i < 1000000; $i++) { 
    $x['big'][] = $i; 
} 
echo (memory_get_usage() - $base).PHP_EOL; //a lot of memory 
$y = $x; 
$y['small'][0] = 'now a bit bigger'; 
echo (memory_get_usage() - $base).PHP_EOL; //a bit more memory 
$z = $x; 
$z['big'][0] = 2; 
echo (memory_get_usage() - $base).PHP_EOL; //a LOT more memory 
+0

अरे, मैं इन पंक्तियों के साथ कुछ सोच रहा था, हालांकि यह एक और टेस्ट केस का उपयोग नहीं कर रहा है, यह पहले से ही दो समान कार्यों के बाद मेमोरी आवंटन युगल पुन: उत्पन्न हुआ था (प्रश्न में अंतिम संपादन देखें)। 4 उसी आकार के सरणी पर समान क्रियाओं को रैखिक प्रभाव होना चाहिए। यदि हम 4 बार एक ही कार्य करते हैं, तो एक बार लागत का 1/4 वां खर्च करना चाहिए। ध्यान दें कि परीक्षणों में मैं अलग-अलग सूचकांक बदलता रहता हूं, अगर हम एक की प्रतिलिपि बनाते हैं, तो हम सभी को कॉपी करते हैं। जैसे ही मैं कर सकता हूं, मैं नए प्रश्न मानों के साथ अपना प्रश्न संपादित करूंगा। – Patrick

+2

@ पैट्रिक पहली क्रिया का खर्च उतना ही नहीं है, क्योंकि आपको पुराने मान को रखने की आवश्यकता नहीं है क्योंकि कोई भी चर इसका उपयोग नहीं करता है। चौथे कार्यों के माध्यम से दूसरे को एक प्रतिलिपि बनाना है क्योंकि मूल अभी भी उपयोग में है।इसे देखने के लिए प्रत्येक असाइनमेंट के बाद मेमोरी उपयोग प्रिंट करें। – Austin

+0

आपके उत्तर का परीक्षण करने के बाद वास्तव में सही है, शानदार! :) – Patrick

संबंधित मुद्दे