|
มาเขียน 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 |
|
Sponsored Links |
|
|
|
|
|
|