【问题标题】:Why does using fsync() to flush writes to disk speed up access?为什么使用 fsync() 将写入刷新到磁盘会加快访问速度?
【发布时间】:2012-11-12 01:09:40
【问题描述】:

我们需要一个应用程序尽可能地保证当它报告一条记录时,它确实是持久的。我知道要做到这一点,您使用fsync(fd)。然而,出于某种奇怪的原因,使用 fsync() 似乎加快了写入磁盘的代码,而不是像预期的那样减慢它。

一些示例测试代码返回以下结果:

no sync() seconds:0.013388   writes per second:0.000001 
   sync() seconds:0.006268   writes per second:0.000002

下面是产生这些结果的代码:

#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>

void withSync() {
    int f = open( "/tmp/t8" , O_RDWR | O_CREAT );
    lseek (f, 0, SEEK_SET );
    int records = 10*1000;
    clock_t ustart = clock();
    for(int i = 0; i < records; i++) {
        write(f, "012345678901234567890123456789" , 30);
        fsync(f);
    }
    clock_t uend = clock();
    close (f);
    printf("   sync() seconds:%lf   writes per second:%lf\n", ((double)(uend-ustart))/(CLOCKS_PER_SEC), ((double)records)/((double)(uend-ustart))/(CLOCKS_PER_SEC));
}

void withoutSync() {
    int f = open( "/tmp/t10" , O_RDWR | O_CREAT );
    lseek (f, 0, SEEK_SET );
    int records = 10*1000;
    clock_t ustart = clock();
    for(int i = 0; i < records; i++) {
        write(f, "012345678901234567890123456789" , 30 );
    }
    clock_t uend = clock();
    close (f);
    printf("no sync() seconds:%lf   writes per second:%lf \n", ((double)(uend-ustart))/(CLOCKS_PER_SEC), ((double)records)/((double)(uend-ustart))/(CLOCKS_PER_SEC));
}

int main(int argc, const char * argv[])
{
    withoutSync();
    withSync();
    return 0;
}

【问题讨论】:

  • 运行时是如此之小,以至于差异很可能来自执行环境中的差异。尝试一个需要几秒钟的测试用例并通过它运行两个版本。
  • 文件缓存也起到了作用。例如,一些 UNIX 系统运行一个守护进程,flushd,它清理脏缓存页面。设置会影响您正在处理的内容以及它执行的频率。如果您想保证写入,则必须考虑环境
  • 这两个函数在 Linux 上为我输出零时间。当我将记录数增加到 100 万条时,我得到了可衡量的时间量,但不同步版本要快一些。
  • /tmp 是否与tmpfs 一起挂载?
  • 由于“正确答案”是 cmets,我也以答案的形式添加答案。谢谢大家的反馈!

标签: c performance fsync


【解决方案1】:

问题在于您尝试为 I/O 写入计时的方式。您在语义上想要测量 I/O 记录写入之间的 wall-clock time,但您使用的是 C 库函数 clock,它测量 CPU 执行时间而不是总时间。将clock_gettimeCLOCK_MONOTONIC 或理想情况下的CLOCK_MONOTONIC_RAW 时钟选择一起使用(后者是Linux 扩展)。

您没有收集调用clock 之间经过的总时间:您正在收集进程旋转 CPU 周期的估计时间。您的磁盘 I/O(特别是对 writefsync 的调用)是阻塞的,这意味着这些系统调用中的每一个都由内核代表您处理,并且不会在您的进程上下文中消耗 CPU。因此,您需要测量 挂钟时间 的实际差异,这听起来是现实世界中经过的总时间,超出了您的测试程序过程的范围。实际上,fsync 根本不是您关心的 CPU 时间。大多数 I/O 操作的执行时间不会由内核甚至 CPU 处理;这将是由于磁盘控制器。

此外,小记录大小也可以作为基准。这是同步 I/O 的常见用例(例如,为事务日志写入元数据)。要获得较大记录大小的时序稳定性,只需显着增加每个计时器间隔和平均/摊销的循环迭代次数。这将准确地模拟同步写入和刷新的小阻塞记录的成本。

请考虑fdatasync 以提高性能。

【讨论】:

    【解决方案2】:

    非常感谢您的 cmets,谢谢! cmets 建议将测试增加到更大数量的事务是正确的。当使用大量事务时,fsync() 似乎确实有所作为。至少在 OS/X 10.8 上:

    1. 当写入不增加文件大小时,fsync() 将完成写入所需的时间加倍。
    2. 当写入确实增加了文件大小时,fsync() 会明显变慢。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-03
      • 2020-02-06
      • 1970-01-01
      • 1970-01-01
      • 2011-01-28
      • 1970-01-01
      • 2016-04-14
      • 2014-01-05
      相关资源
      最近更新 更多