es
Feedback
Gopher Academy

Gopher Academy

Ir al canal en Telegram
3 829
Suscriptores
Sin datos24 horas
+117 días
+1130 días
Archivo de publicaciones
Repost from AI
مدل ۲.۷۸ تریلیون پارامتری Kimi K3 رو فقط با C خالص روی یک CPU اجرا کردن بدون GPU، بدون PyTorch، بدون BLAS. فقط حدود ۸ گیگ رم. مهندسی‌ش دیوانه‌کننده‌ست. https://github.com/FareedKhan-dev/kimi-k3-in-c <Reza/>

درود و وقت بخیر دوستان 🌹 اگر مطالب کانال براتون مفیده، لطفاً با Boost کردن کانال از ما حمایت کنید. ❤️ با حمایت شما کمک می‌کنید کانال بیشتر دیده بشه و بتونیم محتوای بهتر و بیشتری در حوزه Golang و برنامه‌نویسی منتشر کنیم. پیشاپیش ممنون از حمایت و همراهی‌تون 🙏❤️ 👇 برای Boost کردن کانال https://t.me/boost/gopher_academy

sticker.webp0.24 KB

☝️ ویژگی‌های Go 1.26 رو از اینجا دنبال کنید. این پست به‌مرور به‌روزرسانی می‌شه و بعد از اون می‌ریم سراغ ویژگی‌های Go 1.27. و Go 1.27 هم از ۱۹ آگوست ۲۰۲۶ منتشر شده، پس بعد از تکمیل مرور 1.26، می‌تونیم بریم سراغ تغییرات نسخه جدید.

🎖Hybrid Public Key Encryption رمزنگاری ترکیبی با کلید عمومی (Hybrid Public Key Encryption) پکیج جدید crypto/hpke استاندارد Hybrid Public Key Encryption (HPKE) را مطابق با مشخصات RFC 9180 پیاده‌سازی می‌کند. این HPKE یک استاندارد نسبتاً جدید IETF برای رمزنگاری ترکیبی است. روش‌های سنتی رمزنگاری با کلید عمومی، مانند RSA، نسبتاً کند هستند و فقط می‌توانند حجم محدودی از داده را به‌صورت مستقیم رمزنگاری کنند. این HPKE با ترکیب دو نوع رمزنگاری این مشکل را برطرف می‌کند: از رمزنگاری نامتقارن (Asymmetric Cryptography)، یعنی public/private key، برای ایجاد امن یک shared secret استفاده می‌کند. سپس از رمزنگاری متقارن (Symmetric Encryption) که بسیار سریع‌تر است، برای محافظت از دادهٔ واقعی استفاده می‌کند. در نتیجه، می‌توان فایل‌ها یا پیام‌های حجیم را هم به‌صورت امن و هم با سرعت بالا رمزنگاری کرد، در حالی که مزایای امنیتی سیستم‌های مبتنی بر کلید عمومی نیز حفظ می‌شوند. بخش نامتقارن HPKE که KEM (Key Encapsulation Mechanism) نام دارد، می‌تواند هم از الگوریتم‌های سنتی، مانند الگوریتم‌های مبتنی بر elliptic curve، و هم از الگوریتم‌های جدید post-quantum مانند ML-KEM استفاده کند. این ML-KEM به‌گونه‌ای طراحی شده است که حتی در برابر کامپیوترهای کوانتومی آینده نیز امنیت خود را حفظ کند؛ کامپیوترهایی که ممکن است بتوانند رمزنگاری‌های سنتی را بشکنند.

rand.Int(rand.Reader, big.NewInt(10000))
هم می‌توان در محیط test، randomness را deterministic کرد. اگر موقتاً بخواهید رفتار قدیمی که به Reader ارائه‌شده احترام می‌گذاشت را برگردانید، می‌توانید هنگام اجرا این گزینه را تنظیم کنید:
GODEBUG=cryptocustomrand=1
البته این گزینه قرار است در یکی از نسخه‌های آیندهٔ Go حذف شود.

🎖Reader-less Cryptography این APIهای فعلی cryptography، مانند ecdsa.GenerateKey یا rand.Prime، معمولاً یک io.Reader را به‌عنوان منبع دادهٔ تصادفی دریافت می‌کنند:
// Generate a new ECDSA private key for the specified curve.
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
fmt.Println(key.D)

