我有一个超快的M.2硬盘,它有多快?没关系,反正我也用不上这个速度,所以我才问这个问题。
我有一个应用程序,它需要大量的内存,以至于内存都放不下它。幸运的是,它不是一次就需要的,而是用来保存计算的中间结果。
不幸的是,应用程序不能足够快地写入和读取这些数据。我尝试使用多个读取器和写入器线程,但这只会使情况变得更糟(后来我读到这是因为this)。
所以我的问题是:有没有可能在C++中实现真正的异步文件IO,以充分利用那些宣称的每秒千兆字节数?如果有可能,那么如何实现(以跨平台的方式)?
如果您知道某个库,您也可以推荐一个能够胜任此类任务的库,因为我认为重新发明轮子是没有意义的。
编辑:
下面的代码展示了我如何在程序中执行文件IO。它不是来自前面提到的程序,因为它不会是最小的。这一个说明了问题。不要介意Windows.h
。它只用于设置线程关联。在实际程序中我也设置了关联,所以这就是为什么我包含了它。
#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)。我想知道它是否可以增加。
2条答案
按热度按时间u4dcyp6a1#
最简单的事情得到(最多)10倍的速度是改变这一点:
改为:
其中我们在发送到std ofstream之前将写入分组为最多4096字节。
流操作有许多令人讨厌的、编译器难以避免的虚拟调用,当您一次只写入少量字节时,这些虚拟调用会影响性能。
通过将数据分成更大的块,我们使得vtable查找足够少,以至于它们不再占主导地位。
有关原因的更多详细信息,请参见this SO post。
为了获得最后一点性能,你可能不得不使用像boost. asio这样的东西,或者访问你的平台原始异步文件io库。
但是,当你工作在〈10%的驱动器带宽,而栏杆你的CPU,瞄准低挂水果第一。
nbnkbykc2#
分块I/O确实是这里最重要的优化,在大多数情况下应该足够了。然而,对关于 * 异步 * IO的确切问题的直接答案如下。
Boost::Asio在1.21.0版本中增加了对文件操作的支持。界面与Asio的其余部分相似。
首先,我们需要创建一个表示文件的对象。最常见的用例是使用
random_access_file
或stream_file
。在本示例代码中,流文件就足够了。阅读是通过
async_read_some
完成的,但是通常的async_read
helper函数可以用来一次读取特定数量的字节。如果操作系统支持的话,这些操作确实会在后台运行,并且只占用很少的处理器时间,Windows和Linux都支持这一点。