|  
  
     
      |  
         
           
            |  | 
 
   
    |  
        มาเขียน PHP ให้ทำงานอย่างประสิทธิภาพ (Performance) กันเถอะ (ตอนที่ 2)       |  
 ติดตามบทความล่าสุดของผู้เขียนได้ที่ phpinfo() Facebook Page
 
 
 
 มาเขียน PHP ให้ทำงานอย่างประสิทธิภาพ (Performance) กันเถอะ (ตอนที่ 2) บทความนี้นำเสนอสิ่งที่ควรรู้เกี่ยวกับภาษา PHP ทั้งสิ่งที่ควรใช้ และไม่ควรใช้ ที่จะทำให้โปรแกรมทำงานได้เร็วขึ้น
 
 
 
 
 6. ใช้การคูณแทนการหารในกรณีที่ให้ผลลัพธ์เหมือนกัน
 
 ไม่ว่าจะในภาษาโปรแกรมใดก็ตาม การดำเนินการหาร (division) จะทำงานช้ากว่าการคูณ (multiplication) เสมอ
 ดังนั้นเราควรใช้การหารในเฉพาะกรณีที่จำเป็นต้องใช้เท่านั้น ซึ่งมีหลายกรณีที่เราสามารถใช้การคูณแทนการหารได้ เช่น
 
 การหาร 2
 
 $x = $y * 0.5; // $x = $y / 2;
 การหาร 4
 
 $x = $y * 0.25; // $x = $y / 4;
 การหาร 10
 
 $x = $y * 0.1; // $x = $y / 10;
 
 หรือเราสามารถใช้ bitwise shift right ในการหารไม่เอาเศษได้กับเลขที่เป็นค่ายกกำลังของ 2 (2, 4, 8, 16, 32 ...)
 
 
 $x = $y >> 1; // $x = floor($y / 2);
$x = $y >> 2; // $x = floor($y / 4);
$x = $y >> 3; // $x = floor($y / 8);
$x = $y >> 4; // $x = floor($y / 16);
$x = $y >> 5; // $x = floor($y / 32);
 
 แม้ในโปรแกรมทั่วๆ ไปอาจจะไม่ได้ทำให้เป็นความแตกต่างทางด้านความเร็วมากมายอะไร แต่สำหรับโปรแกรมที่มีการคำนวณในลูปมากๆ การใช้เทคนิคเหล่านี้จะช่วยให้ความเร็วเพิ่มขึ้น
 
 
 
 
 7. ใช้ === กับการเปรียบเทียบที่รู้ชนิดของค่านั้นๆ แน่นอน
 
 === นั้นเร็วกว่า == มาก เพราะ === จะไม่แปลงค่าที่นำมาเปรียบเทียบ ในขณะที่ == จะแปลงค่าฝั่งขวาให้เป็นชนิดเดียวกับฝั่งซ้ายก่อนที่จะนำมาเปรียบเทียบ
 
 การเปรียบเทียบ '0' กับ 0
 หากใช้ '0' == 0 สิ่งที่ PHP จะทำคือ
 - ตรวจว่า '0' เป็นชนิดเดียวกันกับ 0 หรือไม่ (string กับ int)
 - และเพราะมันไม่ใช่ชนิดเดียวกัน ก็จะแปลง 0 ให้เป็น string
 - และเปรียบเทียบ แล้วจึงให้ผลลัพธ์คืนมา (true)
 
 แต่ '0' === 0 สิ่งที่ PHP จะทำคือ
 - ตรวจว่า '0' เป็นชนิดเดียวกันกับ 0 หรือไม่ (string กับ int)
 - และเพราะมันไม่ใช่ชนิดเดียวกัน ก็จะให้ผลลัพธ์คืนมาทันที (false)
 
 การเปรียบเทียบ '0' กับ array()
 หากใช้ '0' == array() สิ่งที่ PHP จะทำคือ
 - ตรวจว่า '0' เป็นชนิดเดียวกันกับ array() หรือไม่ (string กับ array)
 - และเพราะมันไม่ใช่ชนิดเดียวกัน ก็จะแปลง array() ให้เป็น string และเปรียบเทียบ
 - และเมื่อพบว่า array() ที่แปลงเป็น string ได้ 'Array' ไม่ตรงกับ '0' PHP ก็จะพยายามแปลง array ให้เป็น int ซึ่งได้ 0
 - และเปรียบอีกครั้ง แต่เพราะมันไม่ใช่ชนิดเดียวกัน PHP จึงต้องแปลง 0 ให้เป็น '0' อีกครั้งเพื่อเปรียบเทียบ แล้วจึงให้ผลลัพธ์คืนมา (true)
 
 แต่ '0' === array() สิ่งที่ PHP จะทำคือ
 - ตรวจว่า '0' เป็นชนิดเดียวกันกับ array() หรือไม่ (string กับ array)
 - และเพราะมันไม่ใช่ชนิดเดียวกัน ก็จะให้ผลลัพธ์คืนมาทันที (false)
 
 ดังนั้นการใช้ === จะมีประโยชน์ในเพิ่มความเร็วให้กับโปรแกรมได้มาก โดยเฉพาะกรณีที่เรารู้แน่ชัดว่าค่าที่จะนำมาเปรียบเทียบนั้นเป็นชนิดอะไร และต้องการชนิดอะไรที่จะเปรียบเทียบ เช่นตัวแปรจำพวก $_GET, $_POST, $_COOKIE ที่แน่นอนว่าค่าของสมาชิกของมันจะเป็น string เสมอ
 
 แบบนี้ถือว่าไม่ดี
 
 if ($_GET['action'] == 'delete') {
    // do something
}
 
 แบบนี้เร็วกว่า
 
 if ($_GET['action'] === 'delete') {
    // do something
}
 
 นอกจากนี้ การใช้ === จะช่วยป้องกันความผิดพลาดที่อาจจะเกิดขึ้นได้ด้วย
 
 
 $s = 'Hello World';
