GORAGOD.com

เปรียบเทียบความเร็วในการแปลตัวเลข 0-9 ให้เป็นภาษาอังกฤษ

โจทย์ต้องการแปลตัวเลข 0-9 ให้เป็นภาษาอังกฤษ ส่วนตัวเลขอื่นๆ นอกจากนี้ให้คืนค่าตัวเลขออกมา โดยแสดงผลบนบราวเซอร์ (PHP)
จากโจทย์ หลายคนคงคิดถึง if และ switch case ประมาณนี้

โดยการใช้ if else
if ($value == 0) {
    echo "Zero\n";
} elseif ($value == 1) {
    .....
} else {
    // ไม่พบ
    echo $value."\n";
}

หรือการใช้ switch case
switch ($value) {
    case 0:
        echo "Zero\n";
        break;
    case 1:
        .....
    default:
        // ไม่พบ
        echo $value."\n";
        break;
}

ซึ่งโดยทั่วๆ เราจะมองว่ามันเป็นคำสั่งพื้นฐาน
ในแง่ความเร็ว หลายๆคนคงเคยได้ยินมาว่า switch case นั้นเร็วกว่า if else ซึ่งจากการที่ผมทดสอบมันก็เป็นแบบนั้นแหละ แต่มันมีปัญหาอยู่อย่างหนึงของการใช้ if else และ switch case คือในกรณีที่เงื่อนไขมันมากคำสั่งก็จะยาวและอ่านยากไปด้วยตามมา (ดูได่จากโค้ดด้านล่าง)  แล้วจะมีวิธีไหนที่จะลดความซับซ้อนของโค้ดลงได้

ผมขอเสนออีกวิธีหนึ่ง ด้วยการใช้ isset
// ตัวแปรแอเรย์เก็บตัวเลขในภาษาอังกฤษ
$numbers = array(
    0 => 'Zero',
    1 => 'One',
    ....
    9 => 'Nine',
);
if (isset($numbers[$value])) {
    // พบ
    echo $numbers[$value]."\n";
} else {
    // ไม่พบ
    echo $value."\n";
}

โค้ดด้านบนเราจะสร้างแอเรย์เก็บ ตัวเลข 0-9 พร้อมกับคำแปลตามโจทย์ จากนั้นใช้ isset ในการทดสอบว่ามีตัวแปรที่ต้องการหรือไม่ ถ้ามีก็คืนค่าตัวแปรนั้นๆ ออกมา โค้ดสั้นและง่ายขึ้นเห็นๆ (คำสั่งแอเรย์ที่สร้างอาจดูยาวอยู่นะครับ แต่มันก็อ่านง่าย รวมถึงแก้ไขง่ายขึ้นเยอะเลย)  ส่วนความเร็วของคำสั่งทั้งสามรูปแบบ ดูได้ท้ายบทความเลย (สามารถนำโค้ดไปทดสอบได้ในเครื่องตัวเอง)
// จำนวนรอบการทดสอบ ผลลัพท์เฉลี่ยจากค่านี้
$count = 100;
// แอเรย์เก็บผลลัพท์
$ret = array(0, 0, 0);
// ตัวเลขที่ต้องการแปล 0 - 11
$wants = range(0, 11);
// ตัวแปรแอเรย์เก็บตัวเลขในภาษาอังกฤษ
$numbers = array(
    0 => 'Zero',
    1 => 'One',
    2 => 'Two',
    3 => 'Three',
    4 => 'Four',
    5 => 'Five',
    6 => 'Six',
    7 => 'Seven',
    8 => 'Eight',
    9 => 'Nine',
);
for ($m = 0; $m < $count; $m++) {
    $start = microtime(true);
    foreach ($wants as $value) {
        if (isset($numbers[$value])) {
            // คืนค่าตัวเลขในภาษาอังกฤษ
            echo $numbers[$value]."\n";
        } else {
            // ไม่พบ
            echo $value."\n";
        }
    }
    $ret[0] += microtime(true) - $start;
}
usleep(10240);
for ($m = 0; $m < $count; $m++) {
    $start = microtime(true);
    foreach ($wants as $value) {
        if ($value == 0) {
            echo "Zero\n";
        } elseif ($value == 1) {
            echo "One\n";
        } elseif ($value == 2) {
            echo "Two\n";
        } elseif ($value == 3) {
            echo "Three\n";
        } elseif ($value == 4) {
            echo "Four\n";
        } elseif ($value == 5) {
            echo "Five\n";
        } elseif ($value == 6) {
            echo "Six\n";
        } elseif ($value == 7) {
            echo "Seven\n";
        } elseif ($value == 8) {
            echo "Eight\n";
        } elseif ($value == 9) {
            echo "Nine\n";
        } else {
            // ไม่พบ
            echo $value."\n";
        }
    }
    $ret[1] += microtime(true) - $start;
}
usleep(10240);
for ($m = 0; $m < $count; $m++) {
    $start = microtime(true);
    foreach ($wants as $value) {
        switch ($value) {
            case 0:
                echo "Zero\n";
                break;
            case 1:
                echo "One\n";
                break;
            case 2:
                echo "Two\n";
                break;
            case 3:
                echo "Three\n";
                break;
            case 4:
                echo "Four\n";
                break;
            case 5:
                echo "Five\n";
                break;
            case 6:
                echo "Six\n";
                break;
            case 7:
                echo "Sever\n";
                break;
            case 8:
                echo "Eight\n";
                break;
            case 9:
                echo "Nine\n";
                break;
            default:
                echo $value."\n";
                break;
        }
    }
    $ret[2] += microtime(true) - $start;
}
usleep(10240);
echo '<br><br>Current PHP version: '.phpversion().'<br>';
echo ($ret[0] / $count).' (isset)<br>';
echo ($ret[1] / $count).' (if else)<br>';
echo ($ret[2] / $count).' (switch case)<br>';

ผลการทดสอบ Benchmark ของคำสั่งแต่ละรูปแบบ  
  • ผลการทดสอบเฉลี่ยผมให้ isset ชนะ ทั้งในแง่อ่านง่ายแก้ไขง่าย และทางด้านความเร็ว
  • ผลการทดสอบจริงอาจแตกต่างจากนี้เนื่องจากเวอร์ชั่นของ PHP (PHP แต่ละเวอร์ชั่นอาจได้ผลลัพท์แตกต่างจากนี้ เนื่องจากการ Optimize ของ PHP เอง)
  • ผลการทดสอบในแต่ละรอบไม่ได้ให้ผลเหมือนกันทุกครั้ง และ isset ไมได้เร็วกว่า switch case ซะทุกครั้ง โดยมี if else รั้งท้าย หากใครต้องการเห็นผลทดสอบจริงๆ แนะนำให้นำโค้ดไปรันทดสอบบนเครื่องตัวเอง
  • การทดสอบในตัวอย่าง กระทำกับแต่ละคำสั่งเป็นจำนวน 100 รอบ (ดูได้จากโค้ด) แล้วคืนค่าเวลาเฉลี่ยของแต่ละรูปแบบออกมา