क्या तेज है: in_array या isset? [बन्द है]


96

यह प्रश्न केवल मेरे लिए है क्योंकि मैं हमेशा अनुकूलित कोड लिखना पसंद करता हूं जो सस्ते धीमे सर्वर (या ट्रैफ़िक के बहुत से सर्वर) पर भी चल सकता है

मैंने चारों ओर देखा और मुझे कोई उत्तर नहीं मिला। मैं सोच रहा था कि उन दो उदाहरणों के बीच क्या तेजी है, यह ध्यान में रखते हुए कि मेरे मामले में सरणी की चाबियाँ महत्वपूर्ण नहीं हैं (छद्म कोड स्वाभाविक रूप से:

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!in_array($new_val, $a){
        $a[] = $new_val;
        //do other stuff
    }
}
?>

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!isset($a[$new_val]){
        $a[$new_val] = true;
        //do other stuff
    }
}
?>

जैसा कि प्रश्न का बिंदु सरणी टकराव नहीं है, मैं यह जोड़ना चाहूंगा कि यदि आप आवेषण के टकराने से डरते हैं $a[$new_value], तो आप उपयोग कर सकते हैं $a[md5($new_value)]। यह अभी भी टकराव का कारण बन सकता है, लेकिन उपयोगकर्ता द्वारा प्रदान की गई फ़ाइल ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html ) से पढ़ते समय एक संभावित DoS हमले से दूर ले जाएगा


3
यदि आप हमेशा अनुकूलित कोड लिखने का प्रयास कर रहे हैं, तो आप निश्चित रूप से एक समय में एक बार एक प्रोफाइलर का उपयोग कर रहे हैं?
मारियो

59
मैं फिर से वोट देने के लिए। प्रश्न अच्छी तरह से बना है और उत्तर तथ्यों और संदर्भों के साथ समर्थित हैं। एक माइक्रो- अपनाने के दौरान, इस प्रकार के प्रश्न रचनात्मक होते हैं ।
जेसन मैककेरी

5
@JasonMcCreary दूसरा; बस एक और।
जाक

7
यह कई साल बाद है, लेकिन मैं इसे माइक्रो ऑप्टिमाइज़ेशन भी नहीं मानूंगा। बड़े डेटा सेट के लिए यह एक टन का अंतर बना सकता है !!
राबर्ट

2
... यह प्रश्न मुझे "रचनात्मक" लगता है। मैं एक और फिर से अभियान शुरू करूंगा।
मिकमैकुसा

जवाबों:


117

अब तक के जवाब हाजिर हैं। issetइस मामले में उपयोग तेज है क्योंकि

  • यह कुंजी पर एक O (1) हैश खोज का उपयोग करता है, जबकि in_arrayप्रत्येक मूल्य की जांच तब तक करनी चाहिए जब तक कि यह एक मैच न मिल जाए।
  • एक opcode होने के नाते, इसमें in_arrayअंतर्निहित फ़ंक्शन को कॉल करने की तुलना में कम ओवरहेड है ।

मूल्यों के साथ एक सरणी का उपयोग करके इनका प्रदर्शन किया जा सकता है (नीचे परीक्षण में 10,000), in_arrayअधिक खोज करने के लिए मजबूर करना।

isset:    0.009623
in_array: 1.738441

यह जेसन के बेंचमार्क को कुछ यादृच्छिक मूल्यों में भरकर और कभी-कभी ऐसे मान का पता लगाता है जो सरणी में मौजूद है। सभी यादृच्छिक, इसलिए उस समय से सावधान रहें।