if (strpos($s, 'Hello') == false) {
    // not found
}
 
 จากตัวอย่างข้างบน เป็นการใช้ฟังก์ชั่น strpos() เพื่อตรวจว่ามีคำว่า 'Hello' อยู่ในตัวแปร $s หรือไม่
 โดย strpos() จะคืนค่าตำแหน่งของคำที่ค้นหามาเป็น int และคืนค่า false กลับมาในกรณีที่หาไม่เจอ
 ดังนั้น strpos('Hello World', 'Hello') จะให้ค่า 0 กลับคืนมา เพราะเจอ 'Hello' อยู่ที่ตำแหน่ง 0
 แต่เมื่อเอา 0 ไปเปรียบเทียบกับ false ก็จะให้ผลลัพธ์เป็นจริง เพราะ == แปลง false ให้เป็น 0 ดังนั้น 0 กับ false จึงมีค่าเท่ากันในการเปรียบเทียบด้วย == ซึ่งจะทำให้โค้ดดังกล่าวจะทำงานไม่ตรงตามที่ตั้งใจไว้
 
 แบบนี้ถึงจะทำงานถูกต้องเพราะ 0 ไม่เท่ากับ false
 
 $s = 'Hello World';
if (strpos($s, 'Hello') === false) {
    // not found
}
 
 นอกจาก PHP แล้ว ในภาษาอื่นที่ === มีความหมายเดียวกันกับใน PHP คือ "เปรียบเทียบชนิดและค่า" เช่น JavaScript, ActionScript ก็สามารถใช้เทคนิคนี้ได้เช่นเดียวกัน
 
 แต่สิ่งที่ต้องระวังคือ ใน PHP int และ float จะไม่เท่ากันเสมอ
 0 จะไม่เท่ากับ 0.0 เมื่อเปรียบเทียบด้วย ===
 แต่ใน JavaScript หรือ ActionScript 0 จะเท่ากับ 0.0 เมื่อเปรียบเทียบด้วย ===
 
 PHP
 
 0 === 0.0; // false
1 === 1.0; // false
50 === 50.0; // false
2000 === 2000.0; // false
 
 JavaScript
 
 alert(0 === 0.0); // true
alert(1 === 1.0); // true
alert(50 === 50.0); // true
alert(2000 === 2000.0); // true
 
 ที่เป็นเช่นนี้เพราะอะไร เพราะว่าใน JavaScript (ECMAScript) ไม่มีชนิด int และ float มีแค่ Number
 และแม้ใน ActionScript 3.0 จะมี int และ uint แต่เมื่อเปรียบเทียบ int หรือ uint กับ Number ด้วย === จะมีวิธีพิเศษในการตรวจสอบ (ซึ่งก็ยังทำงานเร็วอยู่ดี)
 
 ActionScript 3.0
 
 var a:int = 0;
