ফরচ এর পারফরম্যান্স, ল্যাম্বডা সহ অ্যারে_ম্যাপ এবং স্ট্যাটিক ফাংশন সহ অ্যারে_ম্যাপ


144

এই তিনটি পদ্ধতির মধ্যে পারফরম্যান্স পার্থক্য (যদি থাকে তবে) উভয়ই একটি অ্যারেরকে অন্য অ্যারেতে রূপান্তর করতে ব্যবহৃত হয়?

  1. ব্যবহার foreach
  2. array_mapল্যাম্বদা / ক্লোজার ফাংশন ব্যবহার করে
  3. ব্যবহার array_map'স্ট্যাটিক' ফাংশনটি / পদ্ধতি সঙ্গে
  4. অন্য কোন পন্থা আছে?

নিজেকে পরিষ্কার করার জন্য, আসুন উদাহরণগুলি দেখে নেওয়া যাক, সমস্ত একই কাজ করে - সংখ্যার অ্যারে 10 দ্বারা গুণ করে:

$numbers = range(0, 1000);

প্রতিটির জন্য

$result = array();
foreach ($numbers as $number) {
    $result[] = $number * 10;
}
return $result;

ল্যাম্বদা সহ মানচিত্র

return array_map(function($number) {
    return $number * 10;
}, $numbers);

স্ট্যাটিক রেফারেন্স হিসাবে পাস 'স্ট্যাটিক' ফাংশন সহ মানচিত্র

function tenTimes($number) {
    return $number * 10;
}
return array_map('tenTimes', $numbers);

অন্য কোন পন্থা আছে? আমি উপরের থেকে মামলার মধ্যে সমস্ত পার্থক্য শুনে খুশি হব এবং অন্যের পরিবর্তে কেন ব্যবহার করা উচিত তা কোনও ইনপুট uts


10
আপনি শুধু বেঞ্চমার্ক করে দেখুন না কী হয়?
জন

17
ঠিক আছে, আমি একটি মানদণ্ড তৈরি করতে পারি। তবে এটি এখনও অভ্যন্তরীণভাবে কীভাবে কাজ করে তা আমি জানি না। এমনকি যদি আমি জানতে পারি যে একটি দ্রুততর, তবে এখনও আমি জানি না। এটি কি পিএইচপি সংস্করণের কারণে? এটি কি ডেটা নির্ভর করে? সাহচর্যমূলক এবং সাধারণ অ্যারেগুলির মধ্যে কোনও পার্থক্য রয়েছে? অবশ্যই আমি পুরো মানদণ্ডের মান তৈরি করতে পারি তবে কিছু তত্ত্ব পাওয়া এখানে অনেক সময় সাশ্রয় করে। আশা করি আপনি বুঝতে পেরেছেন ...
পাভেল এস।

2
দেরিতে মন্তব্য করা হয়েছে, তবে কি উপরে নেই (তালিকা ($ k, $ v) = প্রতিটি ($ অ্যারে)) উপরের সমস্তটির চেয়ে দ্রুত? আমি এই পিএইচপি 5.6 এ বেঞ্চমার্ক করিনি, তবে এটি পূর্ববর্তী সংস্করণগুলিতে ছিল।
ওভেন বেরেসফোর্ড

উত্তর:


121

এফডব্লিউআইডাব্লু, পোস্টার এটি না করায় আমি কেবল বেঞ্চমার্কটি করেছি। পিএইচপি 5.3.10 + এক্সডিব্যাগে চলছে।

এক্সডিবেগ এবং আরও সাম্প্রতিক পিএইচপি সংস্করণ ছাড়াই অতিরিক্ত ফলাফলের জন্য নীচে এমসিএফিডারের উত্তরের সাথে তুলনা করুন 2015-01-22।


function lap($func) {
  $t0 = microtime(1);
  $numbers = range(0, 1000000);
  $ret = $func($numbers);
  $t1 = microtime(1);
  return array($t1 - $t0, $ret);
}

