【发布时间】:2020-04-27 06:47:51
【问题描述】:
我有一个超快的 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)。我想知道它是否可以增加。
【问题讨论】:
-
贴出你正在使用的代码,也许我们可以发现性能错误?
-
除了显式读写文件之外,你有没有想过其他的方法? 内存映射文件怎么样?
-
(a) 驱动器的速度和您使用它的次数确实很重要。很容易误读性能信息(位与字节、随机与顺序、读取与写入)。 (b) 许多读/写线程使情况变得更糟可能是您实际上使驱动器饱和的症状。 (c) 解决您的问题可能需要了解您的业务逻辑。如果您使用 1%、10%、50% 或 80% 的带宽,那么您接下来应该采取哪些措施来提高带宽使用率将会非常不同。
-
如果你能生成一个具体的、完整的、最小的带宽问题示例(其他人可以复制/粘贴和重现!),以及显示如何接近你的带宽饱和。我(和其他人)可以就如何让事情变得更快提供大量建议,但你应该做什么取决于你没有分享的细节,所以我们会在黑暗中拍摄。 (例如,使用 .then 方法查找或扩展未来,将所述未来附加到伪执行器,并将一堆工作排队)
-
接近列出的任何驱动器的最大传输速率的唯一方法是使用操作系统特定的无缓冲 I/O 例程。如果你使用普通的 C++ 库,你会得到大量的内存复制:可能 C++ 例程有一个缓冲区,而 OS I/O 调用(将由 C++ 库调用)有一个缓冲区(磁盘缓存)。如果您提前知道需要哪些数据,如果操作系统支持,您还可以利用异步 I/O 调用。
标签: c++ multithreading file asynchronous io