【问题标题】:Reading large files on Linux vs Windows (mmap vs CreateFileMapping/MapViewOfFile)在 Linux 与 Windows 上读取大文件(mmap 与 CreateFileMapping/MapViewOfFile)
【发布时间】:2021-08-29 11:41:50
【问题描述】:

我必须从一个大文件(超过 7GB)中逐行读取一些数据,它包含一个顶点坐标列表和面到顶点连接信息以形成一个网格。我还在学习如何在 Linux 上使用 openmmap 和在 Windows 上使用 CreateFileACreateFileMappingMapViewOfFile。 Linux 和 Windows 版本都是 64 位编译的。

当我使用g++-10 test.cpp -O3 -std=c++17 在 Linux(使用 docker)上时,我大约需要 6 秒。 当我在 Windows(我的实际 PC)上使用(版本 19.29.30037 x64)cl test.cpp /EHsc /O3 /std:c++17 时,我得到 13 秒,而使用 clang++-11(来自 Visual Studio 构建工具)我得到 11 秒。

两个系统(同一台 PC,但一个使用 docker)使用相同的确切代码,除了生成表示内存阵列的 const char* 和表示内存大小的 uint64_t 大小。

这是我切换平台的方式:

// includes for using a specific platform API
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// using windows handle void*
#define handle_type HANDLE
#else
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
// using file descriptors
#define handle_type int
#endif

具体在char-s数组中获取内存的代码是:

using uint_t = std::size_t;

// open the file -----------------------------------------------------------------------------
handle_type open(const std::string& filename) {
#ifdef _WIN32
  // see windows file mapping api for parameter explanation
  return ::CreateFileA(filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // private access
#else
  return ::open(filename.c_str(), O_RDONLY);
#endif
}


// get the memory size to later have a bound for reading -------------------------------------
uint_t memory_size(handle_type fid) {
#ifdef _WIN32
  LARGE_INTEGER size{};
  if (!::GetFileSizeEx(fid, &size)) {
    std::cerr << "file not found\n";
    return size.QuadPart;
  }
  return size.QuadPart;
#else
  struct stat sb;
  // get the file stats and check if not zero size
  if (fstat(fid, &sb)) {
    std::cerr << "file not found\n";
    return decltype(sb.st_size){};
  }
  return sb.st_size;
#endif
}

// get the actual char array to access memory ------------------------------------------------
const char* memory_map(handle_type fid, uint_t memory_size) {
#ifdef _WIN32
  HANDLE mapper = ::CreateFileMapping(fid, NULL, PAGE_READONLY, 0, 0, NULL);
  return reinterpret_cast<const char*>(::MapViewOfFile(mapper, FILE_MAP_READ, 0, 0, memory_size));
#else
  return reinterpret_cast<const char*>(::mmap(NULL, memory_size, PROT_READ, MAP_PRIVATE, fid, 0));
#endif
}

我对这种解析完全陌生,想知道我在选择 Windows API 中的参数时是否做错了(模仿 mmap 的行为),或者时间差异是否与编译器/系统有关并且必须接受?

实际打开时间、获取内存大小和内存映射在 Linux 和 Windows 上都可以忽略不计,其余代码相同,因为它仅使用 const char*size_t 信息运行。

感谢您抽出宝贵时间阅读。非常感谢任何提示,如果有任何不清楚的地方,我们深表歉意。

【问题讨论】:

  • 你的“问题”中没有问题。
  • 你说得对,我忘记了'?',现在更正了。
  • 在语句中添加问号不会产生问题。如果您想知道您的代码是否有问题,那么接受一个归结为 “使用我在互联网上找到的另一个随机库,让我们假装 POSIX 并非无用”的答案 是错误的动作。请拨打tour 了解这个地方的运作方式。
  • 感谢您花时间看这个,我不接受这个答案,因为它只是一个建议,问题正是你所说的。我想知道我是否对 windows 中的参数做错了什么来模仿 mmap 的行为。

标签: c++ linux windows c++17 file-mapping


【解决方案1】:

也许您应该看看https://github.com/alitrack/mman-win32,它是 Windows 的 mmap 实现。这样您就无需为 Windows 编写不同的代码。

【讨论】:

    最近更新 更多