function useForeach($numbers)  {
  $result = array();
  foreach ($numbers as $number) {
      $result[] = $number * 10;
  }
  return $result;
}

function useMapClosure($numbers) {
  return array_map(function($number) {
      return $number * 10;
  }, $numbers);
}

function _tenTimes($number) {
    return $number * 10;
}

function useMapNamed($numbers) {
  return array_map('_tenTimes', $numbers);
}

foreach (array('Foreach', 'MapClosure', 'MapNamed') as $callback) {
  list($delay,) = lap("use$callback");
  echo "$callback: $delay\n";
}

আমি এক ডজন চেষ্টা জুড়ে 1M সংখ্যা সহ বেশ সুসংগত ফলাফল পেয়েছি:

  • Foreach: 0.7 সেকেন্ড
  • বন্ধ হওয়ার মানচিত্র: 3.4 সেকেন্ড
  • ফাংশন নামের মানচিত্র: 1.2 সেকেন্ড

ধরুন বন্ধ হওয়ার সময় মানচিত্রের অপ্রয়োজনীয় গতিটি বন্ধ হওয়ার কারণটি সম্ভবত প্রতিবার মূল্যায়ন করা হয়েছিল, আমিও এটি পরীক্ষা করেছিলাম:


function useMapClosure($numbers) {
  $closure = function($number) {
    return $number * 10;
  };

  return array_map($closure, $numbers);
}

তবে ফলাফলগুলি অভিন্ন, নিশ্চিত হয়ে যে বন্ধটি কেবল একবারই মূল্যায়ন করা হবে।

2014-02-02 আপডেট: অপকডস ডাম্প

তিনটি কলব্যাকের জন্য এখানে অপকোড ডাম্প রয়েছে। প্রথম useForeach():



compiled vars:  !0 = $numbers, !1 = $result, !2 = $number
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
  10     0  >   EXT_NOP                                                  
         1      RECV                                                     1
  11     2      EXT_STMT                                                 
         3      INIT_ARRAY                                       ~0      
         4      ASSIGN                                                   !1, ~0
  12     5      EXT_STMT                                                 
         6    > FE_RESET                                         $2      !0, ->15
         7  > > FE_FETCH                                         $3      $2, ->15
         8  >   OP_DATA                                                  
         9      ASSIGN                                                   !2, $3
  13    10      EXT_STMT                                                 
        11      MUL                                              ~6      !2, 10
        12      ASSIGN_DIM                                               !1
        13      OP_DATA                                                  ~6, $7
  14    14    > JMP                                                      ->7
        15  >   SWITCH_FREE                                              $2
  15    16      EXT_STMT                                                 
        17    > RETURN                                                   !1
  16    18*     EXT_STMT                                                 
        19*   > RETURN                                                   null

এরপর useMapClosure()


compiled vars:  !0 = $numbers
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
  18     0  >   EXT_NOP                                                  
         1      RECV                                                     1
  19     2      EXT_STMT                                                 
         3      EXT_FCALL_BEGIN                                          
         4      DECLARE_LAMBDA_FUNCTION                                  '%00%7Bclosure%7D%2Ftmp%2Flap.php0x7f7fc1424173'
  21     5      SEND_VAL                                                 ~0
         6      SEND_VAR                                                 !0
         7      DO_FCALL                                      2  $1      'array_map'
         8      EXT_FCALL_END                                            
         9    > RETURN                                                   $1
  22    10*     EXT_STMT                                                 
        11*   > RETURN                                                   null

এবং বন্ধটি এটি বলে:


compiled vars:  !0 = $number
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
  19     0  >   EXT_NOP                                                  
         1      RECV                                                     1
  20     2      EXT_STMT                                                 
         3      MUL                                              ~0      !0, 10
         4    > RETURN                                                   ~0
  21     5*     EXT_STMT                                                 
         6*   > RETURN                                                   null

তারপরে useMapNamed()ফাংশন:


