หยุด Benchmark ซะ! ถ้าไม่อยากปวดตับ

Nuttavut Thongjor

"เห้ยคุณมึง รู้ยังภาษา $()%@ เร็วสัดๆ Benchmark ร้อยสำนักบอกว่าเร็วกว่าภาษา C อีกนะเห้ย" คุณจะทำหน้ายังไงเมื่อได้ยินประโยคนี้?

ประโยคข้างต้นเรามักได้ยินเสมอๆในโลกของการเขียนโปรแกรม หลายคนเลือกที่จะเบะปากใส่ ภาษา C คุณกูยังเขียนไม่เป็นเลย นี่ยังต้องแส่หาเรื่องไปเรียนภาษาอื่นเพิ่มเติมอีกหรือ?

ปอบชอบกินตับสดฉันใด โปรแกรมเมอร์ก็กระหายสิ่งใหม่ฉันนั้น เพราะรสชาติของตับสดมันช่างยั่วยวนใจ โปรแกรมเมอร์ชาวไทยจึงต้องลิ้มลอง ด้วยผล Benchmark ที่น่าเชื่อถือ เราจึงโอนอ่อนตามและพลอยเปลี่ยนงานของเราจากภาษาหนึ่งไปสู่ภาษาหนึ่ง...

สามวันผ่านไป... ไวดังตดปู๊ด~ ภาษา mother of all bombs ได้ถือกำเนิดขึ้น ด้วยผล Benchmark ที่เร็วกว่าภาษา ()()%@ ถึงสิบเท่า คุณจึงตัดสินใจเรียนรู้ภาษาใหม่ พร้อมสละเรือถีบหัวส่งภาษา()%@ อย่างเป็นทางการ...

ลาก่อย Benchmark นี่คือสิ่งที่บทความนี้จะนำเสนอ Benchmark ไม่ใช่เรื่องแย่ แต่มันจะพาเราอิ๊บอ๋ายได้หากยังยึดติดกับมัน

Community สำคัญกว่าโค้ด

ภาษาหรือเฟรมเวิร์กที่เกิดใหม่ ต่อให้ดีแค่ไหนก็ถือว่ายังสอบไม่ผ่าน ด่านอรหันต์ที่ต้องฟันฝ่าคือมีชุมชนนักพัฒนามากน้อยแค่ไหน ต่อให้ภาษาหรือเฟรมเวิร์กนั้นผลิตโดยพระยูไล หากพระถังซัมจั๋งไม่เล่นด้วยก็จบ โอเคป๊ะ!

กาลครั้งหนึ่งไม่นานนักมีเฟรมเวิร์คเกิดใหม่ชื่อ Inferno ที่ผู้สร้างตั้งใจให้เป็นดั่ง React แต่เร็วกว่า ผุดออกมาสู่ท้องตลาด Benchmark ทุกสถาบันก็อวยไส้แตกแหกไส้ฉีก ยกให้ขึ้นแท่นเป็นสุดยอดเฟรมเวิร์คที่โคตรจะเร็วชนิดที่ว่า DTAC ยังกลายเป็นเต่าคลาน...

เวลากลับกลายเป็นเครื่องพิสูจน์ ถึงตอนนี้ Vue กลับกลายเป็นเฟรมเวิร์คที่ดังกว่าหลายขุม ทั้งๆที่สองตัวนี้ก็ชูโรงเรื่องความเร็ว แต่ใยคนจึงทอดทิ้ง Inferno ทั้งๆที่ Inferno เร็วกว่า Vue ด้วยซ้ำ...

เหตุผลหลักประการหนึ่งคือ Community ครับ ภาษาหรือเฟรมเวิร์คอะไรที่เกิดขึ้นมาต้องมีชุมชนและสังคม อารมณ์เหมือนแชร์ลูกโซ่ มันต้องมีคนใช้ เห็นชอบ แล้วบอกต่อ มันถึงจะไปรอด หากไม่มีชุมชนนักพัฒนาแล้ว เมื่อเราเกิดคำถามจะนำข้อสงสัยนั้นไปถามใครหละ? ถามท่านนายกก็คงไม่ได้ ท่านไม่ว่าง แบบว่า.. ท่านออกกำลังกายอยู่

