برنامه نویس فان | Fun Developer یک آدم ساده که عاشق برنامه نویسی و کد زدنه :) تلاش میکنه تا به بقیه کمک کنه. توسعه دهنده هسته لاراول و فضای اوپن سورس. فاندر پرانتز و کد نیوز.

در این مقاله با ۸ نکته کلیدی برای بهبود عملکرد برنامههای نوشتهشده با زبان گولنگ آشنا میشویم. گولنگ بهصورت پیشفرض زبانی سریع و بهینه است، اما با رعایت برخی نکات و الگوهای حرفهای میتوان سرعت اجرای برنامه، مصرف حافظه، و کارایی کلی را به سطح بالاتری رساند. این نکات برای توسعهدهندگانی که به ساخت سرویسهای مقیاسپذیر و سریع علاقهمندند، بسیار کاربردی خواهند بود.
1) Use Goroutines Wisely
گوروتینها نوشتن کد همزمان (concurrent) را آسان میکنند، اما ایجاد تعداد زیادی از آنها میتواند منجر به مشکلات عملکردی شود. هر گوروتین فضای کوچکی دارد، اما هزاران گوروتین میتوانند مقدار قابل توجهی از حافظه را مصرف کنند:
for _, item := range items {
go process(item)
}
استفاده از Worker Pool برای مصرف بهینه حاقظه:
const numWorkers = 10
jobs := make(chan Item, len(items))
results := make(chan Result, len(items))
for w := 1; w <= numWorkers; w++ {
go worker(w, jobs, results)
}
for _, item := range items {
jobs <- item
}
close(jobs)
for i := 0; i < len(items); i++ {
result := <-results
// Do something with result
}
با استفاده از Worker Pool ها میتوانید یک اپلیکیشن بهینه تر و کارآمد تر داشته باشید.
نکته حرفه ای: با استفاده از Buffered Channels ها از بلاک شدن گوروتین ها جلوگیری کنید.
2) Avoid Unnecessary Memory Allocations
اختصاص دادن حافظه (Memory Allocation) پرهزینه است. استفادهٔ مجدد از حافظه میتواند منجر به بهبود عملکرد شود، مخصوصاً در حلقههای فشرده (tight loops). در اپلیکیشن های بزرگ این موارد خیلی میتونه تاثیر زیادی در روند یک تسک بزاره:
for i := 0; i < 1_000_000; i++ {
data := make([]byte, 1024)
// Use data
}
استفاده از buffers:
data := make([]byte, 1024)
for i := 0; i < 1_000_000; i++ {
// Reset data if necessary
// Use data
}
با استفاده از بافر ها (Buffers)، میتونید بار کاری Garbage Collector کم کنید همینطور تاخیر (Latency) هم بهبود ببخشید.
واقعا جالبه که استفادهٔ دوباره از یه اسلایس ساده میتونه اینقدر تأثیر قابلتوجهی داشته باشه.
3) Use Profiler to Find Bottlenecks
از pprof استفاده کن تا بخشهای کند کدت رو شناسایی کنی. با استفاده از این ابزار که داخل خوده گولنگ هست بخش های کند کدت رو شناسایی میکنه و میتونه پرفومنس بهتری رقم بزنی:
import (
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Your code here
}
برای پروفایل گیری از دستور زیر توی ترمینال استفاده کنید:
go tool pprof http://localhost:6060/debug/pprof/profile
نکته: ممکنه در نگاه اول به کد بگید که معلومه پرفومنس خوبی داره و کدم اوکیه، ولی به جای فکر کردن با پرفایل گیری کار رو دربیار مشتی :)
4) Minimize Garbage Collection Impact
Garbage Collector در Go ممکنه باعث توقف موقت برنامهات بشه. کاهش تعداد اختصاصهای حافظه میتونه بار اضافهی GC رو به حداقل برسونه:
برای استفاده مجدد از آبجکت، از sync.Pool
استفاده کن:
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
// Use buf
bufPool.Put(buf)
5) Optimize String Operations
رشتهها (Strings) در گولنگ، اسلایسهای بایت غیرقابل تغییر (immutable) هستند. زمان استفاده آنها در حلقه ها باید مواظب بود و گرنه پرفومنس داستان دار میشه:
var result string
for _, s := range strings {
result += s
}
از ماژول strings خوده گولنگ استفاده کنید:
var builder strings.Builder
for _, s := range strings {
builder.WriteString(s)
}
result := builder.String()
این روش از اختصاص و کپیهای غیرضروری جلوگیری میکنه. با استفاده از این کار پرفومنس خیلی خوبی نسبت به قبل دریافت میکنید.
6) Use Appropriate Data Structures
انتخاب ساختار داده مناسب میتونه تأثیر زیادی داشته باشه. برای جستجوهای سریع، از map استفاده کن:
itemsMap := make(map[string]Item)
for _, item := range items {
itemsMap[item.ID] = item
}
if item, exists := itemsMap["desired_id"]; exists {
// Use item
}
7) Limit Mutex Contention
وقتی برای همزمانسازی از mutex استفاده میکنی، رقابت زیاد (high contention) میتونه باعث کاهش عملکرد بشه:
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
defer mu.Unlock()
counter++
}
به جای این کار از atomic operation ها استفاده کن:
var counter int64
func increment() {
atomic.AddInt64(&counter, 1)
}
استفاده از atomic operations میتونه توی برنامههایی با همزمانی بالا، عملکرد رو بهطرز چشمگیری بهتر کنه.
8) Use copy() function
در مواقعی که نیاز به کپی کردن یک slice داری، استفاده از تابع داخلی copy
میتونه خیلی سریعتر از حلقه دستی باشه — هم به لحاظ عملکرد و هم خوانایی کد:
dst := make([]int, len(src))
for i := 0; i < len(src); i++ {
dst[i] = src[i]
}
استفاده از copy:
dst := make([]int, len(src))
copy(dst, src)
تابع copy()
در سطح پایینتر به زبان اسمبلی پیادهسازی شده و بسیار بهینه است.
اولین نفر باش که نظر ثبت میکنی :) یعنی یه کامنت به ما نمیرسه 😁