compiled vars:  !0 = $numbers
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
  28     0  >   EXT_NOP                                                  
         1      RECV                                                     1
  29     2      EXT_STMT                                                 
         3      EXT_FCALL_BEGIN                                          
         4      SEND_VAL                                                 '_tenTimes'
         5      SEND_VAR                                                 !0
         6      DO_FCALL                                      2  $0      'array_map'
         7      EXT_FCALL_END                                            
         8    > RETURN                                                   $0
  30     9*     EXT_STMT                                                 
        10*   > RETURN                                                   null

এবং নামযুক্ত ফাংশন এটি কল করে _tenTimes():


compiled vars:  !0 = $number
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
  24     0  >   EXT_NOP                                                  
         1      RECV                                                     1
  25     2      EXT_STMT                                                 
         3      MUL                                              ~0      !0, 10
         4    > RETURN                                                   ~0
  26     5*     EXT_STMT                                                 
         6*   > RETURN                                                   null


মানদণ্ডের জন্য ধন্যবাদ। তবে, আমি কেন জানতে চাই যে কেন এইরকম পার্থক্য রয়েছে। এটি কি কোনও ফাংশন কল ওভারহেডের কারণে?
পাভেল এস

4
আমি ইস্যুতে অপকোড ডাম্প যুক্ত করেছি। প্রথম জিনিসটি আমরা দেখতে পাচ্ছি যে নামকৃত ফাংশন এবং ক্লোজারটি হুবহু একই একই ডাম্প রয়েছে এবং এরা অ্যারে_ম্যাপের মাধ্যমে কেবল একই ব্যতিক্রম ছাড়া ডাকা হয়: ক্লোজার কলটিতে আরও একটি অপকোড ডেকলআরএলএমএলবিডিএ_ফুনশন অন্তর্ভুক্ত রয়েছে, যা এটি কেন ব্যবহার করছে তা ব্যাখ্যা করে নামকরণ করা ফাংশনটি ব্যবহার করার চেয়ে কিছুটা ধীর। এখন অ্যারে লুপ বনাম অ্যারে_ম্যাপ কলগুলির সাথে তুলনা করে, অ্যারে লুপের সমস্ত কিছুই ইনলাইন দ্বারা ব্যাখ্যা করা হবে, কোনও ফাংশনে কোনও কল ছাড়াই, যার অর্থ ধাক্কা / পপ করার কোনও প্রসঙ্গ নেই, লুপের শেষে কেবল একটি জেএমপি, যা সম্ভবত বড় পার্থক্য ব্যাখ্যা করে ।
এফজিএম 17

4
আমি এটি একটি বিল্ট-ইন ফাংশন (স্ট্র্টলওয়ার) ব্যবহার করে সবেমাত্র চেষ্টা করেছি এবং সেক্ষেত্রে useMapNamedআসলে এর চেয়ে দ্রুত useArray। ভেবেছিলাম তা উল্লেখযোগ্য।
অসন্তুষ্ট গোয়াট

1
ইন lap, আপনি কি range()প্রথম মাইক্রোটাইম কলের উপরে কলটি চান না ? (যদিও সম্ভবত তুচ্ছ লুপ জন্য সময় সঙ্গে তুলনা।)
contrebis

1
@ বিলিএনএইচ পিএইচপি 7.x প্রকৃতপক্ষে এত দ্রুত। এই সংস্করণটি দ্বারা উত্পাদিত ওপকোডগুলি দেখতে আকর্ষণীয় হবে, বিশেষত ওপচ্যাশের সাথে / ছাড়াই তুলনা করা কারণ এটি কোড ক্যাশে করা ছাড়াও প্রচুর অপ্টিমাইজেশন করে।
এফজিএম

231

এক্সডিবাগ অক্ষম করে এই বেঞ্চমার্কটি চালানো আকর্ষণীয়, কারণ এক্সডিবাগ যথেষ্ট পরিমাণে ওভারহেড যোগ করে, ফাংশন কলগুলিতে সহায়তা করে।

