ประสิทธิภาพของ FOR vs FOREACH ใน PHP


134

ก่อนอื่นฉันเข้าใจใน 90% ของแอปพลิเคชันความแตกต่างของประสิทธิภาพนั้นไม่เกี่ยวข้องกันเลย แต่ฉันแค่ต้องรู้ว่าตัวไหนเร็วกว่า นั่นแล ...

ข้อมูลที่มีอยู่บนอินเทอร์เน็ตในปัจจุบันทำให้เกิดความสับสน หลายคนบอกว่า foreach ไม่ดี แต่ในทางเทคนิคแล้วมันควรจะเร็วกว่าเพราะมันควรจะทำให้การเขียนการส่งผ่านอาร์เรย์ง่ายขึ้นโดยใช้ตัววนซ้ำ Iterators ซึ่งคาดว่าจะเร็วขึ้นอีกครั้ง แต่ใน PHP ก็เห็นได้ชัดว่าช้า (หรือนี่ไม่ใช่ PHP?) ฉันกำลังพูดถึงฟังก์ชั่นอาร์เรย์: next () prev () reset () เป็นต้นถ้าเป็นฟังก์ชั่นที่สม่ำเสมอและไม่ใช่หนึ่งในคุณสมบัติภาษา PHP ที่มีลักษณะเหมือนฟังก์ชัน

เพื่อ จำกัด สิ่งนี้ให้แคบลงเล็กน้อย : ฉันไม่น่าสนใจในการสำรวจอาร์เรย์ในขั้นตอนที่มากกว่า 1 (ไม่มีขั้นตอนเชิงลบเช่นกันเช่นการวนซ้ำย้อนกลับ) ฉันยังไม่สนใจการลัดเลาะไปและกลับจากจุดใด ๆ โดยพลการเพียงแค่ 0 ถึงความยาว ฉันไม่เห็นการจัดการอาร์เรย์ที่มีมากกว่า 1,000 คีย์เกิดขึ้นเป็นประจำ แต่ฉันเห็นว่าอาร์เรย์ถูกข้ามผ่านหลายครั้งด้วยตรรกะของแอปพลิเคชัน! สำหรับการดำเนินการส่วนใหญ่เป็นเพียงการจัดการสตริงและการสะท้อนเท่านั้น

เว็บไซต์อ้างอิงบางส่วนมีดังนี้:
http://www.phpbench.com/
http://www.php.lt/benchmark/phpbench.php

สิ่งที่ฉันได้ยินทุกที่:

  • foreachช้าและทำให้for/ whileเร็วขึ้น
  • PHPs foreachคัดลอกอาร์เรย์ที่มันวนซ้ำ เพื่อให้เร็วขึ้นคุณต้องใช้การอ้างอิง
  • รหัสเช่นนี้เร็วกว่าไฟล์$key = array_keys($aHash); $size = sizeOf($key);
    for ($i=0; $i < $size; $i++)
    foreach

นี่คือปัญหาของฉัน ฉันเขียนสคริปต์ทดสอบนี้: http://pastebin.com/1ZgK07USและไม่ว่าฉันจะเรียกใช้สคริปต์กี่ครั้งฉันจะได้รับสิ่งนี้:

foreach 1.1438131332397
foreach (using reference) 1.2919359207153
for 1.4262869358063
foreach (hash table) 1.5696921348572
for (hash table) 2.4778981208801

ในระยะสั้น:

  • foreachเร็วกว่าforeachการอ้างอิง
  • foreach เร็วกว่า for
  • foreachเร็วกว่าforตารางแฮช

