为什么从 SSD 进行大文件的多线程分块读取比单线程顺序读取更快?

问题描述 投票:0回答:1

StackOverflow 上似乎有一个共识,即如果完整读取一个大文件,那么顺序读取是最快的,并且多线程读取不太可能带来好处(例如,12 和还有几个)。

现在,在下面的代码示例中,多线程读取实际上更快了,而且快了很多(对于 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); } }); }
    
c++ multithreading file-read solid-state-drive
1个回答
0
投票
有多种可能的瓶颈:

    CPU(处理或至少处理数据)
  • RAM(将数据写入RAM)
  • IO(与存储通信的总线)
如果进程触发读取操作,您需要执行以下步骤:

    将操作的参数编组到内核(受 CPU 限制)
  • 向存储发送数据请求(IO 绑定)
  • 将数据从存储传输到 RAM(RAM 限制或 IO 限制,我假设 CPU 不涉及现代 DMA 控制器)
  • 完成后,将数据编组到请求它的进程中,并再次调度该进程(受 CPU 限制)
  • 在进程内处理数据(受 CPU 限制)
  • 重复
查看这些重复的步骤,您会发现某些资源始终处于空闲状态,等待其他一两个资源完成其任务。这意味着这些资源中没有一种资源得到 100% 使用,并且强烈表明这一点可以改进。并行执行多个读取将简单地允许以更好地利用这些资源的方式重叠上述步骤,从而提高吞吐量。

© www.soinside.com 2019 - 2024. All rights reserved.