12/04/2026 18:16น.

JS2GO EP.40 การ Optimize Performance ของโค้ด: Go vs JavaScript ภาษาไหนเร็วกว่า?
#Garbage Collection
#Benchmark
#Optimize Performance
#JavaScript
#Go
ในโลกของการพัฒนาโปรแกรม “ความเร็ว” คือหัวใจของทั้ง 🧠 ประสบการณ์ผู้ใช้ (UX) และ ⚙️ ประสิทธิภาพระบบ (System Performance)
โดยเฉพาะอย่างยิ่งในระบบที่ต้อง
- จัดการข้อมูลจำนวนมหาศาล
- รองรับคำขอพร้อมกันหลายพันครั้ง
- หรือประมวลผลงานคำนวณหนัก (CPU-intensive tasks)
ในบทความนี้ เราจะมาดูแนวทางการเพิ่มประสิทธิภาพของโค้ดทั้งใน JavaScript (Node.js) และ Go (Golang) พร้อมตัวอย่างจริง เทคนิคการ profiling และการ benchmark เพื่อค้นหาว่า “ภาษาไหนทำงานได้เร็วกว่า” ในสถานการณ์ต่าง ๆ 🧩
1. ภาพรวมสถาปัตยกรรมของทั้งสองภาษา
| ประเด็น | JavaScript (Node.js) | Go (Golang) |
|---|---|---|
| Execution Model | Single-threaded (Event Loop) | Multi-threaded (Goroutines) |
| Compilation | Just-In-Time (JIT) ผ่าน V8 Engine | Ahead-Of-Time (AOT) Compiler |
| Concurrency | Async/await, Promise | Goroutines + Channels |
| Memory Model | Managed via V8 Garbage Collector | Static allocation + GC |
| เหมาะกับงานประเภท | I/O-bound | CPU-bound & Parallel workloads |
🔍 สรุปสั้น ๆ:
- Node.js เหมาะกับงานที่ “รอ I/O” เช่น API, file I/O, หรือ network request
- Go เหมาะกับงาน “คำนวณหนัก” และระบบที่ต้องการ throughput สูง
2. Garbage Collection (GC)
Garbage Collection (GC) คือกลไกในการคืนหน่วยความจำของ object ที่ไม่ได้ใช้งานแล้ว
🔹 Node.js
Node.js ใช้ V8 Engine GC แบบ Generational + Incremental Collection
ช่วยลด pause time ระหว่างการเก็บกวาดหน่วยความจำ
global.gc(); // trigger GC (ต้องเปิดด้วย flag --expose-gc)
console.log(process.memoryUsage());
แนวทางเพิ่มประสิทธิภาพ GC ใน Node.js:
✅ หลีกเลี่ยง object ซ้อนลึกเกินไป (nested object)
✅ ใช้ Buffer แทน string สำหรับข้อมูล binary
✅ หลีกเลี่ยงการสร้าง object จำนวนมากภายใน loop
✅ ใช้ WeakMap หรือ WeakSet สำหรับ cache ที่ไม่ต้องถือ reference
🔹 Go
Go ใช้ Concurrent Garbage Collector (non-stop world) ที่ออกแบบให้ทำงานไปพร้อมกับโปรแกรมหลักจึงแทบไม่เกิด pause เหมือนใน Node.js
package main
import (
"fmt"
"runtime"
)
func main() {
m := &runtime.MemStats{}
runtime.ReadMemStats(m)
fmt.Printf("Alloc = %v KB\n", m.Alloc/1024)
}
จุดแข็งของ GC ใน Go:
✅ ทำงานพร้อมกับ goroutine อื่น ๆ (concurrent GC)
✅ Latency ต่ำมาก เหมาะกับระบบที่ต้อง “ออนไลน์ตลอดเวลา”
✅ เสถียรในระบบ production ที่มี load สูง เช่น API server หรือ streaming system
3. Memory Profiling
การวิเคราะห์หน่วยความจำช่วยให้เราทราบว่า “โค้ดส่วนไหนใช้หน่วยความจำมากเกินไป” และสามารถลด bottleneck ได้ตรงจุด
🔹 Node.js: Chrome DevTools / Clinic.js
เปิดโหมดวิเคราะห์ได้ด้วย flag:
node --inspect index.js
หรือใช้เครื่องมือ Clinic.js จาก NearForm
npx clinic doctor -- node index.js
จะได้กราฟแสดง Heap Usage, Event Loop Delay, และ GC Timeline
🔹 Go: pprof
Go มีเครื่องมือ pprof ติดมาในตัว ใช้วิเคราะห์ CPU, Memory, และ Block Profile
import (
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
select {}
}
เปิดดูใน browser ได้ที่
👉 http://localhost:6060/debug/pprof/heap
ข้อดี:
✅ ไม่ต้องติดตั้งเพิ่ม (อยู่ใน standard library)
✅ ใช้ใน production ได้โดยไม่กระทบ performance
✅ วิเคราะห์ได้ทั้ง CPU, Heap, Goroutine และ Mutex
4. Parallel Execution (การทำงานแบบขนาน)
🔹 JavaScript
แม้ Node.js จะเป็น single-threaded แต่สามารถทำงานแบบขนานได้ด้วย Worker Threads
const { Worker } = require('worker_threads');
new Worker(`
const { parentPort } = require('worker_threads');
let sum = 0;
for (let i = 0; i < 1e7; i++) sum += i;
parentPort.postMessage(sum);
`, { eval: true })
.on('message', result => console.log('✅ Result:', result));
ข้อจำกัด:
❌ ต้องสร้าง thread ใหม่ทุกครั้ง (overhead สูง)
❌ ใช้ message passing ในการสื่อสารระหว่าง thread
🔹 Go
Go รองรับการประมวลผลแบบขนานโดยธรรมชาติผ่าน Goroutines
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
sum := 0
mu := sync.Mutex{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
for j := 0; j < 1e6; j++ {
sum += j
}
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("✅ Result:", sum)
}
ข้อดีของ Go:
✅ ไม่ต้องจัดการ thread เอง (runtime จัดการให้)
✅ ใช้หน่วยความจำเพียง ~2KB ต่อ goroutine
✅ รองรับการทำงานพร้อมกันหลายพัน task ได้ในระบบเดียว
5. Benchmark Tools
🔹 Node.js
ใช้ console.time() / console.timeEnd() สำหรับ quick test
console.time("Loop");
for (let i = 0; i < 1e7; i++) {}
console.timeEnd("Loop");
หรือใช้ Benchmark.js สำหรับการทดสอบที่แม่นยำกว่า
npm install benchmark
🔹 Go
Go มีระบบ benchmark ในตัว (ผ่าน package testing)
package main
import "testing"
func Sum() {
for i := 0; i < 1e7; i++ {}
}
func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
Sum()
}
}
รันด้วยคำสั่ง:
go test -bench=.
6. ผลการทดสอบเปรียบเทียบ (จาก benchmark จริง)
| การทดสอบ | Node.js | Go | หมายเหตุ |
|---|---|---|---|
| การอ่านไฟล์ (I/O) | ⚡ เร็วมาก | ⚡ เร็วมาก | ต่างกันน้อยมาก |
| งานคำนวณหนัก (CPU) | 🐢 ช้ากว่า | 🚀 เร็วกว่ามาก | Go รันแบบ parallel ได้จริง |
| Memory usage | ใช้มากกว่า ~2x | ใช้น้อยกว่า | Static allocation |
| Startup time | เร็วกว่ามาก | ช้ากว่าเล็กน้อย | Node.js = dynamic runtime |
| Long-running stability | ปานกลาง | ดีเยี่ยม | GC ของ Go เสถียรกว่า |
7. เทคนิคการ Optimize ของแต่ละภาษา
🟨 JavaScript (Node.js)
✅ ใช้ asynchronous I/O ให้มากที่สุด (fs.promises, fetch, etc.)
✅ ใช้ Cluster / Worker Threads สำหรับงาน CPU
✅ หลีกเลี่ยงการสร้าง object ซ้ำใน loop
✅ ใช้ DevTools / Clinic.js ตรวจสอบ event loop lag
✅ ใช้ caching (เช่น Redis หรือ in-memory) ลด load ที่ซ้ำซ้อน
🟦 Go (Golang)
✅ ใช้ Goroutines แทน Thread
✅ ใช้ Buffered Channels เพื่อลด blocking
✅ ใช้ sync.Pool สำหรับ reuse object ขนาดใหญ่
✅ ใช้ pprof วิเคราะห์ bottleneck จริง
✅ หลีกเลี่ยง allocation ซ้ำโดย pre-allocate slice หรือ struct
8. สรุปเปรียบเทียบ
| ประเด็น | Node.js | Go |
|---|---|---|
| ความเร็วในการเริ่มต้น | ⚡ เร็ว | ปานกลาง |
| การประมวลผลแบบขนาน | จำกัด (ผ่าน worker threads) | เต็มรูปแบบ (ผ่าน goroutines) |
| การใช้หน่วยความจำ | สูงกว่า | ต่ำกว่า |
| เหมาะกับงานประเภท | Web / API / I/O-heavy | Backend / CPU-heavy / Data pipeline |
| การ Optimize | ผ่าน async tuning & DevTools | ผ่าน Goroutines, Profiling, Benchmark |
💡 สรุปสุดท้าย:
- ถ้าระบบของคุณเน้น I/O-intensive เช่น API, Web Server → ใช้ Node.js
- ถ้าระบบของคุณเน้น High Performance, Low Latency, และ Concurrency → Go คือคำตอบที่ชัดเจน 💥
ตอนต่อไป
ใน EP.41 ของซีรีส์ JS2GO เราจะพาคุณก้าวสู่ระดับถัดไปกับหัวข้อ 🧩 “การจัดการ Concurrency Patterns ขั้นสูงใน Go และ JavaScript” คุณจะได้เรียนรู้ Patterns สำคัญ เช่น Worker Pool, Fan-in/Fan-out, Rate Limiter และ Pipeline Optimization เพื่อออกแบบระบบที่รองรับโหลดสูงได้อย่างมีประสิทธิภาพในระดับ Production 🚀