ใครช่วยอธิบายหน่อย

  1. ฉันทำอะไรผิดหรือเปล่า?
  2. PHP foreach สิ่งอ้างอิงสร้างความแตกต่างจริงหรือ? ฉันหมายถึงทำไมมันจะไม่คัดลอกถ้าคุณผ่านการอ้างอิง?
  3. รหัสตัววนซ้ำที่เท่ากันสำหรับคำสั่ง foreach คืออะไร ฉันเคยเห็นอยู่สองสามแห่งบนเน็ต แต่ทุกครั้งที่ฉันทดสอบพวกเขาเวลานั้นเป็นไปไม่ได้ ฉันยังได้ทดสอบโครงสร้างตัววนซ้ำแบบง่าย ๆ แต่ดูเหมือนจะไม่ได้ผลลัพธ์ที่ดีเลย - ตัวทำซ้ำอาร์เรย์ใน PHP นั้นแย่มากหรือไม่?
  4. มีวิธี / วิธีการ / โครงสร้างที่เร็วกว่าในการวนซ้ำแม้ว่าอาร์เรย์อื่นที่ไม่ใช่ FOR / FOREACH (และ WHILE)

PHP เวอร์ชัน 5.3.0


แก้ไข: คำตอบ ด้วยความช่วยเหลือจากผู้คนที่นี่ฉันสามารถปะติดปะต่อคำตอบของคำถามทั้งหมดได้ ฉันจะสรุปไว้ที่นี่:

  1. “ ฉันทำอะไรผิดเหรอ?” ฉันไม่สามารถใช้เสียงสะท้อนในเกณฑ์มาตรฐานได้ โดยส่วนตัวแล้วฉันยังไม่เห็นว่า echo เป็นฟังก์ชันที่มีเวลาดำเนินการแบบสุ่มอย่างไรหรือฟังก์ชันอื่น ๆ แตกต่างกันอย่างไร - และความสามารถของสคริปต์นั้นในการสร้างผลลัพธ์เดียวกันของ foreach ได้ดีกว่าทุกอย่างที่ยาก เพื่ออธิบายว่าแค่ "คุณกำลังใช้เสียงสะท้อน" (ฉันควรใช้อะไร) อย่างไรก็ตามฉันยอมรับว่าการทดสอบควรทำด้วยสิ่งที่ดีกว่า แม้ว่าการประนีประนอมในอุดมคติจะไม่อยู่ในใจ
  2. "PHP foreach reference เป็นสิ่งที่สร้างความแตกต่างได้จริงหรือฉันหมายความว่าทำไมมันจะไม่คัดลอกถ้าคุณผ่านการอ้างอิง" ircmaxell แสดงให้เห็นว่าใช่แล้วการทดสอบเพิ่มเติมดูเหมือนจะพิสูจน์ได้ในกรณีส่วนใหญ่การอ้างอิงควรจะเร็วกว่า - แม้ว่าจะได้รับตัวอย่างโค้ดด้านบนของฉัน แต่ส่วนใหญ่ไม่ได้หมายถึงทั้งหมด ฉันยอมรับว่าปัญหานี้อาจไม่ใช้งานง่ายเกินไปที่จะรบกวนในระดับดังกล่าวและต้องการบางสิ่งบางอย่างที่รุนแรงเช่นการแยกส่วนเพื่อพิจารณาว่าสถานการณ์ใดดีกว่าสำหรับแต่ละสถานการณ์
  3. "รหัสตัววนซ้ำที่เท่ากันสำหรับคำสั่ง foreach คืออะไรฉันเคยเห็นบางส่วนบนเน็ต แต่ทุกครั้งที่ฉันทดสอบพวกเขาเวลาจะหยุดฉันยังทดสอบโครงสร้างตัววนซ้ำแบบง่าย ๆ สองสามอย่าง แต่ดูเหมือนจะไม่ได้ผลลัพธ์ที่ดีเลยแม้แต่น้อย - ตัวทำซ้ำอาร์เรย์ใน PHP นั้นแย่มากหรือไม่ " ircmaxell ให้คำตอบร้อง; แม้ว่าโค้ดอาจใช้ได้กับเวอร์ชัน PHP เท่านั้น> = 5
  4. "มีวิธี / วิธีการ / โครงสร้างที่เร็วกว่าในการวนซ้ำแม้ว่าอาร์เรย์อื่นที่ไม่ใช่ FOR / FOREACH (และในขณะที่)" ขอบคุณกอร์ดอนสำหรับคำตอบ การใช้ประเภทข้อมูลใหม่ใน PHP5 ควรเพิ่มประสิทธิภาพหรือเพิ่มหน่วยความจำ (ซึ่งอาจเป็นที่ต้องการขึ้นอยู่กับสถานการณ์ของคุณ) ในขณะที่อาร์เรย์ประเภทใหม่ ๆ มีความเร็วที่ชาญฉลาดดูเหมือนจะไม่ดีไปกว่า array () แต่ splpriorityqueue และ splobjectstorage ดูเหมือนจะเร็วกว่ามาก ลิงก์ที่จัดทำโดย Gordon: http://matthewturland.com/2010/05/20/new-spl-features-in-php-5-3/

