CodeBlanks Logo Icon CodeBlanks

Öbek ayırıcı

Kendi bellek ayırıcını unsafe ile yazarsın.

Bitirme görevi: öbek ayırıcı

Uzman seviyenin ilk bitirme görevi: programın tüm heap ayırmalarını sayan bir küresel ayırıcı. GlobalAlloc trait'i iki zorunlu metot ister: alloc ve dealloc. Sistem ayırıcısını sarıp her çağrıda atomik sayaçları güncelleyeceğiz:

use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};

struct Sayan;
static ADET: AtomicUsize = AtomicUsize::new(0);
static BAYT: AtomicUsize = AtomicUsize::new(0);

unsafe impl GlobalAlloc for Sayan {
    unsafe fn alloc(&self, l: Layout) -> *mut u8 {
        ADET.fetch_add(1, Ordering::Relaxed);
        BAYT.fetch_add(l.size(), Ordering::Relaxed);
        unsafe { System.alloc(l) }
    }
    unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
        unsafe { System.dealloc(p, l) }
    }
}

#[global_allocator]
static AYIRICI: Sayan = Sayan;

Bir bloğu ölçmek

Sayaçların bloktan önceki ve sonraki farkı, o bloğun ayırmalarını verir. Ölçüm sırasında yazdırma yapmamak önemlidir, çünkü println! de ayırma yapabilir:

let (a0, b0) = (ADET.load(Ordering::Relaxed), BAYT.load(Ordering::Relaxed));
{
    let v: Vec<u8> = Vec::with_capacity(100);
    std::hint::black_box(&v);
}
let (a1, b1) = (ADET.load(Ordering::Relaxed), BAYT.load(Ordering::Relaxed));
println!("{} {}", a1 - a0, b1 - b0);   // 1 100

Bu teknik, sıcak döngülerdeki gereksiz ayırmaları bulmak için kullanılır; dhat crate'i aynı fikrin ayrıntılı bir sürümüdür.

Uygulama Sayan ayırıcı

Yukarıdaki Sayan ayırıcısını yaz ve kaydet. Vec::<u8>::with_capacity(100) oluşturan bir bloğun ayırma sayısını ve bayt miktarını, ölçüm sırasında yazdırma yapmadan hesapla ve boşlukla yazdır.

Başlangıç kodu

main.rs
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};

fn main() {
    // buraya yaz
}

Örnek çözüm

main.rs
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};

struct Sayan;

static ADET: AtomicUsize = AtomicUsize::new(0);
static BAYT: AtomicUsize = AtomicUsize::new(0);

unsafe impl GlobalAlloc for Sayan {
    unsafe fn alloc(&self, l: Layout) -> *mut u8 {
        ADET.fetch_add(1, Ordering::Relaxed);
        BAYT.fetch_add(l.size(), Ordering::Relaxed);
        // SAFETY: sözleşme çağırandan System'e aynen aktarılır.
        unsafe { System.alloc(l) }
    }
    unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
        // SAFETY: p, aynı layout ile System'den alınmıştı.
        unsafe { System.dealloc(p, l) }
    }
}

#[global_allocator]
static AYIRICI: Sayan = Sayan;

fn main() {
    let (a0, b0) = (ADET.load(Ordering::Relaxed), BAYT.load(Ordering::Relaxed));
    {
        let v: Vec<u8> = Vec::with_capacity(100);
        std::hint::black_box(&v);
    }
    let (a1, b1) = (ADET.load(Ordering::Relaxed), BAYT.load(Ordering::Relaxed));
    println!("{} {}", a1 - a0, b1 - b0);
}

Beklenen çıktı

1 100

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 Ayırıcının içinde <code>println!</code> ya da <code>Vec</code> kullanmak neden tehlikelidir?

  • Onlar da ayırma yapabilir; ayırıcı kendini özyinelemeli çağırıp kilitlenir ya da taşar Doğru cevap
  • Yavaştır
  • Derlenmez
  • Tehlikeli değildir

Açıklama: Ayırıcı yalnızca ayırma gerektirmeyen işlemler yapmalı: atomik sayaçlar ideal.

2 Küresel ayırıcıyı kaydet

Soru
#[____]
static AYIRICI: Sayan = Sayan;
Cevap
#[global_allocator]
static AYIRICI: Sayan = Sayan;

Açıklama: #[global_allocator] programın tüm heap ayırmalarını bu değere yönlendirir.

3 Öğe ve görevi

Kavram Anlamı
GlobalAlloc Ayırıcı trait'i
Layout Boyut ve hizalama
System İşletim sistemi ayırıcısı
#[global_allocator] Kayıt

Uzman bölümündeki tüm dersler