如何在 C++ 中最大化 SSD I/O?
How to maximize SSD I/O in C++?
我编写了一个程序,用于将二维数组读写到 NVME SSD(三星 970EVO plus)中。
我将程序设计为像
一样读取 N*M
#pragma omp parallel for
for(int i=0;i<N;i++)
fstream.read(...) // read M bytes
但是,此代码显示的性能 (KB/s) 低于 SSD 规格 (< GB/s)
我认为如果大小 M 大于块大小(可能是 4KB)并且是 2 的倍数,该代码将显示 GB/s 性能。
然而,事实并非如此。我想我错过了什么。
是否有一些 c++ 代码可以在 SSD 上最大化 I/O 性能?
您可能在尝试并行化 istream
对象(本质上是 串行 机制)上的 read()
调用时遇到麻烦。
来自 cppreference 的 istream::read
(加粗我的):
Modifies the elements in the array pointed to by s and the stream
object. Concurrent access to the same stream object may cause data
races, except for the standard stream object cin when this is
synchronized with stdio (in this case, no data races are initiated,
although no guarantees are given on the order in which extracted
characters are attributed to threads).
不管你叫fstream
读多少,都可能读完a fixed size streambuf buffer。 C++ 标准没有指定它的默认大小,但 4kb 是相当常见的。因此,将 4mb 的大小传递给 read()
很可能最终会有效地将其减少到 1024 次读取 4kb 数据的调用。这可能解释了您观察到的性能。您不是一次读取大量数据,而是您的应用程序多次调用以读取较小的数据块。
C++ 标准确实提供了通过 pubsetbuf
方法调整内部流缓冲区大小的方法,并将其留给每个 C++ 实现来准确指定何时以及如何配置流缓冲区非默认尺寸。您的 C++ 实现可能只允许您在打开 std::ifstream
之前调整流缓冲区的大小,或者它可能根本不允许您调整 std::ifstream
的默认流缓冲区大小;相反,您必须先构造您的自定义流缓冲区实例,然后使用 rdbuf()
将其附加到 std::ifstream
。有关详细信息,请参阅 C++ 库的文档。
或者,您可能希望考虑使用操作系统的本机文件 input/output 系统调用,并完全绕过流缓冲区库,这也会增加一些开销。文件的内容很可能首先被读入流缓冲区,然后复制到您传递到此处的缓冲区中。调用您的本机文件输入系统调用将消除此冗余副本,挤压更多性能。
我编写了一个程序,用于将二维数组读写到 NVME SSD(三星 970EVO plus)中。
我将程序设计为像
一样读取 N*M#pragma omp parallel for
for(int i=0;i<N;i++)
fstream.read(...) // read M bytes
但是,此代码显示的性能 (KB/s) 低于 SSD 规格 (< GB/s)
我认为如果大小 M 大于块大小(可能是 4KB)并且是 2 的倍数,该代码将显示 GB/s 性能。
然而,事实并非如此。我想我错过了什么。
是否有一些 c++ 代码可以在 SSD 上最大化 I/O 性能?
您可能在尝试并行化 istream
对象(本质上是 串行 机制)上的 read()
调用时遇到麻烦。
来自 cppreference 的 istream::read
(加粗我的):
Modifies the elements in the array pointed to by s and the stream object. Concurrent access to the same stream object may cause data races, except for the standard stream object cin when this is synchronized with stdio (in this case, no data races are initiated, although no guarantees are given on the order in which extracted characters are attributed to threads).
不管你叫fstream
读多少,都可能读完a fixed size streambuf buffer。 C++ 标准没有指定它的默认大小,但 4kb 是相当常见的。因此,将 4mb 的大小传递给 read()
很可能最终会有效地将其减少到 1024 次读取 4kb 数据的调用。这可能解释了您观察到的性能。您不是一次读取大量数据,而是您的应用程序多次调用以读取较小的数据块。
C++ 标准确实提供了通过 pubsetbuf
方法调整内部流缓冲区大小的方法,并将其留给每个 C++ 实现来准确指定何时以及如何配置流缓冲区非默认尺寸。您的 C++ 实现可能只允许您在打开 std::ifstream
之前调整流缓冲区的大小,或者它可能根本不允许您调整 std::ifstream
的默认流缓冲区大小;相反,您必须先构造您的自定义流缓冲区实例,然后使用 rdbuf()
将其附加到 std::ifstream
。有关详细信息,请参阅 C++ 库的文档。
或者,您可能希望考虑使用操作系统的本机文件 input/output 系统调用,并完全绕过流缓冲区库,这也会增加一些开销。文件的内容很可能首先被读入流缓冲区,然后复制到您传递到此处的缓冲区中。调用您的本机文件输入系统调用将消除此冗余副本,挤压更多性能。