এটি xdebug সহ 5.6 ব্যবহার করে এফজিএম এর স্ক্রিপ্ট

ForEach   : 0.79232501983643
MapClosure: 4.1082420349121
MapNamed  : 1.7884571552277

Xdebug ছাড়া

ForEach   : 0.69830799102783
MapClosure: 0.78584599494934
MapNamed  : 0.85125398635864

এখানে পূর্বাভাস এবং বন্ধ সংস্করণের মধ্যে খুব সামান্য পার্থক্য রয়েছে।

এটির সাথে ক্লোজার সহ একটি সংস্করণ যুক্ত করাও আকর্ষণীয় use

function useMapClosureI($numbers) {
  $i = 10;
  return array_map(function($number) use ($i) {
      return $number * $i++;
  }, $numbers);
}

তুলনার জন্য আমি যুক্ত করছি:

function useForEachI($numbers)  {
  $result = array();
  $i = 10;
  foreach ($numbers as $number) {
    $result[] = $number * $i++;
  }
  return $result;
}

এখানে আমরা দেখতে পাচ্ছি এটি ক্লোজার সংস্করণে প্রভাব ফেলবে, অন্যদিকে অ্যারে লক্ষণীয়ভাবে পরিবর্তন হয়নি।

19/11/2015 আমি এখন তুলনা করার জন্য পিএইচপি 7 এবং এইচএইচভিএম ব্যবহার করে ফলাফল যুক্ত করেছি। সিদ্ধান্তগুলি একই রকম, যদিও সবকিছু অনেক দ্রুত।

PHP 5.6

ForEach    : 0.57499806880951
MapClosure : 0.59327731132507
MapNamed   : 0.69694859981537
MapClosureI: 0.73265469074249
ForEachI   : 0.60068697929382

PHP 7

ForEach    : 0.11297199726105
MapClosure : 0.16404168605804
MapNamed   : 0.11067249774933
MapClosureI: 0.19481580257416
ForEachI   : 0.10989861488342

HHVM

ForEach    : 0.090071058273315
MapClosure : 0.10432276725769
MapNamed   : 0.1091267824173
MapClosureI: 0.11197068691254
ForEachI   : 0.092114186286926

2
টাই ভেঙে এবং 51 তম শীর্ষে তুলে দিয়ে আমি আপনাকে বিজয়ী ঘোষণা করি। পরীক্ষার ফলাফলগুলি পরিবর্তিত হয় না তা নিশ্চিত করার জন্য খুব গুরুত্বপূর্ণ! প্রশ্ন, যদিও, আপনার "অ্যারে" এর ফলাফলের সময়গুলি ফোরচ লুপ পদ্ধতিটি, তাই না?
বাটাল বাটকাস

2
দুর্দান্ত রেডোন। 7 টা কত দ্রুত তা দেখে ভাল লাগল। কর্মক্ষেত্রে এখনও 5.6-এ আমার ব্যক্তিগত সময়ে এটি ব্যবহার শুরু করবে।
ড্যান

1
তাহলে কেন আমাদের অবশ্যই পূর্বাঞ্চের পরিবর্তে অ্যারে_ম্যাপটি ব্যবহার করতে হবে? এটি যদি পিএইচপি-তে যুক্ত হয় তবে যদি এটির কার্য সম্পাদন খারাপ হয়? ভবিষ্যতের পূর্বে অ্যারে_ম্যাপের দরকার আছে এমন কোন নির্দিষ্ট শর্ত আছে? ফোরচ হ্যান্ডেল করতে না পারায় এবং অ্যারে_ম্যাপটি পরিচালনা করতে পারে এমন কোনও নির্দিষ্ট যুক্তি রয়েছে কি?
হেন্দ্রডাব্লুডি 13

