将32位整数存储到磁盘的绝对最快方法?

时间:2011-01-26 23:37:23

标签: c++ c linux file-io low-latency

我有一个非常延迟敏感的例程,它按顺序生成整数,但需要在崩溃或重新启动时将最后生成的一个存储到磁盘。

目前我正在寻找文件的开头,然后写出整数,然后每次生成新的int时刷新。需要刷新,因此写入至少会击中电池供电的控制器高速缓存。

搜索是非常昂贵的,所以我考虑只是附加4个字节,如果需要恢复,那么寻找到最后并读取最后4个字节。之前的声明显然假设没有太多其他磁盘活动发生,因此写头应该理想地保留在文件的末尾。

这个数字通常不会高于10,000,000,所以40MB并不是那么糟糕。

关于如何在不牺牲完整性的情况下实现最小延迟的任何建议?

Linux 2.6 +上的

C或C ++

8 个答案:

答案 0 :(得分:8)

我认为最快/最简单的方法是将mmap / msync - mmap 1页文件存入内存并将值存储在该页面上。每次更改值时,请调用msync(2)以强制页面返回磁盘。这样,每个商店只需要一次系统调用

答案 1 :(得分:2)

如果我读得正确,那么使用内存映射文件怎么样?只需将您的号码写入指定的地址,它就会显示在文件中。这假设操作系统在需要时将缓存强制写入磁盘,但您可能会发现它值得一试。

int len = sizeof(unsigned);
int fildes = open(...)
void* address = mmap(0, len, PROT_READ, MAP_PRIVATE, fildes, 0)
unsigned* mappedNumber = (unsigned*)(address);

* mappedNumber现在可以包含您的整数。

答案 2 :(得分:2)

<强>测量

您对硬件有多少控制权?如果小于完整,你将得不到保证。

在Linux上我可能会尝试制作一个内核驱动程序,它可以执行具有最高优先级的写入,甚至可能不使用文件系统。

但是,理论上......如果它足以让你点击控制器缓存,那么每次将任何东西刷新到磁盘时,数据都会打到它。这意味着无论驱动器内部是否存在物理搜索,数据都已存在。而且因为你永远不会知道其他应用程序会做什么,或者磁盘旋转的速度有多快,即使你将逻辑文件句柄保存在文件的开头或结尾,你的搜索也是随机的。

您可以随时要求您的用户使用闪存驱动器。

答案 3 :(得分:1)

编写文件的最快方法是将该文件映射到内存中并将其视为char数组。

如果你不关心操作系统崩溃,你不需要同步文件(Linux在生产中从未崩溃过)。所有的写入都会转到绕过内核的文件映射,换句话说,就是真正的零拷贝(你不能用标准硬件上的套接字来做到这一点)。您可能需要在该文件中保留一个标头,其中包含一些记录,以防您的应用程序在将记录写入内存时发生崩溃。即写一条记录,只有在增加记录计数器之后。

调整此文件的大小需要ftruncate()/remap()序列,这​​可能需要一些时间,因此您可能希望通过将文件增加一个因子来最小化调整大小,例如std::vector<>在{{push_back()上增加1.5。 1}}当它溢出时根据您的吞吐量和延迟要求,可以应用某些优化。

内核将异步写入映射到磁盘的文件(好像应用程序中有另一个专用于写入磁盘的线程)。如有必要,可以使用msync()强制写入磁盘。但是,如果您希望在操作系统崩溃中幸存下来,这只是必要的。但是,在操作系统崩溃中幸存下来需要复杂的应用程序设计,因此在实践中幸免于应用程序崩溃已经足够了。

答案 4 :(得分:1)

为什么你的应用程序必须等待写完成?

异步写入数据,或者从另一个线程写入数据。

你对硬盘没有太多的低级别控制。只要你一次写出这么少的数据,你就会招致很多昂贵的搜寻工作。但是,由于您只是将其用作“检查点”以便在发生崩溃时进行恢复,因此似乎没有理由不能异步进行写入。

答案 5 :(得分:0)

无论块大小如何,存储int只需在光盘上占用一个块。因此,您必须将一个块同步到光盘,并且需要花费的时间,并且您无法做任何事情来使其更快。

无论你做什么,fdatasync()都会成为时间杀手。它会将一个块同步到您的(电池供电的RAID)控制器中。

除非你有某种非易失性ram,否则所有(明智的)方法都将完全等效,因为它们都需要同步一个块。

执行搜索系统调用不会产生任何影响,因为这对硬件没有影响。在任何情况下,您都可以使用pwrite()来避免它。

答案 6 :(得分:0)

考虑“附加4个字节”的含义。磁盘不存储文件,甚至不存储字节。它们存储集群和固定数量的集群。文件的概念由OS创建。它将一些集群分配给文件系统表,以跟踪文件的精确位置。现在,附加4个字节意味着至少将4个字节写入集群。但这也意味着确定哪个集群。现有的文件大小是多少?我们需要新的集群吗?如果没有,我们需要读取最后一个集群,将4个字节修补到正确的位置,然后写回集群,然后更新文件系统中的文件大小。如果我们附加一个新的集群,我们可以写入4个字节后跟零(不需要旧值),但我们需要做大量的簿记才能将集群添加到文件中。

所以,绝对最快的方法永远不能追加4个字节。您必须覆盖4个现有字节。最好是在你已经拥有的一个扇区中。其他人已经指出,您可以使用mmap/msync实现此目的。

显然,考虑到当前的SSD和开发者价格以及40 MB的限制,您将使用SSD。如果你节省一个小时,它会为自己付出代价。因此,寻求时间是无关紧要的;固态硬盘没有物理头。

答案 7 :(得分:0)

这里有很多人在谈论mmap(),好像这会解决问题,但是你的系统调用开销与磁盘写入开销相比基本上为零。请记住,附加或写入文件要求您无论如何更新inode(mtime,filesize),这意味着磁盘搜索。

我建议您考虑将整数存储在磁盘以外的其他位置。例如:

  • 将其写入您控制的某个nvram(例如,在嵌入式系统上)。 (如果您的RAID控制器有nvram用于写入,它可能会为您执行此操作。但如果您提出此问题,则可能不会。)

  • 将其写入系统CMOS内存中的空闲字节(例如,在PC硬件上)。

  • 将其写入网络上的另一台计算机(如果它是一个快速网络)并让他们确认。

  • 重新设计您的应用程序,这样您就可以在每次交易后,而不是在每次交易后都进行同步。这比每次做的快约n倍。

  • 重新设计您的应用程序,以便在整数丢失时,最近一次交易的更改丢失。那么你在技术上失去整数更新的事实并不重要;当你重新启动时,它就好像你从未增加它,所以你可以从那里继续。

你没有解释为什么你需要这种行为;说实话,如果你的应用程序需要这个,听起来你的应用程序可能设计得不是很好。例如,有些人建议使用数据库,因为他们总是这样做;是的,但数据库通过缓慢(即每次同步磁盘)来执行此操作,除非首先创建事务,在这种情况下,磁盘只需要在执行“提交事务”时同步。但是如果你绝对必须在每个整数之后进行同步,那么你将不断提交事务,而数据库无法从中拯救你;没有神奇的方法数据库可以保证不丢失数据,除非它至少fdatasync()。