基本上,当遵循《Rust in Action:System's Programming Concepts And Techniques》一书时,该material 实现了一个自定义分配器(它基本上调用System allocator,但打印出分配时间等).我想我可以修改自定义分配器将分配时间写入文件,这样我就可以用Pandas做一些EDA,但这似乎失败了,因为我现在已经得到了上述错误.

#[global_allocator]
static ALLOCATOR: ReportingAllocator = ReportingAllocator;
struct ReportingAllocator;

unsafe impl GlobalAlloc for ReportingAllocator{
    unsafe fn alloc(&self, layout : Layout) -> *mut u8{
        let start = Instant::now();
        let ptr = System.alloc(layout);
        let end = Instant::now();
        let time_taken = end - start;
        let bytes_requested = layout.size();
        //TODO: convert data to strings and pass to func
        let str_bytes = format!("{}", bytes_requested);
        let str_time = format!("{}", time_taken.as_nanos());
        log_data(str_bytes, str_time);
        // eprintln!("{}\t{}", bytes_requested, time_taken.as_nanos());
        return ptr;
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) -> (){
        System.dealloc(ptr, layout);
    }
}

函数日志(log)数据如下:


fn log_data(mem: String, time: String){
    let mut test = match File::options().append(true).open("src/log_data.csv"){
        Ok(var) => var,
        Err(_) => panic!("File could not be opened in read only mode!")
    };
    writeln!(&mut test, "{}, {}", mem, time).expect("Unsuccessful Write");
}

该文件是在main函数中创建的.

所以现在我不知道如何仍然写入文件,并避免错误

推荐答案

format!()创建一个新的String,这意味着分配.因此,您递归地调用分配器,直到获得堆栈溢出.

有多种方法可以解决这个问题:使用预分配的缓冲区并确保不分配,直接使用系统分配器进行分配,调用libc分配器...

你也不能在分配器内部panic ,因为it's UB if the allocator unwinds.您也不能在内部打印,因为运行库可能会在启动时在打印准备好之前进行分配.

一般来说,自定义分配器需要对它们调用的代码非常谨慎.我不认为你可以安全地使用stdalloc中的任何东西,只有core或直接拨打libc.

以下是一个如何使用libc和堆栈缓冲区而不是分配API来安全地完成此操作的示例:

unsafe impl GlobalAlloc for ReportingAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        let start = Instant::now();
        let ptr = System.alloc(layout);
        let end = Instant::now();
        let time_taken = end - start;
        let time_taken = time_taken.as_nanos();
        let mut io_buffer = [0u8; 100];
        let mut io_buffer_tmp = &mut io_buffer[..];
        let bytes_requested = layout.size();
        // Do not `unwrap()` here!
        _ = writeln!(io_buffer_tmp, "{}, {}", time_taken, bytes_requested);
        let spare_len = io_buffer_tmp.len();
        let written_len = io_buffer.len() - spare_len;
        let io_buffer = &io_buffer[..written_len];

        let file = libc::fopen(
            b"src/log_data.csv\0".as_ptr().cast::<libc::c_char>(),
            "a\0".as_ptr().cast::<libc::c_char>(),
        );
        if !file.is_null() {
            libc::fwrite(
                io_buffer.as_ptr().cast::<libc::c_void>(),
                std::mem::size_of::<u8>(),
                io_buffer.len(),
                file,
            );
            libc::fclose(file);
        }

        return ptr;
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) -> () {
        System.dealloc(ptr, layout);
    }
}

当然,更有效的方法是缓存文件句柄,而更有效的方法是将数据存储在内存中,并且只在一段时间内将其批量写入文件.

Rust相关问答推荐

为什么父作用域中的变量超出了子作用域

空字符串转换为Box字符串时是否分配?<>

如何从使用mockall模拟的方法中返回self?

在泛型 struct 的字段声明中访问关联的Conant

为潜在的下游实现使用泛型绑定而不是没有泛型绑定的trait

S,一般性状和联想型性状有什么不同?

如何在递归数据 struct 中移动所有权时变异引用?

在macro_rule中拆分模块和函数名

允许 rust 迹 struct 条目具有多种类型

关于使用平面图功能的borrow 判断器的问题

如何将带有嵌套borrow /NLL 的 Rust 代码提取到函数中

为什么要这样编译?

一个函数调用会产生双重borrow 错误,而另一个则不会

以 `static` 为前缀的闭包是什么意思?我什么时候使用它?

如何将 Rust 中的树状 struct 展平为 Vec<&mut ...>?

n 个范围的笛卡尔积

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

字符串切片的向量超出范围但原始字符串仍然存在,为什么判断器说有错误?

您如何使用枚举反序列化字符串,其中任何其他值反序列化为新类型变体同时保留字符串?

覆盖类型的要求到底是什么?为什么单个元素元组满足它?