如何将数据写入缓冲区并使用第二个线程将缓冲区写入二进制文件?

时间:2015-08-13 12:37:17

标签: c++ multithreading compression buffer

我从传感器(相机)获取数据并将数据写入二进制文件。问题是它需要占用大量磁盘空间。 所以,我使用了boost(zlib)的压缩,空间减少了很多!问题是压缩过程很慢并且缺少大量数据。

所以,我想实现两个线程,一个从相机获取数据并将数据写入缓冲区。第二个线程将获取缓冲区的前端数据并将其写入二进制文件。在这种情况下,所有数据都将存在。

如何实现此缓冲区?它需要动态扩展和pop_front。我应该使用std :: deque,还是已经存在更好的东西?

1 个答案:

答案 0 :(得分:0)

首先,您必须考虑这四种速率(或速度):

  • 生产速度( SP ):传感器每秒产生的平均字节数。
  • 压缩速度( SC ):您可以压缩的平均每秒字节数。这是压缩算法的输入字节数。
  • 压缩率( RC ):压缩算法产生的压缩数据与未压缩数据的平均比率(输出大小与压缩输入的比率)。(这显然介于0之间)和1。)
  • 写入速度( SW ):每秒可写入磁盘的平均字节数。

如果 SC 小于 SP ,则表示您遇到了麻烦。这意味着您无法实时压缩从传感器收集的所有数据。这意味着你最终会耗尽缓冲存储器。您必须找到更快的压缩算法,或者将更多CPU内核专用于压缩。

如果 SW 小于 SP RC (压缩后传感器数据的大小),则再次遇到麻烦。这意味着你不能像生成和压缩它们那样快地写出输出数据,而且无论你有多少,你最终都会耗尽缓冲存储器。您可以通过采用更好的写入策略或文件系统来获得一些速度,但 SW 的真正好处来自更好的磁盘系统(RAID,SSD,更好的硬件等)

现在,如果速度一切正常,您可以使用以下架构来读取,压缩和写入数据:

您将拥有三个线程(或两个,稍后描述),每个线程执行管道的一部分。您还将拥有两个线程安全队列,一个用于从管道的每个阶段到下一个阶段的通信。

假设两个队列名为Q1Q2,则线程的高级操作将如下所示:

  1. 输入线程:
    1. 读取K字节的传感器数据
    2. 将整个K个字节作为一个整数放在Q1
    3. 转到1.
  2. 压缩线程:
    1. 等到Q1上有某事。
    2. K弹出一个数据缓冲区(可能是Q1个字节)。
    3. 将缓冲区压缩到希望较小的缓冲区并将其放在Q2
    4. 转到1.
  3. 输出线程:
    1. 等到Q2上有某事。
    2. Q2弹出一个数据缓冲区。
    3. 将缓冲区写入输出文件。
    4. 转到1.
  4. 工作中CPU占用最多的部分是在第二个线程中,另外两个可能不会消耗太多CPU时间,因此可能可以共享一个CPU核心。这意味着上述策略可以在两个核上运行。但是,如果工作负载很轻,或者需要许多核心,它也可以在单个核心上运行。这完全取决于我所描述的四种费率。

    使用异步写入(例如,Windows上的IOCP或Linux上的epoll),您可以完全删除第三个线程和第二个队列。然后你的第二个线程需要执行这样的事情:

    1. 等到Q1上有某事。
    2. K弹出一个数据缓冲区(可能是Q1个字节)。
    3. 将缓冲区压缩到希望更小的缓冲区。
    4. 向操作系统发出异步写请求,将压缩缓冲区写入磁盘。
    5. 转到1.
    6. 还有四个值得一提的问题:

        应选择
      1. K,以便与分配缓冲区相关的各种(通常是常量时间)活动所需的时间,将其推入并从线程安全队列中弹出,开始压缩运行并且在执行实际工作(读取传感器数据,压缩字节和写入磁盘)时,向文件发出写入请求变得可以忽略不计。这通常意味着K需要尽可能大。但是如果K非常大(许多兆字节或几百兆字节),那么如果您的应用程序崩溃,您将丢失大量数据。您需要在性能和数据丢失风险之间找到平衡点。我建议(在不知道您的具体需求和约束的情况下)K的值介于10KiB到1MiB之间。

      2. 如果您对并发/并行编程有一定的知识和经验,那么实现一个线程安全的队列很容易,但如果不这样做,则很容易并且容易出错。找到好的例子和实现应该不难。正常的std::dequestd::liststd::任何东西本身都无法使用,但可以用作编写线程安全队列的良好基础。

        < / LI>
      3. 请注意,您正在排队数据缓冲区,而不是单个数字或字节。如果您通过此管道一次传递一个数字,那将非常缓慢且浪费。

      4. 某些压缩算法受限于每次调用可以消耗多少数据,或者您必须将每次调用的输出同步到压缩例程,稍后再调用一次解压缩例程。这些可能会影响K的选择,以及您编写输出文件的方式。您可能必须添加一些元数据,以便以后能够实际解压缩和读取数据。