gpt4 book ai didi

c++ - 大型读写器缓冲机制-高频率数据C++

转载 作者:行者123 更新时间:2023-12-01 02:21:37 25 4
gpt4 key购买 nike

我需要一个写入器和多个读取器(最多5个)机制,该写入器将大小分别为1 MB和每秒15个包的数据连续推送到C++中。我想做的是一个线程继续写入数据,而5个读取器将同时根据数据的时间戳进行一些搜索操作。我必须将每个数据包保留60分钟,然后才能将其从容器中删除。

由于数据可以以15 MB * 60秒* 60分钟= 54000MB/h的速度增长,因此我需要将近50 GB的空间来保存数据,并使写入器和读取器的操作速度都足够快。但问题是我们无法将如此大小的数据保留在高速缓存或RAM上,因此它必须位于SSD之类的硬盘中(对于这种操作,HDD太慢了)

到目前为止,我一直在想的是直接将循环缓冲区(因为我可以计算最大大小)实现到SSD上,到目前为止,我找不到合适的示例,我也不知道是否是否有可能实现或无法实现某种映射机制,即在RAM中提供一个圆形阵列,该圆形阵列仅保留数据的时间戳和内存的物理地址以搜索硬盘上可用的数据。因此,我猜至少搜索操作会更快。

由于任何类型的锁,互斥或信号量都会减慢操作速度(尤其是写操作至关重要,因为任何读操作都不能丢失数据),所以我不想使用它们。我知道有一些共享锁可用,但我认为它们又有一些缺点。有没有什么方法/想法可以实现这种具有无锁,无等待和线程安全的系统?任何数据结构(容器),模式,示例代码/项目或其他类型的建议都将受到高度赞赏,谢谢您……

编辑:还有其他想法,而不是更大数量的RAM?

最佳答案

这可以在商用PC上完成(并且可以在不更改代码的情况下扩展到服务器)。

锁不是问题。由于只有一个编写者,并且很少有消费者在大数据上执行耗时的任务,因此您将拥有很少的锁定和几乎为零的锁定争用,因此这不是问题。
任何简单的自旋锁(如果您真的很想降低延迟),或者最好是pthread_mutex(无论如何大部分时间都回落到自旋锁)中的任何方法都可以。没有什么花哨。

请注意,您请勿获取锁,也不要从套接字接收兆字节的数据,将其写入磁盘,然后释放该锁。那不是它的工作原理。
您将收到一兆字节的数据,并将其写入您专有的区域,然后获取一个锁,更改一个指针(从而转移所有权),然后释放该锁。锁保护元数据,而不是千兆字节大小的缓冲区中的每个字节。长时间运行的任务,较短的锁定时间,争用= 0。

对于实际数据,写出15MiB/s绝对不是挑战,普通硬盘的性能是5-6倍,SSD的性能很容易达到10到20倍。这也不是您自己需要做的事情。您可以将其留给操作系统进行管理。

我将在磁盘和内存映射上创建一个54.1GB1文件(假定它是64位系统,这在谈论数GB的RAM服务器时是一个合理的假设,这没有问题)。操作系统负责其余的工作。您只需将数据写入映射的区域即可用作循环缓冲区2。
最近写入的内容将或多或少地保证3驻留在RAM中,因此用户可以无故障地访问它。较早的数据可能会或可能不会在RAM中,这取决于您的服务器是否有足够的可用物理RAM。

仍然可以访问较旧的数据,但速度可能会稍慢(如果没有足够的物理RAM来保留整个数据集)。但是,这不会影响生产者或消费者读取最近写入的数据(除非机器的规范太低以至于它甚至无法在RAM中容纳2-3个1MiB块,但是那您就遇到了另一个问题! )。

除了要有5个使用者外,您对如何处理数据不是很具体,所以我不会在这部分做得太深。您可能必须实现作业计划系统,或者可以将每个传入的块分成5个较小的块,或者其他任何块-取决于您要执行的操作。

在任何情况下,您都需要考虑的是映射的环形缓冲区中“有效”的数据区域(作为指针,或者更好的是映射的偏移量)和“未使用”的区域。
生产者是映射的所有者,它“允许”消费者在元数据(偏移的开始/结束对)中指定的范围内访问数据。只有生产者可以更改此元数据。
任何访问此元数据的人(包括生产者)都需要获取一个锁。

甚至有可能通过原子操作来做到这一点,但是看到您很少锁定,我什至都不会打扰。使用锁毫无疑问,并且您不会犯任何细微的错误。

由于生产者知道使用者只会查看定义良好的边界内的数据,因此它可以写入边界之外的区域(已知为“空”的区域)而不会锁定。之后只需要锁定即可更改范围。

当54.1Gib> 54Gib时,您可以在映射中写入一百个备用1MiB块。这可能远远超过了需要的数量(应该做2或3个),但是拥有一些额外的内容并没有什么坏处。当您写入一个新块时(将有效范围增加1),还要调整“有效范围”的另一端。这样,将不再允许线程访问旧块,但是仍在该块中工作的线程可以完成其工作(数据仍然存在)。
如果严格要求正确性,则如果处理一个块花费的时间非常长(在这种情况下超过1 1/2分钟),则可能会导致竞争。如果要绝对确定,则需要另一个锁,在最坏的情况下,该锁可能会阻止生产者。那是您绝对不想要的,但是在最坏的情况下阻止生产者是唯一在每种人为情况下都是100%正确的事情,除非假设的计算机具有无限的内存。
在这种情况下,我认为这种理论上的种族是“允许的”事情。如果处理单个块确实花费了这么长时间,并且有稳定的大量数据输入,那么您手头的问题就更加严重了,因此实际上这不是问题。

如果您的老板在将来的某个时刻决定应保留超过1个小时的积压,则可以放大文件并重新映射,而当“空”区域位于旧缓冲区大小的末尾时,只需扩展“已知”文件的大小,并在生产者中调整您的max_size值。使用者线程甚至不需要知道。您当然可以创建另一个文件,复制数据,交换数据,并同时阻止使用方,但是我认为这是一个较差的解决方案。尺寸增加不必立即可见,但是另一方面,非常希望这是一个“不可见”的过程。
如果将更多的RAM放入计算机,程序将“神奇地”使用它,而无需进行任何更改。操作系统只会在RAM中保留更多页面。如果您再添加几个消费者,它仍然可以正常工作。

1故意大于您的需要,让一些“额外的” 1MiB块。

2最好,您可以使用madvise操作系统(如果您使用的操作系统具有破坏性的DONT_NEED提示,例如Linux),则在覆盖区域之前您将不再对内容感兴趣。但是,如果您不这样做,它将以任何一种方式起作用,效率只会稍低一些,因为OS可能会执行一次写操作就足够的读-修改-写操作。

3当然,从来没有真正的保证,但是无论如何都会如此。

关于c++ - 大型读写器缓冲机制-高频率数据C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20067570/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com