var b:int = 1;
var c:uint = 50;
var d:uint = 2000;
trace(a === 0.0); // true
trace(b === 1.0); // true
trace(c === 50.0); // true
trace(d === 2000.0); // true
 
 
 
 
 8. พยายามใช้ isset() หรือ empty() ในการตรวจสอบค่าของตัวแปรที่ต้องการแค่ผล จริง/เท็จ
 
 isset() และ empty() เป็น Language Construct ที่มีหน้าที่ในการตรวจสอบการมีอยู่ของตัวแปรและค่าของมัน
 โดย isset() จะตรวจสอบว่าตัวแปรนั้นๆ ถูกสร้างขึ้นมาหรือยัง และมีค่าที่ไม่ใช่ null หรือไม่
 ส่วน empty() จะตรวจสอบว่าตัวแปรนั้นๆ ถูกสร้างขึ้นมาหรือยัง และมีค่าแปลงแล้วได้ผลเป็น false หรือไม่
 ดังนั้นเมื่อมองดูจากการทำงานแล้วจะพบว่า empty() นั้นทำงานช้ากว่า isset() เล็กน้อย เพราะต้องมีการแปลงค่า
 
 แต่ทั้ง isset() และ empty() จะทำงานเร็วกว่าการตรวจสอบค่าตัวแปรแบบธรรมดา โดยเฉพาะในกรณีที่ตัวแปรนั้นถูกสร้างขึ้นมาแล้ว
 
 การตรวจสอบแบบธรรมดา
 
 // ถ้ายังไม่มีการกำหนดค่าให้กับ $_SESSION['logged_in'] จะเกิด error
if ($_SESSION['logged_in']) {
    // do something
}
 
 การตรวจสอบด้วย isset()
 
 // ถ้ายังไม่มีการกำหนดค่าให้กับ $_SESSION['logged_in'] ก็จะไม่เป็นไร
if (isset($_SESSION['logged_in'])) {
    // do something
}
 
 จากตัวอย่างข้างบน การตรวจสอบด้วย isset() จะทำงานเร็วกว่าแบบธรรมดา เพราะ isset() ตรวจแค่ว่า มีการกำหนดตัวแปรนี้ขึ้นมาหรือยัง และมีค่าเป็น null หรือไม่
 ในขณะที่การตรวจสอบตัวแปรแบบธรรมดา จะตรวจว่ามีการกำหนดตัวแปรนี้ขึ้นมาหรือยัง หากมี ก็จะแปลงค่าของมันให้เป็น boolean true หรือ false และหากว่ายังไม่มีการกำหนด ก็จะแสดง error ซึ่งหากมี error ก็จะทำให้โปรแกรมทำงานช้าลงไปอีก แม้จะปิด error_reporting ก็ตาม
 
 ดังนั้นเราควรใช้ isset() หรือ empty() ในกรณีที่ต้องการตรวจสอบความเป็นจริง/เท็จ และป้องกัน error ที่จะเกิดขึ้นโดยไม่ตั้งใจ
 
 และการใช้ isset() ยังช่วยประหยัดหน่วยความจำด้วย เพราะเราไม่จำเป็นต้องสร้างตัวแปรเพื่อบอกความเป็นเท็จ แต่สร้างแค่ตัวแปรที่บอกความเป็นจริงเท่านั้น เช่น การตรวจสอบว่าผู้ใช้ได้ login แล้วหรือยัง ก็ทำแค่ หาก login แล้ว ก็สร้างตัวแปร $_SESSION ขึ้นมาตัวหนึ่งให้มีค่าที่ไม่ใช่ null และตรวจสอบมันด้วย isset() ซึ่งหากยังไม่ login ก็จะไม่มีตัวแปรตัวนี้เกิดขึ้นมา และไม่ใช้หน่วยความจำใดใด
 
 การเขียนแบบทั่วไป
 
 $done = false; // ตัวแปรที่จะทำให้เงื่อนไขข้างล่างเป็นจริง
// จากนั้นทำอะไรสักอย่างที่อาจจะมีการเปลี่ยนค่าของ $done
// ...
// ...
// ...
if ($done) {
    // do something
}
 
 ใช้ isset() เพื่อเพิ่มความเร็วและประหยัดหน่วยความจำ
 
 // ทำอะไรสักอย่างที่อาจจะมีการสร้างตัวแปร $done เพื่อทำให้เงื่อนไขข้างล่างเป็นจริง
