【问题标题】:fwrite more than 4GB in Visual Studio在 Visual Studio 中 fwrite 超过 4GB
【发布时间】:2017-05-09 09:00:48
【问题描述】:

我正在尝试设置我的视觉工作室,以便我可以一次fwrite 8GB。

我可以看到 malloc 通过使用监视器跟踪内存来分配 8GB。

但是,fwrite 的返回值为零,输出文件大小只有 4GB。

size_t s = fwrite(result, sizeof(unsigned int), 0x80000000, fout);

我使用的是 x64,发布模式。

还有其他我应该使用的设置吗?

【问题讨论】:

  • 您的意思是在您使用 VS 构建和调试的代码中吗?还是你在扩展 VS?
  • 用 64 位参数多次调用原生 fwrite 来实现你的 _fwrite 函数有什么问题?也许你认为如果写入 2 个 4GB 的块,它会工作得更慢?

标签: c++ c visual-studio-2017


【解决方案1】:

使用最古老的 C 运行时函数来处理大数据并没有什么意义,因为 Windows API 和 C++ 都有更好的方法来处理更大的数据,例如内存映射文件。对于 C++ 内存映射文件,boost 有几个实现。

如果你真的想用fwrite,那就拆分一下吧,因为Visual C++的fwrite实现不会一次性写大数组:

fwrite 是在 Windows WriteFile 函数之上实现的,该函数只能写入一个 DWORD 值的字节,此外在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\ 中找到它的实现中的此注释VC\crt\src\fwrite.c 。

/***
*size_t fwrite(void *buffer, size_t size, size_t count, FILE *stream) -
*       write to the specified stream from the specified buffer.
*
*Purpose:
*       Write 'count' items of size 'size' to the specified stream from
*       the specified buffer. Return when 'count' items have been written
*       or no more items can be written to the stream.
*
*Entry:
*       buffer  - pointer to user's buffer
*       size    - size of the item to write
*       count   - number of items to write
*       stream  - stream to write to
*
*Exit:
*       Returns the number of (whole) items that were written to the stream.
*       This may be less than 'count' if an error or eof occurred. In this
*       case, ferror() or feof() should be used to distinguish between the
*       two conditions.
*
*Notes:
*       fwrite will attempt to buffer the stream (side effect of the _flsbuf
*       call) if necessary.
*
*       No more than 0xFFFE bytes may be written out at a time by a call to
*       write(). Further, write() does not handle huge buffers. Therefore,
*       in large data models, the write request is broken down into chunks
*       that do not violate these considerations. Each of these chunks is
*       processed much like an fwrite() call in a small data model (by a
*       call to _nfwrite()).
*...

注意开头的部分通过调用 write() 一次最多可以写出 0xFFFE 字节 - 除了简单之外,使用大块调用它没有任何优势内存与使用较小的块进行多次调用并检查返回。

【讨论】:

  • 我不认为这是对可以发送到fwrite() 的请求的大小的限制,我认为作为实现细节,说明如此大的请求将被“分解成块” .
  • 此外,FFFEWORD,而不是 DWORD。您正在阅读 Win16 时代的 cmets。 Win32 没有“近阵列”。
  • @MSalters 微软有一段时间没有更新他们的 cmets 不是我的错!
  • @AndrewHenle 目的是指出 fwrite 不是用于单个 8gb 数组的 API,在顶部澄清了一点。
  • 我无法理解第一段。 fwrite 是 C 运行时库的一部分,所以我不确定这与 Posix 库有什么关系。 Windows API 具有内存映射文件的功能,但 C++ 没有。如果您只是要顺序写入或读取大块,则尚不清楚内存映射文件在大数据方面是否更好。这个答案的其余部分看起来不错。
【解决方案2】:

我不确定您为什么得到零返回值。每the MS documentation for fwrite()

语法

size_t fwrite(  
   const void *buffer,  
   size_t size,  
   size_t count,  
   FILE *stream   
);

为了获得 4 GB 的输出文件,您编写了零个以上的项目。你怎么知道返回值为零?

其次,尝试在一行代码中写入如此大量的内容是没有意义的 - 部分写入始终是可能的,任何大小的写入尝试都可能导致写入的数量少于该数量。而且当您一次写入千兆字节时,很可能会发生部分写入,因此无论如何您都必须编写代码来处理部分写入。性能方面,即使在超高速磁盘系统上,一旦写入大小达到兆字节范围,您将不会看到性能改进。在消费级商品系统上,一旦写入请求达到几千字节,性能就不太可能提高。

第三,如果您不调整 FILE * 对象的缓冲区大小,则它可能以 4 或 8 KB 的块进行缓冲。较大的块在大多数磁盘系统上不会带来太大的性能提升,这并不是巧合。

最后,正如@Andriy Berestovskyy 在他的回答中所说,您可能会遇到文件系统限制。看看你是否可以使用多个 fwrite() 调用来写入大于 4GB 的文件。

【讨论】:

  • “64 位 Windows 将 size_t 定义为 32 位无符号整数类型”。不,它没有。引用#ifdef _WIN64 typedef unsigned __int64 size_t;
  • “部分写入始终是可能的,任何大小的写入尝试都可能导致写入的数量少于该数量。”。确实,磁盘可能已满,这可能是暂时的情况。实际上,对于磁盘 I/O,“磁盘已满”不是临时情况。
  • “在消费级商品系统上,一旦写入请求达到几千字节,性能就不太可能提高。”。对于 NTFS,这将是 65536 KB。 FILE* 缓冲只是操作系统之前的第一个内部缓冲区,那里你确实有很大的缓冲区。小的内部缓冲区避免了仅几个字节的操作系统调用,它不是为了节省物理 I/O。
  • 除了size_t错误,这是正确的答案。我会投赞成票,希望你能解决这个问题。
  • @2501 现在正在处理这个问题。不知道那是从哪里来的?事后看来很明显,但我知道我在想 OP 如何获得零返回值,也许我在想 Windows 上的 long 仍然是 32 位。
猜你喜欢
  • 2012-11-02
  • 2010-10-16
  • 2013-10-26
  • 2015-11-11
  • 2012-03-13
  • 2013-07-30
  • 2014-10-27
  • 1970-01-01
  • 2021-07-28
相关资源
最近更新 更多