Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  buddy.rs   Sprache: unbekannt

 
use {
    crate::{
        align_up, error::AllocationError, heap::Heap, slab::Slab, unreachable_unchecked,
        util::try_arc_unwrap, MemoryBounds,
    },
    alloc::{sync::Arc, vec::Vec},
    core::{convert::TryFrom as _, mem::replace, ptr::NonNull},
    gpu_alloc_types::{AllocationFlags, DeviceMapError, MemoryDevice, MemoryPropertyFlags},
};

#[derive(Debug)]
pub(crate) struct BuddyBlock<M> {
    pub memory: Arc<M>,
    pub ptr: Option<NonNull<u8>>,
    pub offset: u64,
    pub size: u64,
    pub chunk: usize,
    pub index: usize,
}

unsafe impl<M> Sync for BuddyBlock<M> where M: Sync {}
unsafe impl<M> Send for BuddyBlock<M> where M: Send {}

#[derive(Clone, Copy, Debug)]
enum PairState {
    Exhausted,
    Ready {
        ready: Side,
        next: usize,
        prev: usize,
    },
}

impl PairState {
    unsafe fn replace_next(&mut self, value: usize) -> usize {
        match self {
            PairState::Exhausted => unreachable_unchecked(),
            PairState::Ready { next, .. } => replace(next, value),
        }
    }