// ...
// ...
// ...
if (isset($done)) {
    // do something
}
 
 isset() และ empty() อาจจะดูมีหน้าตาคล้ายฟังก์ชั่น แต่จริงๆ แล้วมันไม่ใช่ มันเป็น op code ดังนั้นมันจะไม่เสียเวลาในการค้นหาฟังก์ชั่นในระบบเพื่อทำงาน แม้อาจจะทำให้ต้องพิมพ์โค้ดยาวขึ้น แต่การฝึกใช้มันให้คล่องจะทำให้โค้ดของคุณมีคุณภาพและมีประสิทธิภาพมากขึ้น
 
 อ่านเพิ่มเติม isset() และ empty() ว่ามันมีการทำงานและประโยชน์อย่างไร">มาทำความเข้าใจ isset() และ empty() ว่ามันมีการทำงานและประโยชน์อย่างไร
 
 
 
 
 9. อย่าใช้ array_push()
 
 เพราะ array_push() เป็นฟังก์ชั่น การเรียกใช้ฟังก์ชั่นในทุกๆ ภาษาโปรแกรมนั้นใช้เวลามากกว่าโค้ดปกติเสมอ
 ใน PHP มี operator []= ที่จะทำการเพิ่มสมาชิกของ array ในตำแหน่งท้ายสุด
 
 ช้ามาก
 
 $arr = array();
array_push($arr, 1);
array_push($arr, 2);
array_push($arr, 3);
array_push($arr, 4);
array_push($arr, 5);
 
 เร็วกว่ามากๆ
 
 $arr = array();
$arr[] = 1;
$arr[] = 2;
$arr[] = 3;
$arr[] = 4;
$arr[] = 5;
 
 แล้ว array_push() มีไว้ทำไม?
 เราควรใช้ array_push() ในกรณีที่เราต้องการเพิ่มสมาชิกมากกว่า 1 ตัวในคราวเดียว
 
 ประโยชน์ของมันจริงๆ
 
 $arr = array();
array_push($arr, 1, 2, 3, 4, 5);
 
 แต่ถึงกระนั้น จากตัวอย่างข้างบน การเพิ่มสมาชิก 5 ตัว (หรือมากกว่านั้น) ใช้ operator []= ก็ยังเร็วกว่าการใช้ array_push() อยู่ดี
 
 ทดสอบความเร็ว
 
 <?php
$t = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
    $arr = array();
    $arr[] = $i;
    $arr[] = $i;
    $arr[] = $i;
    $arr[] = $i;
    $arr[] = $i;
}
$time['[]='] = microtime(true) - $t;
$t = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
    $arr = array();
    array_push($arr, $i, $i, $i, $i, $i);
}
$time['array_push()'] = microtime(true) - $t;
print_r($time);
 
 ดังนั้น array_push() จึงแทบจะไม่มีประโยชน์เลย ยกเว้นว่าจะใช้เป็น variable function หรือทำให้โค้ดสั้นลงโดยการเรียกผ่าน call_user_func_array() เพื่อเพิ่มสมาชิกหลายๆ ตัวในกรณีที่ไม่รู้จำนวนที่แน่นอน
 
 แต่กรณีดังกล่าวใช้ array_merge() จะเร็วกว่า เพราะเรียกใช้ฟังก์ชั่นแค่ครั้งเดียว
 
 
 $some_array = array(4, 5, 6);
