最近,我遇到了一个关于微软实现CRTL的“有趣”问题tmpfile
将临时文件放在根目录中,并完全忽略临时文件目录这对于没有根目录权限的用户(例如,在我们的集群上)有问题。此外,使用_tempnam
将需要应用程序记住删除临时文件,如果不进行大量的返工,则无法执行此操作。
因此,我咬紧牙关,编写了所有IO例程(create_temp、read、write、seek、flush)的Win32版本,这些例程调用适当的方法。我注意到的一件事是图书馆现在糟糕的表现。
测试套件的结果:
CRTL: 4:30.05 elapsed
Win32: 11:18.06 elapsed
Stats measured in my routines:
Writes: 3129934 ( 44,642,745,008 bytes)
Reads: 935903 ( 8,183,423,744 bytes)
Seeks: 2205757 (2,043,782,657,968 bytes traveled)
Flushes: 92442
CRTL v.Win32方法的示例:
int io_write(FILE_POINTER fp, size_t words, const void *buffer)
{
#if !defined(USE_WIN32_IO)
{
size_t words_written = 0;
/* read the data */
words_written = fwrite(buffer, sizeof(uint32_t), words, fp);
if (words_written != words)
{
return errno;
}
}
#else /* !defined(USE_WIN32_IO) */
{
DWORD bytesWritten;
if (!WriteFile(fp, buffer, words * sizeof(uint32_t), &bytesWritten, NULL)
|| (bytesWritten != words * sizeof(uint32_t)))
{
return GetLastError();
}
}
#endif /* USE_WIN32_IO */
return E_SUCCESS;
}
如您所见,它们实际上是相同的,但性能(在发布模式下)却大相径庭。花在
WriteFile
和SetFilePointer
上的时间使花在fwrite
和fseeko
上的时间相形见绌,这似乎违反直觉。思想?
更新:perfmon注意到
fflush
比FlushFileBuffers
便宜约10倍,fwrite
比WriteFile
慢约1.1倍最终的结果是巨大的性能损失,FlushFileBuffers
的使用方式与fflush
相同。从FILE_ATTRIBUTE_NORMAL
到FILE_FLAG_RANDOM_ACCESS
也没有变化。 最佳答案
我认为这可能是由于这个问题,在MSDN的FlushFileBuffers
页面上描述:
由于磁盘缓存交互
在系统内
FlushFileBuffers函数可以是
每次使用后效率低下
写入磁盘驱动器设备时
正在单独执行写入操作。
如果应用程序正在执行
多次写入磁盘,还需要
确保关键数据写入
持久介质,应用程序
应该使用无缓冲I/O而不是
频繁调用FlushFileBuffers。
要为无缓冲I/O打开文件,
使用调用CreateFile函数
文件标志没有缓冲和
文件标志通过标志写入这个
防止文件内容
缓存元数据并将其刷新到
每次写入的磁盘更多
有关信息,请参见CreateFile。
一般来说,FlushFileBuffers
是一个“昂贵的”操作,since it flushes everything in the write-back cache:
FlushFileBuffers():此函数将刷新回写缓存中的所有内容
不知道缓存的哪个部分属于您的文件这需要很多时间,
取决于缓存大小和媒体速度有必要吗有
一个通过并写出脏页的线程,因此它可能不太
必要的。
我假设fflush
不会刷新整个写回缓存。在这种情况下,它的效率要高得多,但这种效率存在潜在的数据丢失风险CRT的fflush
源代码证实了这一点,因为_commit
调用FlushFileBuffers
:
/* lowio commit to ensure data is written to disk */
if (str->_flag & _IOCOMMIT) {
return (_commit(_fileno(str)) ? EOF : 0);
}
从
_commit
的实施:if ( !FlushFileBuffers((HANDLE)_get_osfhandle(filedes)) ) {
retval = GetLastError();
}