1

我正在解决一个问题,我想以指定的速率重放存储在文件中的数据。

例如:25,000 条记录/秒。

该文件为 ascii 格式。目前,我读取文件的每一行并应用正则表达式来提取数据。2-4行组成一个记录。我对这个操作进行了计时,生成每条记录大约需要 15 微秒。

发布每条记录所需的时间为 6 微秒。

如果我按顺序执行读取和写入,那么我最终将有 21 微秒的时间来发布每条记录。如此有效,这意味着我的上限是每秒约 47K 条记录。

如果我决定对读取和写入进行多线程处理,那么我将能够每 9 微秒发送一个数据包(忽略锁定惩罚,因为读取器和写入器共享相同的 Q),这提供了每秒 110K 滴答的吞吐量。

我以前的设计正确吗?

当单个生产者和消费者共享一个队列时,什么样的队列和锁定结构具有最小的惩罚?

如果我想扩大规模,最好的方法是什么?

我的应用程序是 C++

4

1 回答 1

1

如果读取/准备一条记录需要 15uS,那么您的最大吞吐量将约为 1sec/15uSec = 67k/sec。您可以忽略 6uSec 部分,因为读取文件的单个线程无法生成更多记录。(尝试一下,将程序更改为仅读取/处理并丢弃输出)不确定您是如何获得 9uS 的。

为了让这个速度超过 67k/sec ...

A) 估计每秒可以从要格式化的磁盘读取的最大记录数。虽然这在很大程度上取决于硬件,但对于普通笔记本电脑来说,20Mb/sec 的数字是典型的。这个数字将为您提供目标的上限,当您接近时,您可以轻松尝试。

B)创建一个线程只是为了读取文件并导致IO延迟。该线程应该写入大的预分配缓冲区,例如每个 4Mb。有关管理这些内容的方法,请参见http://en.wikipedia.org/wiki/Circular_buffer。您希望每个缓冲区保存 1000 条记录(猜测,但不仅仅是 8 条记录!)伪代码:

   while not EOF
     Allocate big buffer
     While not EOF and not buffer full
          Read file using fgets() or whatever
          Apply only very small preprocessing, ideally none
          Save into buffer
     Release buffer for other threads

C)创建另一个线程(或多个,如果记录的顺序不重要)来处理一个满的环形缓冲区,你的正则表达式步骤。该线程依次写入另一组输出环形缓冲区(提示,保持环形缓冲区控制结构在内存中分开)

    While run-program
        Wait/get an input buffer to process, semaphores/mutex/whatever you prefer
        Allocate output buffer
        Process records from input buffer,
           Place result in output buffer
        Release output buffer for next thread
        Release input buffer for reading thread

D)创建最终线程来使用数据。目前尚不清楚此输出是否正在写入磁盘或网络,因此这可能会影响磁盘读取线程。

    Wait/get input buffer from processed records pool
    Output records to wherever
    Return buffer to processed records pool

笔记。预分配所有缓冲区并将它们传递回它们的来源。例如,您可能在文件读取线程和处理线程之间有 4 个缓冲区,当所有 4 个都注入时,文件读取器等待一个空闲,它不只是分配新的缓冲区。如果可以避免,尽量不要使用 memset() 缓冲区,浪费内存带宽。你不需要很多缓冲区,6?每个环形缓冲区?

系统将自动调整到最慢的线程(http://en.wikipedia.org/wiki/Theory_of_constraints),因此如果您读取和准备数据的速度比您想要输出的速度快,所有缓冲区都会填满,一切都会暂停,除了输出。

由于线程在每个同步点传递合理数量的数据,因此开销不会太大。

上面的设计是我的一些代码如何尽可能快地读取 CSV 文件,基本上都是将输入 IO 带宽作为限制因素。

于 2013-03-03T18:43:34.560 回答