代码之家  ›  专栏  ›  技术社区  ›  user7116

Win32 IO性能问题

  •  1
  • user7116  · 技术社区  · 16 年前

    最近,我遇到了一个“有趣”的问题,微软实现了CRTL。 tmpfile 将临时文件放在根目录中,完全忽略临时文件目录。这与对根目录没有特权的用户(例如,在集群上)有问题。此外,使用 _tempnam 需要应用程序记住删除临时文件,如果不进行大量的返工,则无法删除。

    因此,我咬了一口子弹,写了所有IO例程的win32版本(create_temp、read、write、seek、flush),这些例程调用了适当的方法。我注意到的一件事是图书馆现在糟糕的表现。

    测试套件的结果:

    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 比这个便宜10倍 FlushFileBuffers 写入文件 比…慢1.1倍 蓝色代码 . 最终结果是巨大的性能损失 更新文件缓冲区 使用方式与 刷新缓冲区 . 没有变化 FILE_ATTRIBUTE_NORMAL FILE_FLAG_RANDOM_ACCESS 要么。

    4 回复  |  直到 13 年前
        1
  •  2
  •   Emerick Rogul    16 年前

    我认为这可能是由于这个问题,在msdn的页面上描述 FlushFileBuffers 以下内容:

    由于磁盘缓存交互 在系统内, FlushFileBuffers函数可以是 每次使用后效率低下 写入磁盘驱动器设备 正在分别执行写入操作。 如果应用程序正在执行 多次写入磁盘,还需要 确保关键数据写入 持久介质,应用程序 应该使用未缓冲的I/O而不是 经常调用FlushFileBuffers。 打开一个文件进行无缓冲I/O, 使用调用createfile函数 文件\标记\无\缓冲和 文件标记通过标记写入。这个 阻止文件内容 缓存元数据并将其刷新到 每次写入的磁盘。为了更多 信息,请参见创建文件。

    一般来说, 更新文件缓冲区 是一个“昂贵”的操作, since it flushes everything in the write-back cache :

    flushfilebuffers():此函数将刷新写回缓存中的所有内容,因为它 不知道缓存的哪个部分属于您的文件。这可能需要很多时间, 取决于缓存大小和媒体速度。有什么必要?有 通过并写出脏页的线程,因此它可能不是 必要的。

    我认为 fflush 不刷新整个写回缓存。在这种情况下,它的效率要高得多,但是这样的效率会带来潜在的数据丢失风险。CRT的源代码 刷新缓冲区 确认这一点,因为 _commit 电话 更新文件缓冲区 :

    /* lowio commit to ensure data is written to disk */
    if (str->_flag & _IOCOMMIT) {
            return (_commit(_fileno(str)) ? EOF : 0);
    }
    

    从实施 犯下的罪行 :

    if ( !FlushFileBuffers((HANDLE)_get_osfhandle(filedes)) ) {
            retval = GetLastError();
    }
    
        2
  •  2
  •   Timo Geusch    16 年前

    传统上,C运行时库函数缓冲数据,只触发写操作(因此需要像 fflush )我不这么认为 WriteFile 缓冲写入操作,以便每次调用 蓝色代码 ,触发I/O操作,而使用 fwrite 当缓冲区达到一定大小时,I/O被触发。

    从您的测量中可以看到,缓冲的I/O往往更有效…

        3
  •  2
  •   user7116    13 年前

    我可能疯了,但写一个替代品不是更容易吗? tmpfile 使用的 fopen(temporaryname, "wbTD+") ,在那里生成自己的 temporaryname ?

    至少你不必担心重新实现 <file.h> .

        4
  •  1
  •   Adrian McCarthy    16 年前

    我还是有点不清楚问题是什么。首先讨论管理临时文件的生存期,然后跳到包装整个文件I/O接口。您是否在询问如何管理一个临时文件,而不需要包装所有文件I/O的性能惩罚?或者您对CRT函数如何比它们构建在上面的winapi函数更快感兴趣?

    在C运行时函数和winapi函数之间进行的一些比较是苹果和橙子的种类。

    C运行时函数缓冲库内存中的I/O。操作系统中还有另一层缓冲(和缓存)。

    fflush 将数据从库缓冲区刷新到操作系统。它可以直接进入磁盘,也可以进入操作系统缓冲区以备以后写入。 FlushFileBuffers 从OS缓冲区获取数据到磁盘,这通常比将数据从库缓冲区移动到OS缓冲区所需的时间长。

    未对齐的写入很昂贵。操作系统缓冲区使未对齐的写入成为可能,但它们并不能真正加速进程。库缓冲区可以在将数据推送到操作系统之前接受多次写入,从而有效地减少了对磁盘的未对齐写入次数。

    库例程也有可能(尽管这只是猜测)利用磁盘的重叠(异步)I/O,直接到WinAPI的实现都是同步的。