我有一个超快的 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/