    unsafe fn replace_prev(&mut self, value: usize) -> usize {
        match self {
            PairState::Exhausted => unreachable_unchecked(),
            PairState::Ready { prev, .. } => replace(prev, value),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Side {
    Left,
    Right,
}
use Side::*;

#[derive(Debug)]
struct PairEntry {
    state: PairState,
    chunk: usize,
    offset: u64,
    parent: Option<usize>,
}

struct SizeBlockEntry {
    chunk: usize,
    offset: u64,
    index: usize,
}

#[derive(Debug)]
struct Size {
    next_ready: usize,
    pairs: Slab<PairEntry>,
}
#[derive(Debug)]
enum Release {
    None,
    Parent(usize),
    Chunk(usize),
}

impl Size {
    fn new() -> Self {
        Size {
            pairs: Slab::new(),
            next_ready: 0,
        }
    }

    unsafe fn add_pair_and_acquire_left(
        &mut self,
        chunk: usize,
        offset: u64,
        parent: Option<usize>,
    ) -> SizeBlockEntry {
        if self.next_ready < self.pairs.len() {
            unreachable_unchecked()
        }

        let index = self.pairs.insert(PairEntry {
            state: PairState::Exhausted,
            chunk,
            offset,
            parent,
        });

        let entry = self.pairs.get_unchecked_mut(index);
        entry.state = PairState::Ready {
            next: index,
            prev: index,
            ready: Right, // Left is allocated.
        };
        self.next_ready = index;

        SizeBlockEntry {
            chunk,
            offset,
            index: index << 1,
        }
    }

    fn acquire(&mut self, size: u64) -> Option<SizeBlockEntry> {
        if self.next_ready >= self.pairs.len() {
            return None;
        }

        let ready = self.next_ready;

        let entry = unsafe { self.pairs.get_unchecked_mut(ready) };
        let chunk = entry.chunk;
        let offset = entry.offset;

        let bit = match entry.state {
            PairState::Exhausted => unsafe { unreachable_unchecked() },
            PairState::Ready { ready, next, prev } => {
                entry.state = PairState::Exhausted;

                if prev == self.next_ready {
                    // The only ready entry.
                    debug_assert_eq!(next, self.next_ready);
                    self.next_ready = self.pairs.len();
                } else {
                    let prev_entry = unsafe { self.pairs.get_unchecked_mut(prev) };
                    let prev_next = unsafe { prev_entry.state.replace_next(next) };
                    debug_assert_eq!(prev_next, self.next_ready);

                    let next_entry = unsafe { self.pairs.get_unchecked_mut(next) };
                    let next_prev = unsafe { next_entry.state.replace_prev(prev) };
                    debug_assert_eq!(next_prev, self.next_ready);

                    self.next_ready = next;
                }

                match ready {
                    Left => 0,
                    Right => 1,
                }
            }
        };

        Some(SizeBlockEntry {
            chunk,
            offset: offset + bit as u64 * size,
            index: (ready << 1) | bit as usize,
        })
    }

    fn release(&mut self, index: usize) -> Release {
        let side = match index & 1 {
            0 => Side::Left,
            1 => Side::Right,
            _ => unsafe { unreachable_unchecked() },
        };
        let entry_index = index >> 1;

        let len = self.pairs.len();

        let entry = self.pairs.get_mut(entry_index);

        let chunk = entry.chunk;
        let offset = entry.offset;
        let parent = entry.parent;

        match entry.state {
            PairState::Exhausted => {
                if self.next_ready == len {
                    entry.state = PairState::Ready {
                        ready: side,
                        next: entry_index,
                        prev: entry_index,
                    };
                    self.next_ready = entry_index;
                } else {
                    debug_assert!(self.next_ready < len);

                    let next = self.next_ready;
                    let next_entry = unsafe { self.pairs.get_unchecked_mut(next) };
                    let prev = unsafe { next_entry.state.replace_prev(entry_index) };

                    let prev_entry = unsafe { self.pairs.get_unchecked_mut(prev) };
                    let prev_next = unsafe { prev_entry.state.replace_next(entry_index) };
                    debug_assert_eq!(prev_next, next);

                    let entry = unsafe { self.pairs.get_unchecked_mut(entry_index) };
                    entry.state = PairState::Ready {
                        ready: side,
                        next,
                        prev,
                    };
                }
                Release::None
            }

            PairState::Ready { ready, .. } if ready == side => {
                panic!("Attempt to dealloate already free block")
            }

            PairState::Ready { next, prev, .. } => {
                unsafe {
                    self.pairs.remove_unchecked(entry_index);
                }

                if prev == entry_index {
                    debug_assert_eq!(next, entry_index);
                    self.next_ready = self.pairs.len();
                } else {
                    let prev_entry = unsafe { self.pairs.get_unchecked_mut(prev) };
                    let prev_next = unsafe { prev_entry.state.replace_next(next) };
                    debug_assert_eq!(prev_next, entry_index);

                    let next_entry = unsafe { self.pairs.get_unchecked_mut(next) };
                    let next_prev = unsafe { next_entry.state.replace_prev(prev) };
                    debug_assert_eq!(next_prev, entry_index);

                    self.next_ready = next;
                }

                match parent {
                    Some(parent) => Release::Parent(parent),
                    None => {
                        debug_assert_eq!(offset, 0);
                        Release::Chunk(chunk)
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
struct Chunk<M> {
    memory: Arc<M>,
    ptr: Option<NonNull<u8>>,
    size: u64,
}

#[derive(Debug)]
pub(crate) struct BuddyAllocator<M> {
    minimal_size: u64,
    chunks: Slab<Chunk<M>>,
    sizes: Vec<Size>,
    memory_type: u32,
    props: MemoryPropertyFlags,
    atom_mask: u64,
}

unsafe impl<M> Sync for BuddyAllocator<M> where M: Sync {}
unsafe impl<M> Send for BuddyAllocator<M> where M: Send {}

impl<M> BuddyAllocator<M>
where
    M: MemoryBounds + 'static,
{
    pub fn new(
        minimal_size: u64,
        initial_dedicated_size: u64,
        memory_type: u32,
        props: MemoryPropertyFlags,
        atom_mask: u64,
    ) -> Self {
        assert!(
            minimal_size.is_power_of_two(),
            "Minimal allocation size of buddy allocator must be power of two"
        );
        assert!(
            initial_dedicated_size.is_power_of_two(),
            "Dedicated allocation size of buddy allocator must be power of two"
        );

        let initial_sizes = (initial_dedicated_size
            .trailing_zeros()
            .saturating_sub(minimal_size.trailing_zeros())) as usize;

        BuddyAllocator {
            minimal_size,
            chunks: Slab::new(),
            sizes: (0..initial_sizes).map(|_| Size::new()).collect(),
            memory_type,
            props,
            atom_mask: atom_mask | (minimal_size - 1),
        }
    }

    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, device)))]
    pub unsafe fn alloc(
        &mut self,
        device: &impl MemoryDevice<M>,
        size: u64,
        align_mask: u64,
        flags: AllocationFlags,
        heap: &mut Heap,
        allocations_remains: &mut u32,
    ) -> Result<BuddyBlock<M>, AllocationError> {
        let align_mask = align_mask | self.atom_mask;

        let size = align_up(size, align_mask)
            .and_then(|size| size.checked_next_power_of_two())
            .ok_or(AllocationError::OutOfDeviceMemory)?;

        let size = size.max(self.minimal_size);

        let size_index = size.trailing_zeros() - self.minimal_size.trailing_zeros();
        let size_index =
            usize::try_from(size_index).map_err(|_| AllocationError::OutOfDeviceMemory)?;

        while self.sizes.len() <= size_index {
            self.sizes.push(Size::new());
        }

        let host_visible = self.host_visible();

        let mut candidate_size_index = size_index;

        let (mut entry, entry_size_index) = loop {
            let sizes_len = self.sizes.len();

            let candidate_size_entry = &mut self.sizes[candidate_size_index];
            let candidate_size = self.minimal_size << candidate_size_index;

            if let Some(entry) = candidate_size_entry.acquire(candidate_size) {
                break (entry, candidate_size_index);
            }

            if sizes_len == candidate_size_index + 1 {
                // That's size of device allocation.
                if *allocations_remains == 0 {
                    return Err(AllocationError::TooManyObjects);
                }

                let chunk_size = self.minimal_size << (candidate_size_index + 1);
                let mut memory = device.allocate_memory(chunk_size, self.memory_type, flags)?;
                *allocations_remains -= 1;
                heap.alloc(chunk_size);

                let ptr = if host_visible {
                    match device.map_memory(&mut memory, 0, chunk_size) {
                        Ok(ptr) => Some(ptr),
                        Err(DeviceMapError::OutOfDeviceMemory) => {
                            return Err(AllocationError::OutOfDeviceMemory)
                        }
                        Err(DeviceMapError::MapFailed) | Err(DeviceMapError::OutOfHostMemory) => {
                            return Err(AllocationError::OutOfHostMemory)
                        }
                    }
                } else {
                    None
                };

                let chunk = self.chunks.insert(Chunk {
                    memory: Arc::new(memory),
                    ptr,
                    size: chunk_size,
                });

                let entry = candidate_size_entry.add_pair_and_acquire_left(chunk, 0, None);

                break (entry, candidate_size_index);
            }

            candidate_size_index += 1;
        };

        for size_index in (size_index..entry_size_index).rev() {
            let size_entry = &mut self.sizes[size_index];
            entry =
                size_entry.add_pair_and_acquire_left(entry.chunk, entry.offset, Some(entry.index));
        }

        let chunk_entry = self.chunks.get_unchecked(entry.chunk);

        debug_assert!(
            entry
                .offset
                .checked_add(size)
                .map_or(false, |end| end <= chunk_entry.size),
            "Offset + size is not in chunk bounds"
        );

        Ok(BuddyBlock {
            memory: chunk_entry.memory.clone(),
            ptr: chunk_entry
                .ptr
                .map(|ptr| NonNull::new_unchecked(ptr.as_ptr().add(entry.offset as usize))),
            offset: entry.offset,
            size,
            chunk: entry.chunk,
            index: entry.index,
        })
    }

    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, device)))]
    pub unsafe fn dealloc(
        &mut self,
        device: &impl MemoryDevice<M>,
        block: BuddyBlock<M>,
        heap: &mut Heap,
        allocations_remains: &mut u32,
    ) {
        debug_assert!(block.size.is_power_of_two());

        let size_index =
            (block.size.trailing_zeros() - self.minimal_size.trailing_zeros()) as usize;

        let mut release_index = block.index;
        let mut release_size_index = size_index;

        loop {
            match self.sizes[release_size_index].release(release_index) {
                Release::Parent(parent) => {
                    release_size_index += 1;
                    release_index = parent;
                }
                Release::Chunk(chunk) => {
                    debug_assert_eq!(chunk, block.chunk);
                    debug_assert_eq!(
                        self.chunks.get(chunk).size,
                        self.minimal_size << (release_size_index + 1)
                    );
                    let chunk = self.chunks.remove(chunk);
                    drop(block);

                    let memory = try_arc_unwrap(chunk.memory)
                        .expect("Memory shared after last block deallocated");

                    device.deallocate_memory(memory);
                    *allocations_remains += 1;
                    heap.dealloc(chunk.size);

                    return;
                }
                Release::None => return,
            }
        }
    }

    fn host_visible(&self) -> bool {
        self.props.contains(MemoryPropertyFlags::HOST_VISIBLE)
    }
}

[ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge