برنامه نویسی

پیش تخصیص ساختارهای پویا – انجمن DEV

معرفی

ما در زمان توسعه نرم افزار زیبا زندگی می کنیم، جایی که هر زبان دارای ساختارهای پویا است. بنابراین، ما می توانیم فقط به مقداری تخصیص حافظه فکر کنیم.
تفاوت بین عملکرد توسعه به طور قابل توجهی بین زبان های تایپ ایستا و پویا در حال کاهش است.

ما می‌توانیم روی Go، Kotlin، Java و غیره کد بنویسیم، تقریباً مانند زبان‌های تایپ استاتیک مانند Python، JavaScript، PHP، و غیره. قدم کوچک و آسان

این مقاله کوتاه توضیح خواهد داد که چرا پیش تخصیص مفید است، حتی برای ساختارهای پویا.

ساده سازی در زبان های تایپ ایستا

تفاوت قابل توجه بین زبان های تایپ ایستا و زبان های تایپ پویا عمدتاً بر اساس سه تفاوت اصلی است:

  1. مدیریت حافظه
  2. پرحرفی
  3. تبدیل نوع

در مقاله‌های آینده در مورد قسمت‌های تبدیل نوع و پرحرفی بحث خواهد شد. با این حال، مدیریت حافظه موضوعی است که در حال حاضر به آن خواهیم پرداخت.

مدیریت حافظه

وقتی زبان‌های پویا درگیر بودند، همه زبان‌های استاتیک به مدیریت مشترک حافظه به صورت دستی نیاز داشتند. هر توسعه دهنده ای باید اندازه آرایه، تغییر اندازه، تمیز کردن حافظه و غیره را در نظر می گرفت.

در حال حاضر، هر زبانی (از جمله C++) راهی برای جلوگیری از مدیریت حافظه تقریباً یا به طور کامل به این روش ها ارائه می دهد:

  • شمارنده های مرجع
  • زباله جمع کن

بنابراین، اگر ما درست انجام دهیم، می توانیم از برخی استفاده کنیم slice1، ArrayList، vector (ساختارهای مشابه از زبان های مختلف) و غیره برای کار با آرایه های پویا.
بنابراین، در دنیای کنونی، می‌توانیم هر مقدار داده‌ای را که بخواهیم بسازیم اضافه کنیم slice1.

مثال با آرایه:

package main

import "fmt"

func main() {
    const size = 10

    arr := [size]int{}
    fmt.Println(arr) // [0 0 0 0 0 0 0 0 0 0]

    for i := 0; i < size; i++ {
        arr[i] = 333
    }

    fmt.Println(arr) // [333 333 333 333 333 333 333 333 333 333]
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مثال بالا نشان می دهد که ما یک اندازه آرایه ثابت داشتیم که نمی توانستیم عنصر یازدهم را بدون دستکاری اضافه کنیم.

مثال با ساختار داده آرایه پویا (slice1 از Go):

package main

import "fmt"

func main() {
    const size = 10

    arr := []int{}
    fmt.Println(arr) // []

    for i := 0; i < size; i++ {
        arr = append(arr, 333)
    }

    fmt.Println(arr) // [333 333 333 333 333 333 333 333 333 333]
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که می بینیم، یک برش با اندازه های صفر ایجاد کردیم. پس از آن، آن را از صفر تا 10 افزایش دادیم. پس از آن، مشکلی برای اضافه کردن یک یا صدها قطعه دیگر نداشتیم.

بهبود عملکرد

همانطور که در بالا دیدیم، ساختارهای پویا مانند slice1 کیفیت زندگی خود را بهبود بخشیده و به ما کمک کنید تا کد را با سرعتی مشابه در زبان های پویا بنویسیم.

مثال بالا مثالی را ارائه داد که در آن یک برش با اندازه 0 ایجاد کردیم و آن را تا ده عنصر اختصاص دادیم. با این حال، یک مشکل عملکرد جزئی در اینجا وجود دارد. با این حال، ما اندازه مورد انتظار را می‌دانستیم (در مورد استفاده ما، ده عنصر بود)، بنابراین می‌توانیم برش را از قبل تخصیص دهیم و عملکرد را بهبود ببخشیم.

بیایید یک معیار کوچک ایجاد کنیم تا تفاوت بین برش های از پیش تخصیص داده شده و غیر اختصاص داده شده در Go را مشاهده کنیم:

package slices_test

import (
    "testing"
)

func BenchmarkNonAllocated(b *testing.B) {
    slice := []int{}
    // run the allocate variable b.N times
    for n := 0; n < b.N; n++ {
        slice = append(slice, 333)
    }
}

func BenchmarkPreAllocated(b *testing.B) {
    slice := make([]int, 0, b.N)
    // run the allocate variable b.N times
    for n := 0; n < b.N; n++ {
        slice = append(slice, 333)
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه دویدن:

$ go test -benchmem -bench . github.com/aohorodnyk/gox/slices -benchtime=20s
goos: darwin
goarch: arm64
pkg: github.com/aohorodnyk/gox/slices
BenchmarkNonAllocated-8         1000000000              14.52 ns/op           42 B/op          0 allocs/op
BenchmarkPreAllocated-8         1000000000               3.918 ns/op           8 B/op          0 allocs/op
PASS
ok      github.com/aohorodnyk/gox/slices        19.872s
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نتیجه، می توانیم ببینیم که بهبود عملکرد ~ 3.7 برابر است.

نتیجه

این مقاله باید نحوه عملکرد برش ها، ArrayList یا ساختارهای داده مشابه را شرح دهد. سپس، پیدا کردن آن در موتور جستجوی مورد علاقه شما آسان است.

در همین حال، این مقاله به طور اجباری نشان می دهد که تخصیص اولیه به طور قابل توجهی عملکرد را بهبود می بخشد. در مقاله، ما فقط لمس کردیم آرایه های پویا، اما می توان آن را برای هر نوع مانندی اعمال کرد slice1، map (hash map)، و غیره.

برنامه های ما می توانند میلیون ها درخواست را مدیریت کنند. بنابراین اگر بتوانیم عملکرد را برای هر درخواست فقط با این بهبود بهبود دهیم، این یک گام بزرگ برای ما خواهد بود.

این بخش کوچکی از بهبود است. در مقالات آینده نیز گزینه های دیگر را مورد بحث قرار خواهیم داد.


  1. برخی از مطالب داخلی سطح بالا در مورد برش ها را می توانید در اینجا بخوانید. ↩

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید
بستن
دکمه بازگشت به بالا