// Generate a 64-bit integer that is prime with high probability.
prim, _ := rand.Prime(rand.Reader, 64)
fmt.Println(prim)
این APIها تضمین نمی‌کنند که دقیقاً چگونه از byteهای تصادفی موجود در Reader استفاده خواهد شد. هر تغییری در الگوریتم‌های cryptographic داخلی می‌تواند ترتیب یا تعداد byteهایی را که از Reader خوانده می‌شوند تغییر دهد. در نتیجه، اگر کد application ــ حتی به‌اشتباه ــ به رفتار یک پیاده‌سازی مشخص در نسخهٔ X از Go وابسته باشد، ممکن است با رفتن به نسخهٔ X+1 خراب شود یا رفتار متفاوتی نشان دهد. تیم Go برای حل این مشکل تصمیم نسبتاً جسورانه‌ای گرفته است: اکنون بیشتر APIهای crypto، پارامتر io.Reader مربوط به randomness را نادیده می‌گیرند و همیشه از منبع تصادفی سیستم، یعنی crypto/internal/sysrand.Read، استفاده می‌کنند. بنابراین می‌توانید این APIها را بدون ارائهٔ Reader فراخوانی کنید و nil بدهید:
// Generate a new ECDSA private key for the specified curve.
key, _ := ecdsa.GenerateKey(elliptic.P256(), nil)
fmt.Println(key.D)

// Generate a 64-bit integer that is prime with high probability.
prim, _ := rand.Prime(nil, 64)
fmt.Println(prim)
این تغییر روی APIهای زیر در subpackageهای crypto اعمال می‌شود:
// crypto/dsa
func GenerateKey(priv *PrivateKey, rand io.Reader) error

// crypto/ecdh
type Curve interface {
  
    GenerateKey(rand io.Reader) (*PrivateKey, error)
}

// crypto/ecdsa
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error)
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error)
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error)

// crypto/rand
func Prime(rand io.Reader, bits int) (*big.Int, error)

