返回

Windows 大型稀疏数组创建指南:高效利用内存

windows

Windows 大型稀疏数组的创建

开发者经常需要创建大型数组来存储数据。但在处理稀疏数据(大部分元素为零)时,分配完整的内存空间显得非常浪费。 在 Linux 中,mmap 可以轻松创建巨大的稀疏数组,只在写入时分配物理内存。如何在 Windows 上实现类似的功能呢?这是一个许多开发者都会遇到的问题。

VirtualAlloc 的误区

许多开发者第一时间想到的可能是 VirtualAlloc。 你可能尝试过保留一大块虚拟地址空间,而没有实际提交物理内存。 但是,直接读取未提交的内存会导致访问冲突,而不是返回零。这是因为未提交的内存页没有关联的物理存储。 如果你提交了内存,则会在读取时分配物理存储,失去了稀疏数组的优势。

CreateFileMapping 的限制

另一个常见的选择是 CreateFileMappingMapViewOfFile。这个方法通过创建一个内存映射文件来模拟稀疏数组。 但正如你所发现的,这个方法会占用大量的临时磁盘空间,并且数组大小受限于可用磁盘空间,难以创建真正的大型稀疏数组。

稀疏文件的解决方案:更优的选择

其实,Windows 提供了一个更优雅的解决方案:稀疏文件 (Sparse Files) 。 稀疏文件允许你创建一个很大的文件,但实际占用的磁盘空间只与文件中非零数据的大小相关。结合内存映射视图,就可以在 Windows 上高效地实现大型稀疏数组。 你只需要在创建文件时设置 FILE_FLAG_SPARSE_FILE 标志,然后像往常一样使用 CreateFileMappingMapViewOfFile 即可。

下面是使用稀疏文件创建大型稀疏数组的代码示例:

#include <windows.h>
#include <iostream>

const size_t MB = 1024 * 1024;
const size_t GB = MB * 1024;

int main() {
    size_t alloc_size = 512 * MB;
    size_t total_alloc = 100 * GB;


    HANDLE hFile = CreateFile(L"sparse_array.dat", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SPARSE_FILE , NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "CreateFile failed: " << GetLastError() << std::endl;
        return 1;
    }


    HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, static_cast<DWORD>(total_alloc), NULL);
    if (hMapping == NULL) {
        std::cerr << "CreateFileMapping failed: " << GetLastError() << std::endl;
        CloseHandle(hFile);
        return 1;
    }


    char* base = (char*)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, total_alloc);
    if (base == NULL) {
        std::cerr << "MapViewOfFile failed: " << GetLastError() << std::endl;
        CloseHandle(hMapping);
        CloseHandle(hFile);
        return 1;
    }

    std::cout << "Allocated Virtual Mem (GB): " << total_alloc / GB << "\n";
    std::cout << "Base Addr: " << (void*)base << "\n";

    // 使用稀疏数组 (例如写入一些数据)
    for (size_t i = 0; i < total_alloc; i += (1 * GB)) {  // 每 GB 写入一次
        base[i] = 1; //  只修改少量数据,保持稀疏性
    }

    UnmapViewOfFile(base);
    CloseHandle(hMapping);
    CloseHandle(hFile);


    std::cout << "Done!" << std::endl;
    return 0;

}

操作步骤:

  1. 使用 CreateFile 创建文件,并使用 FILE_FLAG_SPARSE_FILE 标志。
  2. 使用 CreateFileMapping 创建文件映射对象。
  3. 使用 MapViewOfFile 将文件映射到内存。
  4. 现在你可以像使用普通数组一样使用 base 指针访问稀疏数组了。
  5. 完成后,使用 UnmapViewOfFileCloseHandle 关闭句柄释放资源。

安全建议

使用稀疏文件时,需要注意以下几点:

  • 及时关闭文件和映射对象的句柄,释放系统资源。
  • 谨慎处理访问权限,防止数据泄露或被篡改。
  • 确保有足够的磁盘空间来存储非零数据。

其他的解决方案和建议?

除了稀疏文件,你还可以考虑使用其他方法来管理大型稀疏数据,例如使用专门的稀疏矩阵库或者数据库。每个方案都有其优缺点,选择合适的方案取决于你的具体需求。你还有其他更好的建议吗?不妨在评论区分享你的经验!

相关资源

这个方法对你有帮助吗?希望这篇文章能帮助你更好地理解如何在 Windows 上创建和使用大型稀疏数组!