STATUS_HEAP_CORRUPTION после std::slice::from_raw_parts_mut

Я пытаюсь обернуть Rustfft в функции, которые я могу использовать через привязки из другого языка. Чтобы добиться этого, мне нужны две функции: одна для создания плана, другая для запуска fft. Чтобы упростить обмен данными и генерацию привязки, я хочу, чтобы одним из параметров был список f64. Я обнаружил, что при копировании входных и выходных данных существует, по крайней мере, некоторое снижение производительности, поэтому я считаю, что было бы полезно позволить ржавчине запускать переданные данные на месте.

Вот мой код на данный момент. При завершении основного процесса выдается ошибка STATUS_HEAP_CORRUPTION, но я не смог понять, почему я получаю эту ошибку и что я сделал не так.

use rustfft::{Fft, FftPlanner};

fn main() {
    const SIZE: usize = 8;
    let fft = create_fft_plan(SIZE);

    let mut buffer_raw = vec![0.0f64; SIZE * 2];
    buffer_raw[0] = 1.0;
    println!("Raw buffer before fft: {:?}", buffer_raw);
    run_fft(&fft, &mut buffer_raw);
    println!("Raw buffer after fft: {:?}", buffer_raw);
}

pub fn create_fft_plan(size: usize) -> std::sync::Arc<dyn Fft<f64>> {
    let mut planner = FftPlanner::new();
    let fft = planner.plan_fft_forward(size);
    fft
}

pub fn run_fft(fft: &std::sync::Arc<dyn Fft<f64>>, buffer: &mut [f64]) {
    // We want to modify buffer in-place for performance reasons: This function is likely 
    // to be called very often with big buffers. Vec<f64> and Vec<Complex<f64>> have the same
    // layout in memory as far as I found out so far.
    unsafe {
        let buffer_slice = workaround_transmute_mut(buffer);
        fft.process(buffer_slice);
    };
}

// This function was taken from rustfft-6.2.0\src\array_utils.rs
unsafe fn workaround_transmute_mut<T, U>(slice: &mut [T]) -> &mut [U] {
    let ptr = slice.as_mut_ptr() as *mut U;
    let len = slice.len();
    std::slice::from_raw_parts_mut(ptr, len)
}

Результат, который я получаю, показывает действительный результат БПФ:

Компиляция Rust_fft_test v0.1.0 (C:\Users\Markus\dev\rust\rust_fft_test) Выполнены цели профиля dev [неоптимизированный + debuginfo] за 1,45 с. Запуск target\debug\rust_fft_test.exe необработанного буфера перед fft: [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] Необработанный буфер после fft: [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0] ошибка: процесс не завершился успешно : target\debug\rust_fft_test.exe (код выхода: 0xc0000374, STATUS_HEAP_CORRUPTION)

  • Терминальный процесс «C:\Users\Markus.cargo\bin\cargo.exe 'run», '--package', 'rust_fft_test', '--bin', 'rust_fft_test'" прекращено с кодом выхода: -1073740940. * Терминал будет повторно использоваться задачами, нажмите любую клавишу, чтобы закрыть его.

Вы преобразуете &mut [f64] в &mut [Complex<f64>], но Complex<f64> в два раза больше f64, поэтому полученный срез достигает памяти за пределами выделения исходного среза: неопределенное поведение.

eggyal 10.07.2024 17:51

Конечно, вам очень хочется использовать bytemuck.

Chayim Friedman 10.07.2024 19:51
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
2
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Vec<f64> и Vec<Complex<f64>> имеют одинаковое расположение в памяти

Это не верно. Complex действительно является #[repr(C)], поэтому его расположение гарантировано, но оно содержит два f64, поэтому его размер вдвое больше одного.

Вам необходимо отрегулировать длину:

unsafe fn workaround_transmute_mut(slice: &mut [f64]) -> &mut [Complex<f64>] {
    let ptr = slice.as_mut_ptr() as *mut U;
    let len = slice.len() / 2;
    std::slice::from_raw_parts_mut(ptr, len)
}

Это автоматически пропустит последний элемент, если длина нечетная.

Или лучше используйте bytemuck, чтобы сделать это безопасно. num-complex имеет интеграцию с ним, но вам необходимо включить функцию bytemuck:

num-complex = { version = "0.4.6", features = ["bytemuck"] }
bytemuck = "1.16.1"
fn workaround_transmute_mut(slice: &mut [f64]) -> &mut [Complex<f64>] {
    bytemuck::cast_slice_mut(slice)
}

Это вызовет панику, если длина будет нечетной. Есть и ошибочный вариант.

Другие вопросы по теме