ขอบคุณทุกคนที่พยายามช่วย

ฉันจะยึดติดกับ foreach (เวอร์ชันที่ไม่มีการอ้างอิง) สำหรับการข้ามผ่านแบบง่ายๆ


7
กฎข้อ 2.71 ของการเปรียบเทียบ: อย่าสะท้อนกับการเปรียบเทียบ
Mchl

1
foreach ที่มีการอ้างอิงจะต้องถูกนำมาใช้กับการอ้างอิง คุณมีข้อบกพร่องที่นั่น การใช้การอ้างอิงใด ๆ เห็นได้ชัดว่าจะช้ากว่าที่ไม่มีการอ้างอิงแม้ในลูป do-while
bcosca

2
เนื่องจากเป็น php 5.3 คุณอาจต้องการลองทดสอบ Spl Data types vs Arrays ใหม่ หรือดูที่นี่: matthewturland.com/2010/05/20/new-spl-features-in-php-5-3
Gordon

@ Mchl: ฉันใช้มันสองสามครั้งและได้ผลลัพธ์เหมือนกัน - ถ้าเสียงสะท้อนทำให้เกณฑ์มาตรฐานเสียหายฉันก็ไม่ควรได้ผลลัพธ์แบบสุ่มทั้งหมดใช่หรือไม่? นอกจากนี้ฉันต้องการที่จะทำซ้ำแม้ว่าบางสิ่งบางอย่างและส่งออกมาดังนั้นเสียงสะท้อนจึงสำคัญมากสำหรับฉัน ถ้า foreach เร็วกว่าเมื่อ echo'ing นั่นคือโค้ดส่วนใหญ่ที่ฉันควรใช้ foreach @ นิ่งเฉย: สิ่งที่ฉันได้ยินโดยพื้นฐานแล้วเป็นไปตามแนวของ "การอ้างอิงใน foreach ทำให้เร็วขึ้น (เสมอ) เขียนโดยอ้างอิงเสมอ" นั่นคือเหตุผลที่ฉันทดสอบแบบนั้น - ฉันไม่สนใจที่จะเปรียบเทียบกับลูปอ้างอิงอื่น ๆ
srcspider

2
คำถามที่ว่างเปล่าเหล่านี้ควรถูกแบนโดยธรรมชาติ เช่นเดียวกับไซต์ phpbench ที่หลอกลวง
สามัญสำนึกของคุณ

คำตอบ:


110

ความคิดเห็นส่วนตัวของฉันคือการใช้สิ่งที่เหมาะสมในบริบท โดยส่วนตัวแล้วฉันแทบไม่เคยใช้forสำหรับการส่งผ่านอาร์เรย์ ฉันใช้มันสำหรับการทำซ้ำประเภทอื่น แต่foreachมันง่ายเกินไป ... ความแตกต่างของเวลาจะน้อยที่สุดในกรณีส่วนใหญ่

สิ่งสำคัญที่ต้องระวังคือ:

