StackOverflow 上似乎有一个共识,即如果完整读取一个大文件,那么顺序读取是最快的,并且多线程读取不太可能带来好处(例如,1、2 和还有几个)。
现在,在下面的代码示例中,多线程读取实际上更快了,而且快了很多(对于 1000GB 文件,我已经看到了 2 倍,甚至高达 3 倍)。这是为什么?
sequential: 41s
parallel: 27s
我正在 Xeon w9-3495X 56 核系统上的三星 SSD 990 PRO 4TB 上读取数据。当顺序读取时,SSD 活动时间约为 75%,因此我可以通过多线程实现更高的速率,这在某种程度上是可以理解的。但为什么 SSD 活动时间一开始就不是 100%?我注意到进程的 CPU 负载在 1 个线程时为 2%,在 4 个线程时为 7%,这都接近
100% / 56 * nThreads
,也许这已经是答案了。不过,是什么让 CPU 在
std::filebuf::sgetn
期间如此忙碌?有没有一种更快的方法来读取文件,同时也可以提高单线程读取性能?
#include <chrono>
#include <fstream>
#include <ios>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
// fsutil file createnew 100GB 100000000000
constexpr auto filename = "100GB";
constexpr auto bufferSize = 6'000'000;
constexpr auto nThreads = 4;
template<typename Callback>
void timeit(const char * message, const Callback & callback) {
using namespace std::chrono;
std::cout << message << ": ";
const auto start = high_resolution_clock::now();
callback();
std::cout << duration_cast<seconds>(high_resolution_clock::now() - start) << std::endl;
}
static void readFile(const size_t nThreads = 1, const size_t iThread = 0) {
std::filebuf file;
file.open(filename, std::ios::in | std::ios::binary);
const auto buffer = std::make_unique_for_overwrite<char[]>(bufferSize);
if (iThread > 0) {
file.pubseekoff(iThread * bufferSize, std::ios_base::cur);
}
while (file.sgetn(buffer.get(), bufferSize)) {
if (nThreads > 1) {
file.pubseekoff((nThreads - 1) * bufferSize, std::ios_base::cur);
}
}
}
int main() {
timeit("sequential", [] { readFile(); });
timeit("parallel", [] {
std::vector<std::jthread> threads;
for (int iThread = 0; iThread < nThreads; iThread++) {
threads.emplace_back(readFile, nThreads, iThread);
}
});
}