0

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

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

两个系统(同一台 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信息进行操作。

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

4

1 回答 1

0

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

于 2021-06-13T11:30:35.687 回答