for ($i = 0; $i < count($array); $i++) {

นั่นเป็นการวนซ้ำที่มีราคาแพงเนื่องจากการโทรนั้นนับในการทำซ้ำทุก ๆ ครั้ง ตราบใดที่คุณไม่ทำแบบนั้นฉันไม่คิดว่ามันสำคัญจริงๆ ...

สำหรับการอ้างอิงที่สร้างความแตกต่าง PHP ใช้ copy-on-write ดังนั้นหากคุณไม่เขียนลงในอาร์เรย์จะมีค่าใช้จ่ายค่อนข้างน้อยในขณะที่วนซ้ำ อย่างไรก็ตามหากคุณเริ่มแก้ไขอาร์เรย์ภายในอาร์เรย์นั่นคือจุดที่คุณจะเริ่มเห็นความแตกต่างระหว่างอาร์เรย์ (เนื่องจากจะต้องคัดลอกอาร์เรย์ทั้งหมดและการอ้างอิงสามารถแก้ไขแบบอินไลน์ได้) ...

สำหรับตัววนซ้ำforeachนั้นเทียบเท่ากับ:

$it->rewind();
while ($it->valid()) {
    $key = $it->key();     // If using the $key => $value syntax
    $value = $it->current();

    // Contents of loop in here

    $it->next();
}

เท่าที่มีวิธีที่เร็วกว่าในการทำซ้ำมันขึ้นอยู่กับปัญหาจริงๆ แต่ฉันต้องถามจริงๆว่าทำไม? ฉันเข้าใจว่าต้องการทำให้สิ่งต่างๆมีประสิทธิภาพมากขึ้น แต่ฉันคิดว่าคุณเสียเวลาไปกับการเพิ่มประสิทธิภาพระดับไมโคร จำไว้ว่าPremature Optimization Is The Root Of All Evil...

แก้ไข:จากความคิดเห็นฉันตัดสินใจที่จะทำการทดสอบมาตรฐานอย่างรวดเร็ว ...

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

$start = microtime(true);
foreach ($a as $k => $v) {
    $a[$k] = $v + 1;
}
echo "Completed in ", microtime(true) - $start, " Seconds\n";

$start = microtime(true);
foreach ($a as $k => &$v) {
    $v = $v + 1;
}
echo "Completed in ", microtime(true) - $start, " Seconds\n";

$start = microtime(true);
foreach ($a as $k => $v) {}
echo "Completed in ", microtime(true) - $start, " Seconds\n";

$start = microtime(true);
foreach ($a as $k => &$v) {}    
echo "Completed in ", microtime(true) - $start, " Seconds\n";

และผลลัพธ์:

Completed in 0.0073502063751221 Seconds
Completed in 0.0019769668579102 Seconds
Completed in 0.0011849403381348 Seconds
Completed in 0.00111985206604 Seconds

ดังนั้นหากคุณกำลังแก้ไขอาร์เรย์ในลูปการใช้การอ้างอิงจะเร็วกว่าหลายเท่า ...

และค่าใช้จ่ายสำหรับการอ้างอิงเพียงอย่างเดียวนั้นน้อยกว่าการคัดลอกอาร์เรย์ (นี่คือบน 5.3.2) ... ดังนั้นจึงปรากฏขึ้น (อย่างน้อย 5.3.2) ราวกับว่าการอ้างอิงนั้นเร็วกว่ามาก ...


1
คุณไม่ได้หมายความว่า "การเพิ่มประสิทธิภาพ [ไม่ได้วางแผน] เป็นรากเหง้าของความชั่วร้ายทั้งหมด" หรือ? ;) สิ่งที่ดีคือพวกเขาทั้งหมดทำสิ่งเดียวกันดังนั้นจึงไม่ใช่การเพิ่มประสิทธิภาพมากนักซึ่งเป็น "วิธีมาตรฐานที่ดีกว่าในการนำมาใช้" นอกจากนี้บางคำถามที่ยังไม่ได้รับคำตอบ: คุณพูดเพราะไม่จำเป็นต้องคัดลอก แต่การใช้การอ้างอิงไม่ใช่ค่าใช้จ่ายเกินไปหรือไม่? ความคิดเห็นของการนิ่งเฉยในคำถามของฉันดูเหมือนจะไม่เห็นด้วยกับสมมติฐานของคุณเช่นกัน นอกจากนี้เหตุใดรหัสจึงผลิตช้ากว่าสำหรับการอ้างอิงที่นั่นด้วย foreach เปลี่ยนเป็น 5.3.0 เพื่อแปลงอาร์เรย์ () เป็นอ็อบเจกต์ (เช่น SplFixedArray) หรือไม่?
srcspider

