c++ - C++中真正的异步文件IO

标签 c++ multithreading file asynchronous io

我有一个超快的 M.2 驱动器。它有多快?没关系,因为我无论如何都无法利用这个速度。这就是我问这个问题的原因。
我有一个需要大量内存的应用程序。以至于它不适合 RAM。幸运的是,它不是一下子需要的。相反,它用于保存计算的中间结果。
不幸的是,应用程序无法足够快地写入和读取此数据。我尝试使用多个读取器和写入器线程,但这只会使情况变得更糟(后来我读到这是因为 this )。
所以我的问题是:是否有可能在 C++ 中使用真正的异步文件 IO 来充分利用那些宣传的每秒千兆字节?如果它比如何(以跨平台方式)?
如果您知道,您也可以推荐一个适合此类任务的库,因为我相信重新发明轮子没有意义。
编辑:
这是显示我如何在我的程序中执行文件 IO 的代码。它不是来自提到的程序,因为它不会那么小。不过这说明了问题。不介意Windows.h .它仅用于设置线程关联。在实际程序中我也设置了affinity ,所以这就是我包含它的原因。

#include <fstream>
#include <thread>
#include <memory>
#include <string>

#include <Windows.h> // for SetThreadAffinityMask()

void stress_write(unsigned bytes, int num)
{
    std::ofstream out("temp" + std::to_string(num));
    for (unsigned i = 0; i < bytes; ++i)
    {
        out << char(i);
    }
}

void lock_thread(unsigned core_idx)
{
    SetThreadAffinityMask(GetCurrentThread(), 1LL << core_idx);
}

int main()
{
    std::ios_base::sync_with_stdio(false);
    lock_thread(0);

    auto worker_count = std::thread::hardware_concurrency() - 1;

    std::unique_ptr<std::thread[]> threads = std::make_unique<std::thread[]>(worker_count); // faster than std::vector

    for (int i = 0; i < worker_count; ++i)
    {
        threads[i] = std::thread(
            [](unsigned idx) {
                lock_thread(idx);
                stress_write(1'000'000'000, idx);
            },
            i + 1
        );
    }
    stress_write(1'000'000'000, 0);

    for (int i = 0; i < worker_count; ++i)
    {
        threads[i].join();
    }
}
正如你所看到的,它只是简单的旧 fstream .在我的机器上,它使用 100% 的 CPU,但只有 7-9% 的磁盘(大约 190MB/s)。我想知道是否可以增加。

最佳答案

获得(最多)10 倍加速的最简单方法是更改​​此设置:

void stress_write(unsigned bytes, int num)
{
  std::ofstream out("temp" + std::to_string(num));
  for (unsigned i = 0; i < bytes; ++i)
  {
    out << char(i);
  }
}

对此:
void stress_write(unsigned bytes, int num)
{
  constexpr auto chunk_size = (1u << 12u); // tune as needed
  std::ofstream out("temp" + std::to_string(num));
  for (unsigned chunk = 0; chunk < (bytes+chunk_size-1)/chunk_size; ++chunk)
  {
    char chunk_buff[chunk_size];
    auto count = (std::min)( bytes - chunk_size*chunk, chunk_size );
    for (unsigned j = 0; j < count; ++j)
    {
      unsigned i = j + chunk_size*chunk;
      chunk_buff[j] = char(i); // processing
    }
    out.write( chunk_buff, count );
  }
}

在发送到 std ofstream 之前,我们将最多写入 4096 个字节。

流操作有许多烦人的、编译器难以消除的虚拟调用,当您一次只写入少量字节时,这些虚拟调用会影响性能。

通过将数据分块成更大的块,我们使 vtable 查找足够少,以至于它们不再占主导地位。

this SO post有关原因的更多详细信息。

要获得最后的性能,您可能必须使用 boost.asio 之类的东西或访问您的平台原始异步文件 io 库。

但是,当您在运行 CPU 的同时以小于 10% 的驱动器带宽工作时,首先要瞄准低垂的果实。

关于c++ - C++中真正的异步文件IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59667777/

相关文章:

java - Java中的多线程问题,运行时结果不同

java - 线程会降低 PC 速度并导致 java.lang.OutOfMemoryError

ruby - 我如何让 ruby​​ 在线程内输出异常?

c - EOF 前的 '\n' 个字符

c++ - 如果构造函数抛出异常,那么拥有该类的全局对象是否有意义?

c++ - 如何编辑二进制文件中的特定值?

c++ - 如何使用 sendto() 发送结构

c - 为什么 fread 返回零以及文件何时包含内容

java - 在Java中从输入文本文件中的字符串中分离单词并将其逐行打印到输出文本文件

c++ - 共享库和 .h 文件