$a = array();
for ($i = 0; $i < 10000; ++$i) {
    $v = rand(1, 1000000);
    $a[$v] = $v;
}
echo "Size: ", count($a), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a[rand(1, 1000000)]);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array(rand(1, 1000000), $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

मैं हैश के बारे में जानता हूं, लेकिन सोचता हूं कि जब फ़ंक्शन को गति देने के लिए सरणियों के मूल्यों पर कुछ ऐसा ही नहीं किया जाता है, तो यह मेमोरी कंसोल को भी कम कर देगा यदि समान मूल्यों को केवल मूल्य पर एक अतिरिक्त हैशिंग जोड़कर किया जाता है .. सही है?
फेब्रीज़ियो

3
@ फैब्रिज़ियो - सरणी मानों को डुप्लिकेट किया जा सकता है और इसमें गैर-धोने योग्य ऑब्जेक्ट शामिल होते हैं। कुंजी अद्वितीय होनी चाहिए और केवल तार और पूर्णांक हो सकते हैं जो उन्हें आसानी से धो सकते हैं। जब आप एक-से-एक मानचित्र बना सकते हैं, जिसमें कुंजियाँ और मान दोनों होते हैं, तो यह नहीं है कि PHP की सरणी कैसे काम करती है।
डेविड हार्कस

3
यदि आप सुनिश्चित हैं कि आप सरणी में अद्वितीय मान रखते हैं तो एक और विकल्प है - फ्लिप + आईसेट
अर्कादिज कुजेल

एक फ़्लिप किए गए इस्सेट को ध्यान में रखते हुए इस उदाहरण में अभी भी in_array की तुलना में तेज़ है: `` $ start = microtime (सच); $ फू = array_flip ($ a); for ($ i = 0; $ i <10000; ++ $ i) {isset ($ फू [रैंड (1, 1000000)]); } $ total_time = microtime (सच) - $ शुरुआत; इको "कुल समय (फ़्लिप इस्सेट):", नंबर_फॉर्मैट ($ total_time, 6), PHP_EOL;
आंद्रे बॉमियर

@AndreBaumeier जो तेज है वह सरणी के आकार पर निर्भर करेगा और आप कितने परीक्षण करेंगे। तीन परीक्षण करने के लिए एक दस हजार तत्व सरणी को फ़्लिप करना संभवतः कुशल नहीं है।
डेविड हरकनेस

42

जो तेज है: isset()बनामin_array()

isset() ज्यादा तेज़ है।

जबकि यह स्पष्ट होना चाहिए, isset()केवल एक ही मूल्य का परीक्षण करता है। जबकि in_array()प्रत्येक तत्व के मूल्य का परीक्षण करते हुए, संपूर्ण सरणी पर पुनरावृति होगी।

रफ बेंचमार्किंग का उपयोग करना काफी आसान है microtime()

परिणाम:

Total time isset():    0.002857
Total time in_array(): 0.017103

नोट: परिणाम समान थे भले ही अस्तित्व में हो या नहीं।

कोड:

<?php
$a = array();
$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a['key']);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array('key', $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

exit;

अतिरिक्त संसाधन

मैं आपको यह देखने के लिए प्रोत्साहित करूंगा:


अच्छा समाधान है। मुझे आश्चर्य है कि अधिक लोग अपने कार्यों / कोड को अधिक उपयोग microtime()या अन्य साधनों से विभाजित नहीं करते हैं । अविश्वसनीय रूप से मूल्यवान।
निकाह

1
एक ही कुंजी के लिए एक खाली सरणी खोजना केवल in_arrayफ़ंक्शन को कॉल करने के ओवरहेड को हाइलाइट करता है बनाम issetअंतर्निहित का उपयोग करके । यह एक सरणी के साथ बेहतर होगा जिसमें यादृच्छिक कुंजी का एक गुच्छा होगा और कभी-कभी मौजूदा कुंजी / मान के लिए खोज की जाएगी।
डेविड हार्कस

मैं बेंचमार्क और माइक्रोटाइम का उपयोग बहुत कम करता हूं, लेकिन मुझे एहसास भी हुआ, जबकि मैं परीक्षण कर रहा था whileऔर foreachप्रत्येक रिफ्रेश में मुझे अलग "विजेता" मिल रहा था। यह हमेशा बहुत अधिक सर्वर चर पर निर्भर करता है, और सबसे अच्छा यह है कि अलग-अलग समय पर बहुत बड़ी संख्या में पुनरावृत्ति करें और जो अधिक बार जीतता है उसे प्राप्त करें, या बस पृष्ठभूमि में क्या हो रहा है और यह जान लें कि यह अंतिम विजेता होगा कोई बात नहीं
Fabrizio

@ डेविड हर्कनेस, आपने मेरा जवाब पहले ही दे दिया है। यदि आप अधिक चाहते हैं, तो मेरे कंधों पर खड़े रहें और अपना उत्तर दें। :) फिर भी, यदि फ़ंक्शन ओवरहेड पहले से ही काफी अधिक महंगा है isset(), तो आपको क्या लगता है कि इसे पारित करने से एक बड़ा सरणी इसे तेज बना देगा ?
जेसन मैकक्रेरी