3
array_map(এবং তার সংশ্লিষ্ট ফাংশন array_reduce, array_filter) আপনি সুন্দর কোড লিখতে যাক। যদি array_mapএটি খুব ধীর হয় তবে এটি ব্যবহারের কারণ হবে foreachতবে এটির অনুরূপ, তাই আমি array_mapযেখানেই এটি বোধগম্য করব তা ব্যবহার করব ।
mcfedr

3
পিএইচপি 7 দেখে ভাল লাগছে আমার প্রকল্পগুলির জন্য একটি ভিন্ন ব্যাকএন্ড ভাষায় স্যুইচ করতে চলেছিল তবে আমি পিএইচপি করবো।
রিয়েলসলেও

8

এটা মজার. তবে আমি নিম্নলিখিত কোডগুলি সহ আমার বিপরীত ফলাফল পেয়েছি যা আমার বর্তমান প্রকল্পগুলি থেকে সরলীকৃত হয়েছে:

// test a simple array_map in the real world.
function test_array_map($data){
    return array_map(function($row){
        return array(
            'productId' => $row['id'] + 1,
            'productName' => $row['name'],
            'desc' => $row['remark']
        );
    }, $data);
}

// Another with local variable $i
function test_array_map_use_local($data){
    $i = 0;
    return array_map(function($row) use ($i) {
        $i++;
        return array(
            'productId' => $row['id'] + $i,
            'productName' => $row['name'],
            'desc' => $row['remark']
        );
    }, $data);
}

// test a simple foreach in the real world
function test_foreach($data){
    $result = array();
    foreach ($data as $row) {
        $tmp = array();
        $tmp['productId'] = $row['id'] + 1;
        $tmp['productName'] = $row['name'];
        $tmp['desc'] = $row['remark'];
        $result[] = $tmp;
    }
    return $result;
}

// Another with local variable $i
function test_foreach_use_local($data){
    $result = array();
    $i = 0;
    foreach ($data as $row) {
        $i++;
        $tmp = array();
        $tmp['productId'] = $row['id'] + $i;
        $tmp['productName'] = $row['name'];
        $tmp['desc'] = $row['remark'];
        $result[] = $tmp;
    }
    return $result;
}

এখানে আমার পরীক্ষার ডেটা এবং কোডগুলি রয়েছে:

$data = array_fill(0, 10000, array(
    'id' => 1,
    'name' => 'test',
    'remark' => 'ok'
));

$tests = array(
    'array_map' => array(),
    'foreach' => array(),
    'array_map_use_local' => array(),
    'foreach_use_local' => array(),
);

for ($i = 0; $i < 100; $i++){
    foreach ($tests as $testName => &$records) {
        $start = microtime(true);
        call_user_func("test_$testName", $data);
        $delta = microtime(true) - $start;
        $records[] = $delta;
    }
}

// output result:
foreach ($tests as $name => &$records) {
    printf('%.4f : %s '.PHP_EOL, 
              array_sum($records) / count($records), $name);
}

ফলাফল হলো:

0.0098: অ্যারে_ম্যাপ
0.0114: ভবিষ্যদ্বাণী
0.0114: অ্যারে_ম্যাপ_ ইউজ_লোকাল
0.0115: foreach_use_local

আমার পরীক্ষাগুলি এক্সএডিবাগ ছাড়াই এলএএমপি উত্পাদন পরিবেশে ছিল। আমি xdebug ঘোরাফেরা অ্যারে_ম্যাপ এর কর্মক্ষমতা কমিয়ে দেবে।


আপনার যদি @ এমসিএফিডার উত্তর পড়তে সমস্যা হয় কিনা তা নিশ্চিত নন, তবে তিনি স্পষ্টভাবে ব্যাখ্যা করেছেন যে এক্সডিগুগ সত্যই কমিয়ে দেয় array_map;)
igorsantos07

আমি এক্সএইচপ্রুফের পরীক্ষামূলক পারফরম্যান্স array_mapএবং foreachব্যবহার করছি। এবং এটি আকর্ষণীয় array_map`foreach` এর চেয়ে বেশি স্মৃতি গ্রাস করে`
গোপাল জোশী
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.