เร็วๆนี้ เฟรมเวิร์คน้องใหม่อย่าง Marko ก็เริ่มเฉิดฉายเข้ามาในสายตานักพัฒนา Front-end ด้วยผล Brenchmark ที่ดีเลิศ ชนะแม้กระทั่งลูกเมียน้อยอย่าง Inferno แต่งานดีบอกได้เลย จะอยู่หรือจะไปขึ้นอยู่กับ Community ล้วนๆแจ้

Benchmark ไม่ใช่คำตอบของทุกสิ่ง วันนี้เฟรมเวิร์ค A เร็วสุด พรุ่งนี้อาจมีเฟรมเวิร์คใหม่ที่ดีกว่าและเร็วกว่าก็เป็นได้ สิ่งหนึ่งที่สำคัญกว่าการมานั่งตรวจผลความเร็วนั่นก็คือ เฟรมเวิร์คดังกล่าวมีผู้ใช้มากน้อยแค่ไหน ถ้าเราติดปัญหาต้องมีคนให้ถามนะ ถ้าจินตนาการไม่ออกนึกถึงเวลามีคนมาหลอกขายเครื่องกรองน้ำซิ ถ้าสินค้าไม่เป็นที่รู้จัก ไม่มีบริการหลังการขาย คุณจะซื้อไหม ถามใจเธอดู~

Benchmark ไม่ได้วัดผลจากการใช้งานจริง

Benchmark (ส่วนใหญ่) วัดผลแค่การใช้งานง่ายๆ แต่ไม่ครอบคลุมถึงการใช้งานในชีวิตจริง คุณไม่สามารถนับเวลาที่รอคอยเข้าส้วมที่บ้าน แล้วไปสรุปว่าห้องน้ำห้างก็ใช้เวลารอคิวนานเท่ากันไม่ได้ฉันใด คุณย่อมใช้ Benchmark วัดผลง่ายๆ แล้วไปสรุปผลของการทำงานจริงไม่ได้ฉันนั้น พอเหอะ เหม็นส้วม~

หลายบริษัทใช้ Hapi.js เพื่อสร้าง API Server แทนที่จะใช้ Express.js ในขณะที่หลายคนพิจารณาแล้วว่าการใช้ Hapi.js เป็นบาปอย่างยิ่ง นั่นเพราะ Performance ที่วัดจาก Brenchmark นั้นห่วยแตกกระแทกฝาโลงยิ่งนัก

Node Framework Benchmark

จาก ภาพข้างต้น พบว่า Hapi.js นั่นให้ผลทดสอบที่วินาศสันตะโรสุดๆ แล้วใยเราจึงหน้าด้านใช้มันกันอยู่ได้?

ในการพัฒนาเว็บแอพพลิเคชันเราคงไม่สร้าง API Server เพื่อคอยรับ Request แล้วส่ง Response กลับไปเฉยๆใช่ไหมครับ เรายังต้องมีการตรวจสอบสิทธิ์ คุมเรื่องความปลอดภัยในการใช้งาน จัดการการใช้งานหน่วยความจำให้มีประสิทธิภาพ และมีการตรวจสอบข้อมูลที่ส่งเข้ามาก่อนดำเนินการอย่างอื่น สิ่งเหล่านี้ Express.js ไม่ได้ทำให้เราแต่ต้น แต่ด้วยความสามารถของ Hapi.js ความสามารถเหล่านี้จึงพร้อมเสริฟให้กับคุณตั้งแต่สั่ง npm install แล้วละ!

เมื่อความสามารถของ Hapi.js มีมากกว่า ย่อมไม่แปลกที่ Hapi.js จะช้ากว่า Express.js เราจึงกล่าวได้ว่านี่คือการวัดผลที่ไม่สมเหตุผลยิ่งนัก ถ้าอยากวัดผลแบบแท้จริง ก็เอา Express.js ไปเสริมเขี้ยวเสริมงาให้มีความสามารถแบบ Hapi.js ก่อนซิ แล้วค่อยมาเทียบกันใหม่ ปัดโถ่ เดี๋ยวก็ทุ่มด้วยโพเดี้ยมซะนิ!