19

का उपयोग करते हुए isset()तेजी से देखने का लाभ उठाता है क्योंकि यह हैश तालिका का उपयोग करता है , O(n)खोजों की आवश्यकता से बचता है।

इसी तरह की हैश कीज़ की बकेट को निर्धारित करने के लिए djb हैश फ़ंक्शन का उपयोग करते हुए कुंजी को पहले हैशेड किया जाता है O(1)। बाल्टी को तब तक खोजा जाता है जब तक कि सटीक कुंजी नहीं मिल जाती है O(n)

किसी भी जानबूझकर हैश टकराव को छोड़कर , इस दृष्टिकोण की तुलना में बेहतर प्रदर्शन प्राप्त होता है in_array()

ध्यान दें कि isset()आपके द्वारा दिखाए गए तरीके का उपयोग करते समय , किसी अन्य फ़ंक्शन के अंतिम मानों को पास array_keys()करने के लिए एक नया सरणी बनाने के लिए उपयोग करना होगा। डेटा को कुंजी और मान दोनों में संग्रहीत करके एक मेमोरी समझौता किया जा सकता है।

अपडेट करें

यह देखने का एक अच्छा तरीका है कि आपके कोड डिज़ाइन निर्णय रनटाइम प्रदर्शन को कैसे प्रभावित करते हैं, आप अपनी स्क्रिप्ट के संकलित संस्करण की जांच कर सकते हैं :

echo isset($arr[123])

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   ZEND_ISSET_ISEMPTY_DIM_OBJ              2000000  ~0      !0, 123
         1      ECHO                                                 ~0
         2    > RETURN                                               null

echo in_array(123, $arr)

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   SEND_VAL                                             123
         1      SEND_VAR                                             !0
         2      DO_FCALL                                 2  $0      'in_array'
         3      ECHO                                                 $0
         4    > RETURN                                               null

न केवल in_array()एक अपेक्षाकृत अक्षम O(n)खोज का उपयोग करता है , इसे एक फ़ंक्शन ( DO_FCALL) के रूप में भी बुलाया जाना चाहिए, जबकि इसके isset()लिए एक एकल ओपकोड ( ZEND_ISSET_ISEMPTY_DIM_OBJ) का उपयोग करता है ।


7

दूसरा तेज होगा, क्योंकि यह केवल उस विशिष्ट सरणी कुंजी की तलाश में है और जब तक यह नहीं मिलती है तब तक पूरे सरणी पर पुनरावृति करने की आवश्यकता नहीं है (यदि यह नहीं मिला है तो प्रत्येक सरणी तत्व को देखेगा)


लेकिन वैश्विक दायरे में खोजे गए संस्करण के ठिकाने पर भी निर्भर करता है
el Dude

@ EL2002, क्या आप कृपया उस कथन पर विस्तार से बता सकते हैं?
फेब्रीज़ियो

1
माइक, isset()अगर यह नहीं मिला है , तो भी पूरे सरणी को नहीं देख रहा होगा ?
फेब्रीजियो

1
@Fabrizio नहीं, इसे पुनरावृत्त करने की आवश्यकता नहीं है। आंतरिक रूप से (C में) PHP सरणी केवल एक हैश तालिका है। किसी एकल इंडेक्स वैल्यू को देखने के लिए, C केवल उस वैल्यू का एक हैश बनाता है और मेमोरी में उसके असाइन किए गए स्थान को देखता है। वहाँ या तो वहाँ एक मूल्य है या वहाँ नहीं है।
माइक ब्रेंट

1
@Fabrizio यह आलेख PHP द्वारा C में आंतरिक रूप से कैसे प्रतिनिधित्व किया जाता है, इसका एक अच्छा अवलोकन प्रदान करता है। nikic.github.com/2012/03/28/…
माइक ब्रेंट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.