@srcspider: คำตอบที่แก้ไขด้วยรหัสมาตรฐานและผลลัพธ์ที่แสดงการอ้างอิงนั้นเร็วกว่าการอ้างอิงอย่างมาก ...
ircmaxell

1
"the better standard way to adopt." ประสิทธิภาพของ@srcspider ไม่ใช่เกณฑ์เดียวในการเลือกสิ่งที่จะนำมาใช้ โดยเฉพาะอย่างยิ่งในกรณีที่ไกลตัวเช่นนี้ ตรงไปตรงมาคุณแค่เสียเวลา
สามัญสำนึกของคุณ

@Col Shrapnel ฉันเห็นด้วย 100% ความสามารถในการอ่านและการบำรุงรักษาทำได้ดีกว่าประสิทธิภาพที่เหนือกว่าด้วยอัตรากำไรที่มากในกรณีนี้ ... ฉันเห็นด้วยเกี่ยวกับการเลือกมาตรฐานและยึดติดกับมาตรฐานนี้ แต่ใช้มาตรฐานนั้นตามปัจจัยอื่น ๆ ที่สำคัญกว่า ...
ircmaxell

@ircmaxell: การเรียกใช้สคริปต์ของคุณอย่างรวดเร็วดูเหมือนจะพิสูจน์จุดของคุณ แต่ฉันต้องการดูต่อไปอีกเล็กน้อย ฉันอาจแก้ไขคำถามเดิมของฉันด้วยการทดสอบเพิ่มเติมเพื่อรวมคุณสมบัติ 5.3 ใหม่บางอย่าง @Col Shrapnel: FOR เป็นระดับการเขียนโปรแกรมที่เป็นสากลเกือบทั้งหมด FOREACH เป็นไวยากรณ์ที่ง่ายกว่า เท่าที่อ่านได้ดูเหมือนจะอยู่บนพื้นเท่ากัน ทั้งหมดนี้เป็นระดับที่ต่ำมากฉันไม่คิดว่าการบำรุงรักษาจะเป็นปัญหาสำหรับรูปแบบระดับสูง และฉันไม่คิดว่าฉันจะเสียเวลาเพราะ "โครงสร้างพื้นฐาน" นี้จะอธิบายถึงโค้ดจำนวนมากที่ฉันจะเขียน :)
srcspider

54

