Расположение памяти, выравнивание и освобождение - PullRequest
1 голос
/ 16 апреля 2020

Недавно я хотел выделить вектор u64 s, выровненный по 128-байтовым границам, чтобы я мог использовать функции AVX512F. Мой первый проход функции для копирования любого Vec<u64> в выровненный Vec<u64>:

fn copy_to_aligned(inp: Vec<u64>) -> Vec<u64> {
    // note: when the returned vec is freed, the alignment / layout information
    //       will be lost. The internal memory will be freed using the alignment
    //       of a u64.
    let aligned_layout = Layout::from_size_align(inp.len() * size_of::<u64>(), 128)
        .unwrap();
    let new_vec = unsafe {
        let new_vec_mem = alloc_zeroed(aligned_layout) as *mut u64;
        copy_nonoverlapping(inp.as_ptr(), new_vec_mem, inp.len());
        Vec::from_raw_parts(new_vec_mem, inp.len(), inp.len())
    };

    return new_vec;
}

Теперь я понимаю, что этот код неверен: пока новый вектор создается с выровненной памятью и соответствующим содержимым при отбрасывании нового вектора память будет освобождена с выравниванием u64. Документация для from_raw_parts также упоминает эту ловушку.

1) Почему выравнивание распределения имеет значение, когда я освобождаю это распределение? Например, в C я могу освободить любой указатель, просто вызвав free - выравнивание распределения не имеет значения. Что особенного в распределителе Rust, что ему нужно знать выравнивание?

2) Как правильно распределить этот выровненный вектор? Должен ли я использовать срез в штучной упаковке? Напишите мою собственную структуру с пользовательской реализацией Drop, которая дает правильное выравнивание? Другие источники предлагают создать структуру и указать выравнивание, а затем создать Vec из этих структур (в результате чего для перераспределения будет использоваться правильный макет). Но даже с repr(C) это не гарантирует, что все мои u64 s будут красиво и плотно упакованы рядом друг с другом, не так ли? Я полагаю, что произвольный отступ может быть добавлен в структуру.

1 Ответ

2 голосов
/ 26 апреля 2020

Мне удалось выяснить это с некоторой помощью пользователей Rust Discord svr и seri.

По сути, распределитель для Rust API предполагает, что для обоих распределений предусмотрено Layout и освобождение. Это означает, что распределитель памяти узнает запрошенное выравнивание выделения во время освобождения. Вы можете найти API в книге Rust .

. Этот API можно использовать для хранения чего-либо, например, для выделения памяти для различных выравниваний в разных пулах. Как выясняется, текущая реализация по умолчанию *1013* не использует эту информацию во время освобождения , но это может измениться в будущем.

В настоящее время наилучшим (только?) способ выделить вектор из u64 s, выровненный по N-байтовой границе, - создать структуру с одним значением выравнивания из u64 в качестве полей, а затем использовать подсказку выравнивания, как описано здесь: Как мне выделить Ve c что выровнено по размеру строки кэша?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...