การวัดผลที่วัดจำนวน Request ต่อหนึ่งวินาที ในจังหวะที่อัด Request เยอะขนาดนั้น CPU คงขึ้นพรวดไปเกือบ 100% ในสถานการณ์แบบนี้ก้อน Request อ็อบเจ็กต์คงเยอะแยะตาแป๊ะขายหมูไปหมด เราจะทราบได้อย่างไรว่าเวลาที่ใช้ไปคือเวลาของการทำงานเพื่อตอบสนอง Request ที่ส่งเข้ามาจริงๆ หรือแท้จริงแล้วเวลาส่วนใหญ่หมดไปกับการทำ Garbage Collection เพื่อกำจัดเศษซากตกค้างของอ็อบเจ็กต์เหลือใช้ ที่ลอยค้างเป็นสารแขวนลอยในหน่วยความจำของเรากันแน่?

สวนดุสิตโพลต์ ยังเชื่อถือไม่ได้ แล้วประสาอะไรกับผล Benchmark?

เมื่อ Benchmark เชื่อถือไม่ได้ เราจึงควรยึดถือผลจากการใช้งานจริงเป็นสรณะเสียมากกว่า...

Walmart ห้างสรรพสินค้าดังผู้เล่นแร่แปรธาตุ Hapi.js ให้ได้อุบัติขึ้นมาบนโลกใบนี้ ได้ใช้งานเฟรมเวิร์คดังกล่าวเพื่อจัดการปริมาณทราฟฟิกที่สูงทะลุยอดเขาหิมาลัยในคืนวัน Black Friday ได้อย่างไม่มีปัญหา ผลการใช้งานจริงแบบนี้มันน่าเชื่อถือกว่าผล Benchmark ซะอีก เชื่อซิ!

โลกธุรกิจเวลาคือสิ่งสำคัญ

ยาวไปไม่อ่าน โลกธุรกิจเราสนใจส่งมอบงานให้ทันเวลามากกว่า การพัฒนาที่ช้าแค่นาทีเดียวอาจทำให้คู่แข่งนำเราไปไกลโข เราควรเลือกภาษาโปรแกรมและเฟรมเวิร์คที่ทำให้เราพัฒนางานได้รวดเร็ว มากกว่าจะสนใจว่ามันทำงานได้เร็วแค่ไหน หากคอขวดคือความช้า เราก็แค่อัดแรม เพิ่ม CPU จบป๊ะ!

ภาษาและเฟรมเวิร์คแต่ละตัวมีข้อแตกต่างกัน อย่างน้อยที่สุด Syntax นั่นหละที่แตกต่างกัน ความต่างนี้ย่อมนำไปสู่ผลลัพธ์ของการเขียนโปรแกรมด้วย

ต่อไปนี้คือโปรแกรมเพื่ออ่านไฟล์ JSON ในสองเวอร์ชัน

ภาษา Ruby โดยการใช้ standard library (json)

Ruby
1require 'json'
2
3jobj = JSON.parse(File.read('1.json'))
4coordinates = jobj['coordinates']
5len = coordinates.length
6x = y = z = 0
7
8coordinates.each do |coord|
9 x += coord['x']
10 y += coord['y']
11 z += coord['z']
12end
13
14p x / len
15p y / len
16p z / len

ภาษา C++ โดยการใช้ Gason

Code
1#include "gason.h"
2#include <iostream>
3#include <fstream>
4#include <sstream>
5#include <string>
6#include <string.h>
7
8using namespace std;
9
10void read_file(string filename, stringstream &buffer){
11 ifstream f(filename.c_str());
12 if (f)
13 {
14 buffer << f.rdbuf();
15 f.close();
16 }
17}
18
19int main() {
20 std::stringstream ss;
21 read_file("./1.json", ss);
22
23 string text = ss.str();
24 char *endptr;
25 JsonValue jobj;
26 JsonAllocator allocator;
27 int status = jsonParse((char *)text.c_str(), &endptr, &jobj, allocator);
28 if (status != JSON_OK) return 1;
29
30 JsonValue coordinates;
31 for (auto data : jobj) {
32 if (strcmp(data->key, "coordinates") == 0) { coordinates = data->value; }
33 }
34 double x = 0, y = 0, z = 0;
35 int len = 0;
36
37 for (auto coord : coordinates) {
38 len++;
39 for (auto c : coord->value) {
40 char *key = c->key;
41 if (strcmp(key, "x") == 0) { x += c->value.toNumber(); } else
42 if (strcmp(key, "y") == 0) { y += c->value.toNumber(); } else
43 if (strcmp(key, "z") == 0) { z += c->value.toNumber(); }
44 }
45 }
46
47 std::cout << x / len << std::endl;
48 std::cout << y / len << std::endl;
49 std::cout << z / len << std::endl;
50
51 return 0;
52}

ผล Benchmark ของโปรแกรมข้างต้นพบว่า การโปรแกรมด้วย C++ ผ่าน Gason ใช้เวลาเร็วกว่าการทำงานด้วย Ruby ถึง 8 เท่าเชียวละ อุต๊ะ! จากผลลัพธ์เกินคาดนี้เราจึงควรเขียนโปรแกรมด้วยภาษา C++ แทนที่จะเป็น Ruby ใช่หรือไม่?

การพัฒนาซอฟต์แวร์เรามีขอบเขตการส่งมอบงานครับ ภาษาที่ให้ผลลัพธ์ดีกว่าแต่ใช้เวลาเขียนนานกว่าก็ไม่ตอบโจทย์การพัฒนาเช่นกัน เราต้องไม่ลืมว่า Functional Requirement คือสิ่งสำคัญ ระบบต้องสามารถทำงานได้ตามที่ควรจะเป็นก่อน ส่วน Non-Functional Requirement อย่าง Performance แม้จะสำคัญแต่เราปรับแก้ทีหลังได้ครับ

โปรแกรมที่มีประสิทธิภาพดีกว่า แต่แลกมาด้วยการเขียนที่ยากกว่าย่อมนำไปสู่การ maintain โค้ดที่ยากกว่าด้วย แหม จะกลับมาแก้ไขโค้ดทีนึกว่าถอดรหัสลับดาวินชี แบบนี้ก็ไม่ไหวนะ!

เพื่อให้การส่งมอบงานของเราตรงเวลา เราจึงควรเลือกภาษาและเฟรมเวิร์คที่ทำให้เราพัฒนาระบบได้เร็ว ส่วนประสิทธิภาพเป็นอำนาจของเงินแล้วหละครับ ถ้าเรา Tune Performance จนถึงขีดสุดแล้วไม่มีอะไรดีขึ้น อย่ารอช้าจงควักกะตังแล้วอัพแรม อัพ CPU อยู่ ซะ... อย่างน้อยๆก็ดีกว่าเอาเงินไปจ่ายให้ท่านๆเสวยคาเวียร์ลอยฟ้ากันนะฮับ~

หมายเหตุ มีข้อควรคำนึงพิเศษดังนี้

  1. หากระบบของเราต้องการ Performance แต่แรก เช่นเป็นระบบทาง Financial ยังไงเราก็คงหลีกเลี่ยงไม่ได้ที่จะใช้ภาษาที่เร็วเพียงพอ
  2. ภาษาโปรแกรมที่ทำให้เราพัฒนาโปรแกรมได้เร็ว ไม่ใช่เพียงเพราะเป็นภาษาที่เขียนได้ง่าย แต่ต้องเป็นภาษาที่ทีมเราเชี่ยวชาญและคุ้นเคยด้วย

สรุป

หยุด! ที่จะเสียเวลากับผล Benchmark เพราะคุณจะไม่ได้ประโยชน์อะไรจากมัน ดูกราฟหุ้นยังรู้ว่าติดดอย แต่ดูกราฟ Benchmark คุณจะไม่รู้อะไร

สำหรับเพื่อนๆคนใดที่สนใจข้อมูลเพิ่มเติม เชิญเสพบทความเก่าเรื่อง ใช้ภาษาอะไรเขียนโปรแกรมดี? คำถามยอดฮิตกับ7ข้อบัญญัติในการเลือกภาษาโปรแกรม

เอกสารอ้างอิง

Alex Ogier (2016). Node performance 2016: Hapi, Express.js., Restify and Koa. Retrieved April, 14, 2017, from https://raygun.com/blog/node-performance/

Eran Hammer (2014). Performance at Rest.. Retrieved April, 14, 2017, from https://hueniverse.com/performance-at-rest-75bb8fff143

kostya. Some benchmarks of different languages.. Retrieved April, 14, 2017, from https://github.com/kostya/benchmarks

สารบัญ

สารบัญ

  • Community สำคัญกว่าโค้ด
  • Benchmark ไม่ได้วัดผลจากการใช้งานจริง
  • โลกธุรกิจเวลาคือสิ่งสำคัญ
  • สรุป
  • เอกสารอ้างอิง