ฉันไม่แน่ใจว่ามันน่าแปลกใจมาก คนส่วนใหญ่ที่เขียนโค้ดใน PHP มักไม่ค่อยมีความชำนาญในสิ่งที่ PHP ทำกับ Bare Metal ฉันจะระบุบางสิ่งซึ่งจะเป็นจริงเกือบตลอดเวลา:

  1. หากคุณไม่ได้แก้ไขตัวแปร by-value จะเร็วกว่าใน PHP นี่เป็นเพราะการอ้างอิงนั้นถูกนับอยู่แล้วและผลพลอยได้ก็ให้ทำน้อยลง มันรู้ว่าวินาทีที่คุณแก้ไข ZVAL นั้น (โครงสร้างข้อมูลภายในของ PHP สำหรับประเภทส่วนใหญ่) จะต้องแยกมันออกอย่างตรงไปตรงมา (คัดลอกและลืม ZVAL อื่น ๆ ) แต่คุณไม่เคยแก้ไขมันจึงไม่สำคัญ การอ้างอิงทำให้ซับซ้อนมากขึ้นด้วยการทำบัญชีที่มากขึ้นซึ่งต้องทำเพื่อให้ทราบว่าต้องทำอย่างไรเมื่อคุณแก้ไขตัวแปร ดังนั้นหากคุณอ่านอย่างเดียวขัดแย้งกันจะเป็นการดีกว่าที่จะไม่ใช้เครื่องหมาย & ฉันรู้ว่ามันใช้งานง่าย แต่มันก็เป็นความจริงเช่นกัน

  2. Foreach ไม่ช้า และสำหรับการทำซ้ำอย่างง่ายเงื่อนไขที่ทดสอบกับ - "ฉันอยู่ที่ส่วนท้ายของอาร์เรย์นี้" - ทำโดยใช้โค้ดเนทีฟไม่ใช่ PHP opcodes แม้ว่าจะเป็นรหัส OPC แคชของ APC แต่ก็ยังช้ากว่าการดำเนินการแบบเนทีฟจำนวนมากที่ทำที่ Bare Metal

  3. การใช้ for loop "สำหรับ ($ i = 0; $ i <count ($ x); $ i ++) ทำงานช้าเนื่องจาก count () และขาดความสามารถของ PHP (หรือภาษาที่ตีความจริงๆ) เพื่อประเมินในการแยกวิเคราะห์ เวลาว่ามีสิ่งใดแก้ไขอาร์เรย์หรือไม่สิ่งนี้จะป้องกันไม่ให้ประเมินจำนวนครั้งเดียว

  4. แต่เมื่อคุณแก้ไขด้วย "$ c = count ($ x); สำหรับ ($ i = 0; $ i <$ c; $ i ++) $ i <$ c เป็นกลุ่มของ Zend opcodes ที่ดีที่สุดตามที่เป็นอยู่ $ i ++ ในการทำซ้ำ 100000 ครั้งอาจมีความสำคัญ Foreach รู้ในระดับดั้งเดิมว่าต้องทำอย่างไรไม่จำเป็นต้องใช้ opcodes PHP ในการทดสอบเงื่อนไข "ฉันอยู่ที่ส่วนท้ายของอาร์เรย์นี้"

  5. สิ่งที่เกี่ยวกับโรงเรียนเก่า "ในขณะที่ (list (" stuff? อืมการใช้ each (), current () ฯลฯ ล้วนเกี่ยวข้องกับการเรียกใช้ฟังก์ชันอย่างน้อย 1 ครั้งซึ่งไม่ช้า แต่ก็ไม่ฟรีใช่แล้ว PHP opcodes อีกแล้ว! ดังนั้นในขณะที่ + list + แต่ละรายการก็มีต้นทุนเช่นกัน

ด้วยเหตุผลเหล่านี้ foreach จึงเป็นตัวเลือกที่ดีที่สุดสำหรับการทำซ้ำอย่างง่าย

และอย่าลืมว่ามันอ่านง่ายที่สุดด้วยดังนั้นมันจึงชนะ


นี่คือคำอธิบายที่ฉันต้องการขอบคุณ
ยากลำบาก

คำตอบนี้ควรเป็นส่วนเสริมหรือสรุปสำหรับคำตอบที่ทำเครื่องหมายไว้ ฉันดีใจที่ได้อ่านมันทำงานได้ดี
doz87

30

สิ่งหนึ่งที่ต้องระวังในการวัดประสิทธิภาพ (โดยเฉพาะ phpbench.com) คือแม้ว่าตัวเลขจะฟังดูดี แต่การทดสอบก็ไม่ได้ การทดสอบจำนวนมากบน phpbench.com กำลังทำสิ่งต่างๆที่ไม่สำคัญและใช้ความสามารถของ PHP ในการแคชการค้นหาอาร์เรย์เพื่อบิดเบือนเกณฑ์มาตรฐานหรือในกรณีของการทำซ้ำบนอาร์เรย์ไม่ได้ทดสอบในกรณีของโลกแห่งความเป็นจริง (ไม่มีใครเขียนว่างสำหรับ ลูป) ฉันได้ทำเกณฑ์มาตรฐานของตัวเองที่ฉันพบว่าค่อนข้างสะท้อนถึงผลลัพธ์ในโลกแห่งความเป็นจริงและพวกเขามักจะแสดงไวยากรณ์การวนซ้ำแบบเนทีฟของภาษาforeachออกมาด้านบนเสมอ (แปลกใจแปลกใจ)