$arr = array(1, 2, 3);
$arr = array_merge($arr, $some_array);
print_r($arr);
 
 ผลลัพธ์
 
 Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
)
 
 สรุป อย่าใช้ array_push() ครับ
 
 
 
 
 10. ใช้ foreach แทน for ในการเข้าถึงสมาชิกของ array
 
 ลักษณะนี้เห็นกันบ่อยๆ
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$n = count($arr);
for ($i = 0; $i < $n; $i++) {
    if ($arr[$i] > 5) {
        echo $arr[$i];
    }
}
 
 จากตัวอย่างข้างบนมีการทำงานที่ดูเหมือนจะปกติในลักษณะของการเขียนโปรแกรมในภาษาอื่นๆ โดยทั่วไป
 แต่สำหรับใน PHP นั้น การเขียนในรูปแบบนี้ถือว่าไม่จำเป็น เพราะ PHP มี foreach
 
 ใช้ foreach
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($arr as $value) {
    if ($value > 5) {
        echo $value;
    }
}
 
 จากตัวอย่างข้างบนเมื่อเปรียบเทียบกับตัวอย่างก่อนหน้า จะประหยัดการทำงานไปได้ถึง 2 ขั้นตอน
 1. ไม่ต้องเรียกใช้ count()
 2. ไม่ต้องสั่งให้ PHP ค้นหาสมาชิกใน array เพื่ออ่านค่า ($arr[$i])
 เพราะ foreach ทำทุกอย่างให้หมดแล้ว ตั้งแต่การหาจำนวนสมาชิก และการอ่านค่า และเป็นการทำงานในระดับ op code ซึ่งเร็วกว่ามาก
 
 ถ้าอยากได้ค่า index ด้วย
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($arr as $i => $value) {
    if ($value > 5) {
        echo "$i = $value";
    }
}
 
 จริงๆ แล้ว foreach ไม่ใช่เรื่องระดับสูงแต่เป็น Control Structure พื้นฐานของ PHP เลยก็ว่าได้ แต่ที่ต้องกล่าวถึงเพราะเห็นหลายๆ คนในบอร์ดยังเขียนการเข้าถึง indexed array ด้วย for อยู่ ซึ่งอาจจะเป็นเพราะเคยเรียนรู้ภาษาอื่นๆ มาก่อน เช่น C หรือ Java และไม่ทราบว่า PHP มี foreach
 
 หรืออาจเป็นเพราะในบางครั้งต้องการเขียนการเข้าถึงไปพร้อมๆ กับการเปลี่ยนแปลงค่านั้นๆ
 
 เข้าถึงและเปลี่ยนแปลงค่าด้วย for
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$n = count($arr);
for ($i = 0; $i < $n; $i++) {
    if ($arr[$i] > 5) {
        $arr[$i] = $i * 50;
    }
}
 
 ซึ่งในกรณีนี้ PHP มี syntax พิเศษที่จะทำให้สามารถเปลี่ยนแปลงค่าของสมาชิกใน array ได้
 
 เข้าถึงและเปลี่ยนแปลงค่าด้วย foreach ด้วยการ assign by reference
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($arr as &$value) { // สังเกตว่ามี & หน้า $value
    if ($value > 5) {
        $value = $i * 50;
    }
}
 
 จากตัวอย่างข้างบน $value นั้นจะชี้ไปยังสมาชิกปัจจุบันใน $arr เมื่อเปลี่ยนแปลงค่า $value สมาชิกในตำแหน่งปัจจุบันของ $arr ก็จะเปลี่ยนแปลงด้วย กล่าวคือ $value คือ reference ของสมาชิกแต่ละตัวใน $arr
 
 แต่ข้อควรระวังคือ เมื่อจบการทำงานของ foreach แล้ว reference ดังกล่าวจะคงอยู่
 
 $value ยังคงเป็น reference อยู่แม้จะจบ foreach แล้ว
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($arr as &$value) {
    if ($value > 5) {
        $value = $i * 50;
    }
}
$value = 1234; // ตรงนี้จะทำให้สมาชิกตัวสุดท้ายใน $arr มีค่าเท่ากับ $value
 
 ดังนั้นเมื่อจบ foreach และอาจจะมีการใช้ตัวแปรชื่อเดียวกันกับตอน assign by reference เราควร unset() ตัวแปรนั้นก่อน
 
 unset() เพื่อทำลาย reference
 
 $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($arr as &$value) {
    if ($value > 5) {
        $value = $i * 50;
    }
}
unset($value); // ทำลาย reference
$value = 1234; // จุดนี้จะไม่มีผลต่อ $arr แล้ว
 
 
 
 
 บทความที่เกี่ยวข้อง
 มาเขียน PHP ให้ทำงานอย่างประสิทธิภาพ (Performance) กันเถอะ (ตอนที่ 1)
 มาเขียน PHP ให้ทำงานอย่างประสิทธิภาพ (Performance) กันเถอะ (ตอนที่ 3)
 
 
 
 ติดตามบทความล่าสุดของผู้เขียนได้ที่ phpinfo() Facebook Page
 
 
 
                           
                             |  |  |  |  
                             |  |  |  |  
 
            
              | 
                  
                    |  |  
                    |  | By : | phpinfo() |  
                    |  | Article : | บทความเป็นการเขียนโดยสมาชิก หากมีปัญหาเรื่องลิขสิทธิ์ กรุณาแจ้งให้ทาง webmaster ทราบด้วยครับ |  
                    |  | Score Rating : |   |  
                    |  | Create Date : | 2013-08-19 |  
                    |  | Download : | No files |  |  |  |   |