// crypto/rsa
func GenerateKey(random io.Reader, bits int) (*PrivateKey, error)
func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey, error)
func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error)
اما ed25519.GenerateKey(rand) همچنان در صورتی که rand ارائه شود، از همان Reader استفاده می‌کند. اگر rand برابر nil باشد، این تابع به‌جای crypto/rand.Reader ــ که امکان override شدن دارد ــ از یک منبع امن داخلی برای تولید byteهای تصادفی استفاده می‌کند. تست deterministic برای cryptography برای پشتیبانی از تست‌های deterministic، یک package جدید به نام testing/cryptotest اضافه شده است که یک تابع به نام SetGlobalRandom دارد. این تابع برای مدت اجرای test، یک منبع global و deterministic برای randomness مربوط به cryptography تنظیم می‌کند:
func Test(t *testing.T) {
    cryptotest.SetGlobalRandom(t, 42)

    // All test runs will generate the same numbers.
    p1, _ := rand.Prime(nil, 32)
    p2, _ := rand.Prime(nil, 32)
    p3, _ := rand.Prime(nil, 32)

    got := [3]int64{p1.Int64(), p2.Int64(), p3.Int64()}
    want := [3]int64{3713413729, 3540452603, 4293217813}
    if got != want {
        t.Errorf("got %v, want %v", got, want)
    }
}
در این مثال، هر بار test اجرا شود، همان sequence از اعداد تولید خواهد شد. این SetGlobalRandom روی crypto/rand و همچنین تمام sourceهای implicit مربوط به cryptographic randomness در packageهای crypto/* تأثیر می‌گذارد:
func Test(t *testing.T) {
    cryptotest.SetGlobalRandom(t, 42)

    t.Run("rand.Read", func(t *testing.T) {
        var got [4]byte
        rand.Read(got[:])
        want := [4]byte{34, 48, 31, 184}
        if got != want {
            t.Errorf("got %v, want %v", got, want)
        }
    })

    t.Run("rand.Int", func(t *testing.T) {
        got, _ := rand.Int(rand.Reader, big.NewInt(10000))
        const want = 6185
        if got.Int64() != want {
            t.Errorf("got %v, want %v", got.Int64(), want)
        }
    })
}
در نتیجه حتی در کدی مانند:

🎖Secret Mode پروتکل‌های رمزنگاری مانند WireGuard و TLS ویژگی‌ای به نام Forward Secrecy (محرمانگی پیشرو) دارند. منظور این است که حتی اگر مهاجم به secrets بلندمدت، مانند private key در TLS، دسترسی پیدا کند، نباید بتواند نشست‌های ارتباطی گذشته را رمزگشایی کند. برای دستیابی به این ویژگی، ephemeral keyها، یعنی کلیدهای موقتی که برای مذاکره و برقراری نشست استفاده می‌شوند، باید بلافاصله پس از handshake از حافظه حذف شوند. اگر راه قابل‌اعتمادی برای پاک‌سازی این حافظه وجود نداشته باشد، این کلیدها ممکن است برای مدت نامحدودی در حافظه باقی بمانند. مهاجمی که بعداً به آن‌ها دسترسی پیدا کند، می‌تواند دوباره session key را استخراج کرده و ترافیک گذشته را رمزگشایی کند؛ در نتیجه Forward Secrecy عملاً از بین می‌رود. در Go، مدیریت حافظه بر عهدهٔ runtime است و runtime به‌طور معمول تضمین نمی‌کند که حافظه دقیقاً چه زمانی و چگونه پاک شود. داده‌های حساس ممکن است در heap allocationها یا stack frameها باقی بمانند و از طریق مواردی مانند core dump یا حملات مستقیم به حافظه در معرض افشا قرار گیرند. به همین دلیل، توسعه‌دهندگان کتابخانه‌های رمزنگاری گاهی مجبور می‌شوند از راهکارهای غیرقابل‌اعتماد، مثلاً با استفاده از reflect، برای صفر کردن bufferهای داخلی استفاده کنند. با این حال، ممکن است بخشی از داده همچنان در نواحی‌ای از حافظه باقی بماند که توسعه‌دهنده امکان دسترسی یا کنترل مستقیم آن‌ها را ندارد. راهکار تیم Go برای این مشکل، package جدید runtime/secret است. این package اجازه می‌دهد یک function را در secret mode اجرا کنید. پس از پایان function، runtime بلافاصله registerها و stack مورد استفادهٔ آن را پاک‌سازی و صفر می‌کند. و Heap allocationهایی که function ایجاد کرده است نیز زمانی که Garbage Collector تشخیص دهد دیگر قابل دسترسی نیستند، پاک می‌شوند.
secret.Do(func() {
    // Generate an ephemeral key and
    // use it to negotiate the session.
})
این قابلیت کمک می‌کند داده‌های حساس بیشتر از زمان موردنیاز در حافظه باقی نمانند و در نتیجه، احتمال دسترسی مهاجم به آن‌ها کاهش پیدا کند. یک مثال واقعی‌تر 🔴 فرض کنید می‌خواهیم یک session key تولید کنیم، در حالی که ephemeral private key و shared secret را تا حد امکان ایمن نگه داریم:
func DeriveSessionKey(peerPublicKey *ecdh.PublicKey) (*ecdh.PublicKey, []byte, error) {
    var pubKey *ecdh.PublicKey
    var sessionKey []byte
    var err error

    secret.Do(func() {
        privKey, e := ecdh.P256().GenerateKey(rand.Reader)
        if e != nil {
            err = e
            return
        }

        sharedSecret, e := privKey.ECDH(peerPublicKey)
        if e != nil {
            err = e
            return
        }

        sessionKey = performHKDF(sharedSecret)
        pubKey = privKey.PublicKey()
    })

    return pubKey, sessionKey, err
}
در این مثال، ephemeral private key و raw shared secret عملاً مانند «پسماند سمی» هستند: برای تولید session key ضروری‌اند، اما نگه‌داشتن آن‌ها پس از استفاده خطرناک است. اگر این مقادیر در heap باقی بمانند و مهاجم بعدها به حافظهٔ application دسترسی پیدا کند ــ مثلاً از طریق یک core dump یا آسیب‌پذیری‌ای مانند و Heartbleed ــ می‌تواند از این داده‌های میانی برای استخراج مجدد session key استفاده کند و ارتباطات گذشته را رمزگشایی کند. با قرار دادن محاسبات داخل secret.Do، هدف این است که به‌محض ایجاد session key، مواد اولیهٔ مورد استفاده برای تولید آن پاک‌سازی شوند. در نتیجه، حتی اگر سرور در آینده compromise شود، این نشست مشخصِ گذشته نباید صرفاً به‌دلیل باقی‌ماندن این secrets در حافظه قابل افشا باشد؛ این همان چیزی است که به حفظ Forward Secrecy کمک می‌کند. برای نمونه:

آی بی ام اولین سلول‌های ماژولار فوق‌سرد کامپیوتر کوانتومی جدیدش رو به هم وصل کرد؛ قدمی به سمت کامپیوتر Starling که قراره تا ۲۰۲۹ ساخته بشه. همون سالی که گوگل هم برای فاصله‌گرفتن کامل از رمزنگاری فعلیش تعیین کرده. دلیلش Q-Day‌ه؛ روزی که کامپیوترهای کوانتومی اونقدر قوی بشن که بتونن رمزنگاری بانک، پیام‌رسان‌ها و اسناد دولتی رو بشکنن. NIST از همین حالا استانداردهای رمزنگاری مقاوم در برابر کوانتوم منتشر کرده و دولت‌ها دارن زیرساخت‌هاشون رو عوض می‌کنن. بعضی دولت‌ها همین الان دارن داده‌های رمزگذاری‌شده رو جمع‌آوری و ذخیره می‌کنن، بدون این‌که بتونن بازش کنن؛ فقط منتظرن یه کامپیوتر کوانتومی به‌اندازه‌ی کافی قوی ساخته بشه تا بازش کنن. یعنی هر اطلاعاتی که قراره سال‌ها محرمانه بمونه، از همین الان در خطره. https://newsroom.ibm.com/2026-08-19-ibm-connects-its-first-modular-cryogenic-systems-in-milestone-toward-fault-tolerant-quantum-computing https://ibm.com/quantum/blog/modular-cryogenics https://cnn.com/2026/05/17/science/quantum-computing-cybersecurity-q-day https://nist.gov/news-events/news/2024/08/nist-releases-first-3-finalized-post-quantum-encryption-standards https://paloaltonetworks.com/cyberpedia/what-is-q-day <^mad/>

واقعا Zed به شکل عجیبی سبک و عالیه، با rust توسعه داده شده اما برای رندر ui از ابزار های مرسوم مثل gtk استفاده نکرده و اومده GPUI رو خودش توسعه داده و معرفی کرده، من با Claude یک آپ با rust +GPUI ایجاد کردم و نتیجه فوق العاده بود، دقیقا سبک tailwind در وب رو منتقل کرده به rust تو توسعه دسکتاپ. جدیدا هم 30 میلیون جذب سرمایه داشته، و نکته جالب هم از نظر من اینه که توسعه دهنده Zed همون دولوپر atom هست، دقیقا پروژه که ای vscode اون رو fork می‌کنه و میشه یگانه ادیتور کد برای توسعه دهنده های جهان ولی خود atom که پلتفورم، اکستنشن ها و.. رو آورد هیچ جای از مارکت نبود. حالا با zed اومده تو مارکت و وقتی من پروژه رو بررسی کردم دقیقا فهمیدم این تیم به شدت کمال‌طلب هستند دیوانه وار خوب توسعه میدن مثلا واقعا می‌تونستم از gtk استفاده کنند چه دلیلی داره ui رندر ارائه میدیم.. ب نظرم بهترین می‌خوان باشن ولی یکی میاد فورک میگیره و می‌ره! https://zed.dev/ <MAHDI S. Homeyli/>

Repost from Job
💼 به دنبال استخدام برنامه‌نویس هستید؟ یا به دنبال فرصت شغلی مناسب می‌گردید؟ 🟢 کارفرمایان: اگر به دنبال جذب برنامه‌نویس هستید، آگهی استخدام خود را برای ما ارسال کنید تا منتشر شود. 🟢 کارجویان: اگر به دنبال فرصت شغلی هستید، رزومه خود را برای ما ارسال کنید تا در صورت وجود موقعیت مناسب، به شرکت‌ها و کارفرمایان معرفی شوید. 🚀 👤 ادمین: @mrbardia72 📄 لطفاً موارد زیر را به همراه فایل PDF رزومه ارسال کنید: ✅ نام و نام خانوادگی (اجباری) ✅ سابقه کار (اجباری) ✅ محل سکونت (اجباری) ✅ امکان نقل مکان برای کار (اجباری) 🔹 لینک LinkedIn (اختیاری) 🔹 لینک GitHub (اختیاری)

🔴 دلار : ۲۰۰ هزار و ۸۰۰ تومن!🫤😳🥶🤬

یک جمله‌ی خیلی خوب برای به خاطر سپردن تفاوت بین Data Race و Race Condition در واقعه Data Race درباره‌ی "هم‌زمانی دسترسی به داده" است؛ و Race Condition درباره‌ی "وابستگی نتیجه به ترتیب اجرا" است. و نکته‌ی مهم در Go اینه که Race Detector (-race) عمدتاً Data Race را پیدا می‌کند، نه تمام Race Conditionهای منطقی برنامه.

قضیه این Jump Table چیه که توی کامپایلر گو ازش استفاده می کنن؟ تکنیک/ساختار برای اجرای سریع‌تر شاخه‌های مختلف کده، و در بحث compiler / assembly / runtime هست. خیلی ساده: فرض کن اینو داری:
switch x {
case 0:
    foo()
case 1:
    bar()
case 2:
    baz()
case 3:
    qux()
}
یک روش ساده اینه که کامپایلر کلی if یا مقایسه پشت‌سرهم بسازه:
if x == 0 → foo
else if x == 1 → bar
else if x == 2 → baz
else if x == 3 → qux
ولی اگر caseها پشت‌سرهم و متراکم باشن، می‌شه یک آرایه از آدرس مقصدها داشت:
jump_table:
    [0] → address of foo
    [1] → address of bar
    [2] → address of baz
    [3] → address of qux
بعد تقریباً این اتفاق می‌افته:
x
│
├── bounds check
│
└── jump_table[x]
          │
          ├── 0 → foo
          ├── 1 → bar
          ├── 2 → baz
          └── 3 → qux
یعنی به‌جای اینکه بگه:
x == 0 ?
x == 1 ?
x == 2 ?
x == 3 ?
مستقیم میره سراغش:
jump_table[x]
🫤چرا به درد می‌خوره؟ مهم‌ترین کاربردش سریع کردن dispatch ـه؛ یعنی وقتی یک مقدار تعیین می‌کنه کدوم مسیر باید اجرا بشه. مثلاً: switchهای بزرگ ماشین حالت (state machine) interpreterها VMها opcode dispatch بعضی parserها compiler-generated code مثلاً یک interpreter ممکنه opcode داشته باشه:
0 → ADD
1 → SUB
2 → MUL
3 → DIV
4 → LOAD
5 → STORE
به‌جای اینکه برای هر opcode کلی مقایسه انجام بده، می‌تونه از jump table استفاده کنه:
opcode = 4
    ↓
jump_table[4]
    ↓
LOAD handler
ولی همیشه استفاده نمی‌شه این نکته مهمه. اگر caseها پراکنده باشن:
switch x {
case 10:
case 1000:
case 999999:
}
ساختن jump table می‌تونه خیلی پرمصرف از نظر حافظه باشه؛ چون باید بین 10 تا 999999 کلی slot داشته باشی. در این شرایط compiler ممکنه از روش‌های دیگه مثل binary search یا مقایسه‌های زنجیره‌ای استفاده کنه. پس به‌صورت خیلی خلاصه:👇📣🕊
Jump table = تبدیل یک انتخاب بین چند مسیر به یک lookup + jump مستقیم.
و نکته جالبش اینه که وقتی توی Go داری assembly یا خروجی compiler رو نگاه می‌کنی، ممکنه چیزی ببینی که ظاهراً خیلی عجیب‌تر از switch معمولی Go باشه؛ چون compiler بر اساس تعداد و توزیع caseها تصمیم می‌گیره بهترین روش dispatch چی باشه. @gopher_academy @gopher_academy @gopher_academy @gopher_academy

goos: linux
goarch: amd64
cpu: AMD EPYC 9575F 64-Core Processor
BenchmarkAddPlain/1k-2            1517698         889.9 ns/op  13808.74 MB/s
BenchmarkAddPlain/65k-2             23448       52613 ns/op  14947.46 MB/s
BenchmarkAddPlain/1m-2               2047     1005628 ns/op  11932.84 MB/s
BenchmarkAddSIMD/1k-2            36594340          33.58 ns/op  365949.74 MB/s
BenchmarkAddSIMD/65k-2             410742        3199 ns/op  245838.52 MB/s
BenchmarkAddSIMD/1m-2               12955       94228 ns/op  127351.33 MB/s
این نتایج نشان می‌دهند که در این benchmark، نسخهٔ SIMD نسبت به پیاده‌سازی plain بهبود بسیار چشمگیری دارد؛ هرچند میزان واقعی این بهبود به workload، معماری CPU و الگوی دسترسی به حافظه وابسته است. این پکیج هنوز experimental است و برای فعال‌کردن آن باید هنگام build گزینهٔ زیر را تنظیم کنید:
GOEXPERIMENT=simd

🎖Vectorized Operations پکیج جدید simd/archsimd دسترسی به عملیات وکتوری‌شدهٔ وابسته به معماری (SIMD — Single Instruction, Multiple Data) را فراهم می‌کند. این یک پکیج low-level است که قابلیت‌های وابسته به سخت‌افزار را مستقیماً در اختیار برنامه‌نویس قرار می‌دهد. در حال حاضر فقط از پلتفرم‌های amd64 پشتیبانی می‌کند. از آنجا که معماری‌های مختلف CPU مجموعهٔ بسیار متفاوتی از عملیات SIMD دارند، طراحی یک API واحد و قابل‌حمل که روی همهٔ آن‌ها کار کند دشوار است. بنابراین تیم Go تصمیم گرفته ابتدا با یک API سطح پایین و وابسته به معماری شروع کند تا کاربران حرفه‌ای (power users) بتوانند بلافاصله به قابلیت‌های SIMD روی رایج‌ترین پلتفرم سرورها، یعنی amd64، دسترسی داشته باشند. این پکیج نوع‌های وکتوری را به‌صورت struct تعریف می‌کند؛ برای مثال: Int8x16 — یک وکتور SIMD با عرض ۱۲۸ بیت شامل شانزده عدد صحیح ۸ بیتی Float64x8 — یک وکتور SIMD با عرض ۵۱۲ بیت شامل هشت مقدار floating-point شصت‌وچهار بیتی این نوع‌ها با vector registerهای سخت‌افزار مطابقت دارند. پکیج از وکتورهایی با عرض ۱۲۸، ۲۵۶ و ۵۱۲ بیت پشتیبانی می‌کند. بیشتر عملیات به‌صورت method روی نوع‌های وکتوری تعریف شده‌اند و معمولاً مستقیماً به instructionهای سخت‌افزار نگاشت می‌شوند؛ یعنی عملاً overhead اضافی ندارند. برای دیدن نحوهٔ استفاده، تابع زیر را در نظر بگیرید که با استفاده از SIMD، دو وکتور float32 را با هم جمع می‌کند:
func Add(a, b []float32) []float32 {
    if len(a) != len(b) {
        panic("slices of different length")
    }

    // If AVX-512 isn't supported, fall back to scalar addition,
    // since the Float32x16.Add method needs the AVX-512 instruction set.
    if !archsimd.X86.AVX512() {
        return fallbackAdd(a, b)
    }

    res := make([]float32, len(a))
    n := len(a)
    i := 0

    // 1. SIMD loop: Process 16 elements at a time.
    for i <= n-16 {
        // Load 16 elements from a and b vectors.
        va := archsimd.LoadFloat32x16Slice(a[i : i+16])
        vb := archsimd.LoadFloat32x16Slice(b[i : i+16])

        // Add all 16 elements in a single instruction
        // and store the results in the result vector.
        vSum := va.Add(vb) // translates to VADDPS asm instruction
        vSum.StoreSlice(res[i : i+16])

        i += 16
    }

    // 2. Scalar tail: Process any remaining elements (0-15).
    for ; i < n; i++ {
        res[i] = a[i] + b[i]
    }

    return res
}
در این پیاده‌سازی، اگر CPU از AVX-512 پشتیبانی نکند، تابع به پیاده‌سازی scalar یعنی fallbackAdd برمی‌گردد؛ زیرا متد Float32x16.Add به instruction set مربوط به AVX-512 نیاز دارد. در حلقهٔ SIMD، در هر iteration تعداد ۱۶ عنصر به‌صورت هم‌زمان پردازش می‌شود. در نتیجه:
vSum := va.Add(vb)
هر ۱۶ مقدار را با یک instruction جمع می‌کند و این عملیات به instruction اسمبلی VADDPS نگاشت می‌شود. سپس اگر در انتهای آرایه بین ۰ تا ۱۵ عنصر باقی بماند، این عناصر در بخش scalar tail و به‌صورت معمولی پردازش می‌شوند. برای مثال، می‌توانیم آن را روی دو وکتور اجرا کنیم:
func main() {
    a := []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}
    b := []float32{17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
    res := Add(a, b)
    fmt.Println(res)
}
عملیات رایج در پکیج archsimd شامل موارد زیر است: Load / Store: بارگذاری وکتور از array/slice یا ذخیرهٔ وکتور در array/slice Arithmetic: Add، Sub، Mul، Div، DotProduct Bitwise: And، Or، Not، Xor، Shift Comparison: Equal، Greater، Less، Min، Max Conversion: As، SaturateTo، TruncateTo Masking: Compress، Masked، Merge Rearrangement: Permute این پکیج فقط از instructionهای AVX استفاده می‌کند و SSE را به کار نمی‌گیرد. Benchmark در یک benchmark ساده برای جمع دو وکتور، هر دو پیاده‌سازی plain و SIMD از sliceهای ازپیش‌تخصیص‌یافته استفاده می‌کنند: 👇👇👇👇👇

🎖Faster Memory Allocation در runtime زبان Go اکنون برای اشیای کوچک، از ۱ تا ۵۱۲ بایت، نسخه‌های تخصصی‌شده‌ای از تابع تخصیص حافظه دارد. برای انتخاب سریع تابع مناسب بر اساس اندازه، از jump table استفاده می‌شود؛ به‌جای اینکه همهٔ تخصیص‌ها صرفاً از یک پیاده‌سازی عمومی عبور کنند. در یادداشت‌های انتشار Go آمده است که «compiler اکنون فراخوانی‌هایی را برای routineهای تخصیص حافظهٔ تخصصی‌شده بر اساس اندازه تولید می‌کند». اما بر اساس کد منبع، این بیان کاملاً دقیق نیست: compiler همچنان فراخوانی‌هایی به تابع عمومی mallocgc تولید می‌کند. سپس mallocgc در runtime این فراخوانی‌ها را به توابع جدید و تخصصی‌شدهٔ تخصیص حافظه dispatch می‌کند. این تغییر، هزینهٔ تخصیص حافظه برای اشیای کوچک را تا ۳۰٪ کاهش می‌دهد. تیم Go انتظار دارد بهبود کلی در برنامه‌های واقعی که allocation سنگینی دارند، حدود ۱٪ باشد. من نتوانستم benchmark موجودی برای این تغییر پیدا کنم، بنابراین benchmark خودم را ایجاد کردم. نتیجه هم واقعاً نشان می‌دهد که اجرای آن روی Go 1.25 در مقایسه با Go 1.26 بهبود قابل‌توجهی دارد:
goos: darwin
goarch: arm64
cpu: Apple M1
           │  go1_25.txt   │              go1_26.txt              │
           │    sec/op     │    sec/op     vs base                │
Alloc1-8      8.190n ±  6%   6.594n ± 28%  -19.48% (p=0.011 n=10)
Alloc8-8      8.648n ± 16%   7.522n ±  4%  -13.02% (p=0.000 n=10)
Alloc64-8     15.70n ± 15%   12.57n ±  4%  -19.88% (p=0.000 n=10)
Alloc128-8    56.80n ±  4%   17.56n ±  4%  -69.08% (p=0.000 n=10)
Alloc512-8    81.50n ± 10%   55.24n ±  5%  -32.23% (p=0.000 n=10)
geomean       21.99n         14.33n        -34.83%
پیاده‌سازی جدید به‌صورت پیش‌فرض فعال است. اگر بخواهید آن را غیرفعال کنید، هنگام build این گزینه را تنظیم کنید:
GOEXPERIMENT=nosizespecializedmalloc
البته انتظار می‌رود این گزینه در Go 1.27 حذف شود.

🔴Faster cgo and Syscalls در runtime زبان Go، یک processor که معمولاً با P نشان داده می‌شود، منبعی است که برای اجرای کد موردنیاز است. یک thread یا machine که با M نمایش داده می‌شود، برای اجرای یک goroutine (G) ابتدا باید یک processor در اختیار داشته باشد. اProcessorها می‌توانند در وضعیت‌های مختلفی قرار بگیرند. برای مثال: running — در حال اجرای کد idle — بیکار و منتظر کار gcstop — به‌دلیل اجرای Garbage Collection متوقف شده پیش از Go 1.26، processorها وضعیت دیگری به نام syscall نیز داشتند که هنگام اجرای یک system call یا cgo call توسط یک goroutine استفاده می‌شد. اکنون این وضعیت حذف شده است. به‌جای استفاده از یک وضعیت مجزا برای processor، runtime بررسی می‌کند که goroutine اختصاص‌یافته به آن processor در چه وضعیتی است و آیا درگیر یک system call شده است یا خیر. این تغییر باعث کاهش overhead داخلی runtime و ساده‌تر شدن مسیرهای اجرایی مربوط به cgo و syscallها می‌شود. یادداشت‌های انتشار Go از ۳۰٪ کاهش overhead مربوط به cgo صحبت می‌کنند و commit مربوطه نیز حدود ۱۸٪ بهبود در sec/op را نشان می‌دهد:
goos: linux
goarch: amd64
pkg: internal/runtime/cgobench
cpu: AMD EPYC 7B13
                   │ before.out  │             after.out              │
                   │   sec/op    │   sec/op     vs base               │
CgoCall-64           43.69n ± 1%   35.83n ± 1%  -17.99% (p=0.002 n=6)
CgoCallParallel-64   5.306n ± 1%   5.338n ± 1%        ~ (p=0.132 n=6)
من هم تصمیم گرفتم benchmarkهای CgoCall را به‌صورت local اجرا کنم:
goos: darwin
goarch: arm64
cpu: Apple M1
                      │ go1_25.txt  │             go1_26.txt              │
                      │   sec/op    │   sec/op     vs base                │
CgoCall-8               28.55n ± 4%   19.02n ± 2%  -33.40% (p=0.000 n=10)
CgoCallWithCallback-8   72.76n ± 5%   57.38n ± 2%  -21.14% (p=0.000 n=10)
geomean                 45.58n        33.03n       -27.53%
در هر صورت، چه ۲۰٪ بهبود را در نظر بگیریم و چه ۳۰٪، این میزان افزایش عملکرد واقعاً قابل‌توجه است. و این هم نتایج benchmark محلی برای syscall:
goos: darwin
goarch: arm64
cpu: Apple M1
          │ go1_25.txt  │             go1_26.txt             │
          │   sec/op    │   sec/op     vs base               │
Syscall-8   195.6n ± 4%   178.1n ± 1%  -8.95% (p=0.000 n=10)
این نتیجه هم بسیار خوب است.

🔴 Green Tea Garbage Collector زباله‌جمع‌کن (Garbage Collector یا GC) جدید Go که نخستین بار در Go 1.25 به‌صورت آزمایشی معرفی شد، با هدف کارآمدتر کردن مدیریت حافظه در کامپیوترهای مدرن با تعداد زیادی هستهٔ CPU طراحی شده است. 🔵انگیزه الگوریتم سنتی GC در Go، حافظه را به‌صورت یک گراف در نظر می‌گیرد: اشیا (objects) گره‌های گراف و pointerها یال‌های آن هستند؛ بدون اینکه محل فیزیکی اشیا در حافظه در نظر گرفته شود. در نتیجه، scanner بین موقعیت‌های دور از هم در حافظه جابه‌جا می‌شود و این موضوع باعث cache missهای مکرر می‌شود. در نهایت، CPU زمان زیادی را صرف انتظار برای رسیدن داده از حافظه می‌کند. بیش از ۳۵٪ از زمان صرف‌شده برای اسکن حافظه عملاً به‌دلیل توقف و انتظار برای دسترسی‌های حافظه تلف می‌شود. با افزایش تعداد هسته‌های CPU در کامپیوترهای مدرن، این مشکل حتی شدیدتر می‌شود. 🔵پیاده‌سازی در Green Tea تمرکز GC را از رویکرد processor-centered به رویکرد memory-aware تغییر می‌دهد. به‌جای اسکن تک‌تک اشیا، حافظه در بلوک‌های پیوستهٔ ۸ KiB که span نام دارند اسکن می‌شود. الگوریتم روی اشیای کوچک، یعنی اشیایی تا ۵۱۲ بایت، تمرکز ویژه دارد؛ چون این اشیا هم رایج‌ترند و هم اسکن کارآمد آن‌ها دشوارتر است. هر span بر اساس size class (ردهٔ اندازه) تخصیص‌یافته به آن، به تعدادی slot هم‌اندازه تقسیم می‌شود و فقط شامل اشیایی از همان size class است. برای مثال، اگر یک span به size class برابر با ۳۲ بایت اختصاص داده شده باشد، کل بلوک به slotهای ۳۲ بایتی تقسیم می‌شود و اشیا مستقیماً داخل همین slotها قرار می‌گیرند؛ به‌طوری که هر شیء از ابتدای slot خودش آغاز می‌شود. به‌دلیل این layout ثابت، GC می‌تواند با استفاده از محاسبات سادهٔ آدرس (address arithmetic)، metadata مربوط به یک شیء را به‌سرعت پیدا کند، بدون اینکه لازم باشد اندازهٔ تک‌تک اشیایی را که پیدا می‌کند بررسی کند. وقتی الگوریتم به شیئی می‌رسد که باید اسکن شود، محل آن شیء را در span مربوطه علامت‌گذاری می‌کند، اما بلافاصله آن را اسکن نمی‌کند. در عوض، منتظر می‌ماند تا چند شیء در همان span نیازمند اسکن شوند. سپس وقتی GC آن span را پردازش می‌کند، چندین شیء را به‌صورت یکجا اسکن می‌کند. این روش بسیار سریع‌تر از آن است که یک ناحیهٔ یکسان از حافظه چندین بار و در زمان‌های مختلف پیمایش شود. برای استفادهٔ بهتر از هسته‌های CPU، workerهای GC بار کاری را با سازوکار work stealing میان خود توزیع می‌کنند. هر worker یک صف محلی از spanهایی دارد که باید اسکن شوند. اگر یک worker بیکار باشد، می‌تواند taskها را از صف workerهای دیگر که مشغول هستند بردارد. این معماری غیرمتمرکز (decentralized)، نیاز به یک فهرست مرکزی global را از بین می‌برد، از ایجاد تأخیر جلوگیری می‌کند و contention میان هسته‌های CPU را کاهش می‌دهد. این Green Tea همچنین در صورتی که تعداد اشیا کافی باشد، از دستورهای برداری CPU (vectorized CPU instructions) برای پردازش bulkِ spanهای حافظه استفاده می‌کند؛ این قابلیت فعلاً فقط روی معماری amd64 فعال است. در Benchmarkها نتایج benchmarkها متفاوت‌اند، اما تیم Go انتظار دارد در برنامه‌های واقعی که وابستگی زیادی به GC دارند، سربار Garbage Collection حدود ۱۰ تا ۴۰ درصد کاهش پیدا کند. علاوه بر این، در پیاده‌سازی vectorized، روی CPUهایی مانند Intel Ice Lake، AMD Zen 4 و نسل‌های جدیدتر، انتظار می‌رود حدود ۱۰٪ کاهش اضافی در سربار GC حاصل شود.
متأسفانه نتوانستم برای آخرین نسخهٔ Green Tea، benchmark عمومی‌ای از تیم Go پیدا کنم و خودم هم نتوانستم یک benchmark مصنوعی مناسب ایجاد کنم. بنابراین این بار جزئیات بیشتری در دست نیست :(
این Garbage Collector جدید به‌صورت پیش‌فرض فعال است. اگر بخواهید از GC قدیمی استفاده کنید، باید متغیر محیطی زیر را هنگام build تنظیم کنید:
GOEXPERIMENT=nogreenteagc
البته انتظار می‌رود این گزینه در Go 1.27 حذف شود.

Repost from AI
🔵 عنوان مقاله Skill packs are now available on skills.sh (1 minute read) 🟢 خلاصه مقاله: اکنون کاربران در سایت skills.sh قادر هستند مجموعه‌ای از مهارت‌های مختلف و مرتبط با نمایندگان خود را در قالب پک‌های قابل اشتراک‌گذاری گردآوری کنند. هر پک در حالت خصوصی قرار دارد و لینک مخصوص به خود را دارد که می‌توان آن را برای همکاری‌های تیمی به اشتراک گذاشت. این قابلیت به تیم‌ها کمک می‌کند تا مهارت‌ها را به صورت استاندارد و منسجم در پروژه‌های مختلف یکپارچه سازند و بهره‌وری را افزایش دهند. با این به‌روزرسانی، مدیریت و توزیع مهارت‌ها بسیار آسان‌تر و موثرتر شده است، و تیم‌ها قابلیت سفارشی‌سازی محتوا را دارند تا بهترین نتیجه را در پیاده‌سازی پروژه‌های خود بگیرند. #مهارت #توسعه_فنی #تیم_سازی #ابزارهای_هوشمند 🟣لینک مقاله: https://vercel.com/changelog/skill-packs-are-now-available?utm_source=tldrai ➖➖➖➖➖➖➖➖ 👑 @ai_Labdon