//make a nicely random array
$aHash1 = range( 0, 999999 );
$aHash2 = range( 0, 999999 );
shuffle( $aHash1 );
shuffle( $aHash2 );
$aHash = array_combine( $aHash1, $aHash2 );


$start1 = microtime(true);
foreach($aHash as $key=>$val) $aHash[$key]++;
$end1 = microtime(true);

$start2 = microtime(true);
while(list($key) = each($aHash)) $aHash[$key]++;
$end2 = microtime(true);


$start3 = microtime(true);
$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++) $aHash[$key[$i]]++;
$end3 = microtime(true);

$start4 = microtime(true);
foreach($aHash as &$val) $val++;
$end4 = microtime(true);

echo "foreach ".($end1 - $start1)."\n"; //foreach 0.947947025299
echo "while ".($end2 - $start2)."\n"; //while 0.847212076187
echo "for ".($end3 - $start3)."\n"; //for 0.439476966858
echo "foreach ref ".($end4 - $start4)."\n"; //foreach ref 0.0886030197144

//For these tests we MUST do an array lookup,
//since that is normally the *point* of iteration
//i'm also calling noop on it so that PHP doesn't
//optimize out the loopup.
function noop( $value ) {}

//Create an array of increasing indexes, w/ random values
$bHash = range( 0, 999999 );
shuffle( $bHash );

$bstart1 = microtime(true);
for($i = 0; $i < 1000000; ++$i) noop( $bHash[$i] );
$bend1 = microtime(true);

$bstart2 = microtime(true);
$i = 0; while($i < 1000000) { noop( $bHash[$i] ); ++$i; }
$bend2 = microtime(true);


$bstart3 = microtime(true);
foreach( $bHash as $value ) { noop( $value ); }
$bend3 = microtime(true);

echo "for ".($bend1 - $bstart1)."\n"; //for 0.397135972977
echo "while ".($bend2 - $bstart2)."\n"; //while 0.364789962769
echo "foreach ".($bend3 - $bstart3)."\n"; //foreach 0.346374034882

3

มันเป็นปี 2020 และวัตถุดิบมีการพัฒนาอย่างมากด้วย php 7.4 และopcache

นี่คือเกณฑ์มาตรฐาน OP ^ ซึ่งรันเป็น unix CLIโดยไม่มีส่วน echo และ html

การทดสอบทำงานในเครื่องคอมพิวเตอร์ทั่วไป

php -v

PHP 7.4.6 (cli) (built: May 14 2020 10:02:44) ( NTS )

สคริปต์เกณฑ์มาตรฐานที่ปรับเปลี่ยน:

<?php 
 ## preperations; just a simple environment state

  $test_iterations = 100;
  $test_arr_size = 1000;

  // a shared function that makes use of the loop; this should
  // ensure no funny business is happening to fool the test
  function test($input)
  {
    //echo '<!-- '.trim($input).' -->';
  }

  // for each test we create a array this should avoid any of the
  // arrays internal representation or optimizations from getting
  // in the way.

  // normal array
  $test_arr1 = array();
  $test_arr2 = array();
  $test_arr3 = array();
  // hash tables
  $test_arr4 = array();
  $test_arr5 = array();

  for ($i = 0; $i < $test_arr_size; ++$i)
  {
    mt_srand();
    $hash = md5(mt_rand());
    $key = substr($hash, 0, 5).$i;

    $test_arr1[$i] = $test_arr2[$i] = $test_arr3[$i] = $test_arr4[$key] = $test_arr5[$key]
      = $hash;
  }

  ## foreach

  $start = microtime(true);
  for ($j = 0; $j < $test_iterations; ++$j)
  {
    foreach ($test_arr1 as $k => $v)
    {
      test($v);
    }
  }
  echo 'foreach '.(microtime(true) - $start)."\n";  

  ## foreach (using reference)

  $start = microtime(true);
  for ($j = 0; $j < $test_iterations; ++$j)
  {
    foreach ($test_arr2 as &$value)
    {
      test($value);
    }
  }
  echo 'foreach (using reference) '.(microtime(true) - $start)."\n";

  ## for

  $start = microtime(true);
  for ($j = 0; $j < $test_iterations; ++$j)
  {
    $size = count($test_arr3);
    for ($i = 0; $i < $size; ++$i)
    {
      test($test_arr3[$i]);
    }
  }
  echo 'for '.(microtime(true) - $start)."\n";  

  ## foreach (hash table)

  $start = microtime(true);
  for ($j = 0; $j < $test_iterations; ++$j)
  {
    foreach ($test_arr4 as $k => $v)
    {
      test($v);
    }
  }
  echo 'foreach (hash table) '.(microtime(true) - $start)."\n";

  ## for (hash table)

  $start = microtime(true);
  for ($j = 0; $j < $test_iterations; ++$j)
  {
    $keys = array_keys($test_arr5);
    $size = sizeOf($test_arr5);
    for ($i = 0; $i < $size; ++$i)
    {
      test($test_arr5[$keys[$i]]);
    }
  }
  echo 'for (hash table) '.(microtime(true) - $start)."\n";

เอาท์พุท:

foreach 0.0032877922058105
foreach (using reference) 0.0029420852661133
for 0.0025191307067871
foreach (hash table) 0.0035080909729004
for (hash table) 0.0061779022216797

อย่างที่คุณเห็นว่าวิวัฒนาการนั้นบ้าคลั่งเร็วกว่ารายงานในปี 2555 ประมาณ560 เท่า

บนเครื่องและเซิร์ฟเวอร์ของฉันหลังจากการทดลองมากมายของฉันพื้นฐานสำหรับลูปนั้นเร็วที่สุด สิ่งนี้ชัดเจนยิ่งขึ้นโดยใช้ลูปแบบซ้อนกัน ( $ i $ j $ k .. )

นอกจากนี้ยังมีความยืดหยุ่นในการใช้งานมากที่สุดและอ่านได้ดีขึ้นจากมุมมองของฉัน


0

ฉันคิดว่า แต่ฉันไม่แน่ใจ: forลูปใช้สองการดำเนินการในการตรวจสอบและเพิ่มค่า foreachโหลดข้อมูลในหน่วยความจำจากนั้นมันจะวนซ้ำทุกค่า


7
ทุกคนมีความเห็นผู้คนมาที่ Stack Overflow เพื่อค้นหาคำตอบ หากคุณไม่แน่ใจในสิ่งที่คุณระบุให้ตรวจสอบซอร์สโค้ดเอกสารประกอบค้นหาโดย Google ฯลฯ
Sebastien F.

เนื่องจากประสิทธิภาพขึ้นอยู่กับการวิจัยและการทดสอบคุณจึงควรมีหลักฐานบางอย่าง โปรดให้ข้อมูลอ้างอิงของคุณตามนั้น หวังว่าคุณจะสามารถปรับปรุงคำตอบของคุณได้
Marwan Salim

ฉันคิดว่ามันขึ้นอยู่กับโหลดจริงของเซิร์ฟเวอร์และสิ่งที่คุณต้องการทำในลูป ฉันคิดว่ามันขึ้นอยู่กับโหลดจริงของเซิร์ฟเวอร์และสิ่งที่คุณต้องการทำในลูป ฉันต้องการทราบว่าการวนซ้ำอาร์เรย์ที่มีตัวเลขฉันควรใช้ foreach หรือ for-loop ดีกว่าดังนั้นฉันจึงรันเกณฑ์มาตรฐานบนsandbox.onlinephpfunctions.comด้วย PHP 7.4 ฉันเรียกใช้สคริปต์เดิมซ้ำ ๆ หลายครั้งและทุกการวิ่งให้ผลลัพธ์ที่แตกต่างกัน ครั้งหนึ่ง for-loop เร็วกว่าอีกครั้ง foreach-loop และอีกครั้งที่เท่ากัน
Alexander Behling
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.