简体   繁体   English

C ++低级I / O详细信息,读取少于一个块

[英]c++ low level I/O details, reading less than one block

I am dealing with large files and I'd like to improve writing and reading operations. 我正在处理大文件,并且我想改善读写操作。

I need to read a file bigger than 1GB in a sequential manner (at least at the beginning). 我需要以顺序方式(至少在开始时)读取大于1GB的文件。 I wanted to know, does it make sense to calculate the right amount of bytes to read (in order to read a multiple of a block size), or it is the same thing since the read operation is optimized? 我想知道,计算正确的字节读取量(以便读取块大小的倍数)是否有意义,或者由于读取操作已优化,这是同一回事吗?

what I mean is: as I see it (correct me if I am wrong), if I tell the SO to read 8 bytes, it will read a number of bytes equal to a block size (4KB presumably). 我的意思是:如我所见(如果我错了,请纠正我),如果我告诉SO读取8个字节,它将读取等于块大小(假定为4KB)的字节数。 Now when I tell the SO to read the subsequent 8 bytes, since it previously read a complete block, it should have it already in cache, right? 现在,当我告诉SO读取后续的8个字节时,由于它以前读取了一个完整的块,因此它应该已经在缓存中了,对吗? so it should make no difference if I read a file (in a sequential manner) 8 bytes per time or 4KB per time. 因此,如果我按顺序读取文件(每次)8个字节或每次读取4KB,应该没有什么区别。 Is it right? 这样对吗?

Your intuition is right, what you do in userspace will be optimized on several levels. 您的直觉是正确的,您在用户空间中所做的工作将在多个级别上得到优化。

At the Operating System Level 在操作系统级别

First of all, if you tell the OS to read 8 bytes at a time, it will employ readahead mechanisms and it will issue to the device read requests in larger chunks. 首先,如果您告诉操作系统一次读取8个字节,它将采用预读机制,并将向设备发出较大块的读取请求。 This won't happen for each request, as it would be just a waste of resources, but the OS will employ algorithms to decide whether or not to read a larger chunk. 每个请求都不会发生这种情况,因为这只会浪费资源,但是OS将采用算法来决定是否读取更大的块。

For instance, on my system the readahead size is 256 sectors, 128KB: 例如,在我的系统上,预读大小为256个扇区,即128KB:

➜  ~ [3] at 21:41:06 [Mon 1] $ sudo blockdev --getra  /dev/sda 
256

The OS might therefore decide to read in 128KB chunks. 因此,操作系统可能决定读取128KB的块。 Consider for example reading a file sequentially with dd, one sector at a time: 例如,考虑一次用dd顺序读取文件,一次读取一个扇区:

➜  ~ [3] at 21:43:23 [Mon 1] $ dd if=bigfile of=/dev/null bs=512

and checking the I/O statistics with iostat: 并使用iostat检查I / O统计信息:

➜  ~ [3] at 21:44:42 [Mon 1] $ iostat -cxth /dev/sda 1 1000

This samples every second the I/O statistics for 1000 times. 每秒对I / O统计信息采样1000次。 Before checking iostat output, it's worth verifying that dd is actually reading 512 bytes at a time. 在检查iostat输出之前,值得验证dd一次实际读取512个字节。

mguerri-dell ~ [3] at 21:58:11 [Mon 1] $ strace dd if=bigfile of=/dev/null bs=512 count=32
[...]
read(0, "hb\342J\300\371\346\321i\326v\223Ykd\320\211\345X-\202\245\26/K\250\244O?3\346N"..., 512) = 512
[...]

This confirms that dd is reading in 512 bytes chunks. 这确认dd正在读取512字节块。 The output of iostat is the following: iostat的输出如下:

12/01/2014 09:46:07 PM
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          24.50    0.00   10.75    0.00    0.00   64.75

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda
                  0.00     0.00  418.00    0.00 53504.00     0.00   256.00     0.11    0.26    0.26    0.00   0.25  10.60

12/01/2014 09:46:08 PM
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          23.29    0.00   11.14    0.00    0.00   65.57

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda
                  0.00     0.00  420.00    0.00 53760.00     0.00   256.00     0.11    0.25    0.25    0.00   0.25  10.60

12/01/2014 09:46:09 PM
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          24.13    0.00   11.94    0.00    0.00   63.93

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda
                  0.00     0.00  410.00    0.00 52480.00     0.00   256.00     0.10    0.25    0.25    0.00   0.25  10.30

The meaning of the most important fields is the following: 最重要的字段的含义如下:

r/s       Number of read requests per second
rkB/s     KBs read per second
avgrq-sz  Average size (in 512 bytes sectors) of the requests sent to the device,
          considering both read and write operations. Since here I am doing 
          mostly read operations, we can ignore the contribution of write operations.

You can check that every second KB read / Number requests is 128KB, namely 256 sectors as shown by avgrq-sz . 您可以检查每隔KB read / Number requests是否为128KB,即256个扇区,如avgrq-sz所示。 The OS is therefore reading in 128KB chunks from the device. 因此,操作系统正在从设备读取128KB的块。

The OS won't always employ readahead techniques. 操作系统不会总是采用预读技术。 Consider just reading a couple of KBs from your file (I flushed the page cache before, making sure I was not reading directly from the OS page cache): 考虑只从文件中读取几个KB(我之前刷新过页面缓存,确保没有直接从OS页面缓存中读取):

dd if=bigfile  of=/dev/null bs=512 count=8

This is the result I get: 这是我得到的结果:

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda
                  0.00     0.00    1.00    0.00    16.00     0.00    32.00     0.00    2.00    2.00    0.00   2.00   0.20

It's not possible to show only the requests of a single process with iostat, but you might be able to catch only its activity. 使用iostat不可能仅显示单个进程的请求,但是您可能只能捕获其活动。 In this case I was reading 4KB from the file, and in that moment two read operations with an avgrq-sz of 16KB were issued. 在这种情况下,我正在从文件中读取4KB,在那一刻,发出了两次读取操作,其avgrq-sz为16KB。 The OS is still caching some pages from your file, but it's not reading in 128KB chunks. 操作系统仍在缓存文件中的某些页面,但不是以128KB的块读取。

At the C++ stdlib Level 在C ++ stdlib级别

In your case, since you are writing C++ code, you have an additional layer between the operating system and your code, the C++ stdlib. 在您的情况下,由于您正在编写C ++代码,因此在操作系统和您的代码之间还有一个附加层,即C ++ stdlib。

Consider the following example: 考虑以下示例:

#include <iostream>
#include <fstream>

#define BUFF_SIZE 100
#define RD_SZ 8

using namespace std;
int main() {
    char buff[BUFF_SIZE];
    fstream f;
    f.open("bigfile", ios::in | ios::binary );
    f.read(buff, RD_SZ);
    cout << f.gcount() << endl;
    f.read(buff, RD_SZ);
    cout << f.gcount() << endl;
    f.close();
 }

The output is of course: 输出当然是:

➜ mguerri-dell ~ [3] at 22:32:03 [Mon 1] $ g++ io.cpp -o io
➜ mguerri-dell ~ [3] at 22:32:04 [Mon 1] $ ./io          
8
8

But strace shows that just one read syscall is issued, reading 8191 bytes. 但是strace显示仅发出了一个读取的系统调用,读取了8191个字节。

➜ mguerri-dell ~ [3] at 22:33:22 [Mon 1] $ strace ./io
[...]
open("bigfile", O_RDONLY)               = 3
read(3, "hb\342J\300\371\346\321i\326v\223Ykd\320\211\345X-\202\245\26/K\250\244O?3\346N"..., 8191) = 8191
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 16), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faecc811000
write(1, "8\n", 28)                      = 2
write(1, "8\n", 28)                      = 2
close(3)
[...]

After the first read, the C++ stdlib has already cached 8KB of data and the second call does not even need to issue a syscall since your data is available in the stdlib buffers. 第一次读取后,C ++ stdlib已经缓存了8KB数据,第二次调用甚至不需要发出syscall,因为您的数据在stdlib缓冲区中可用。 In fact, if the data had not been available, a read syscall would have been issued, but it would have probably hit the OS page cache, avoiding a request to the device. 实际上,如果数据不可用,则将发出读取的syscall,但可能会打入OS页面缓存,从而避免了对设备的请求。

Having seen how these two caching mechanisms work 了解了这两种缓存机制的工作原理

I would recommend reading 4KB at a time, to reduce the overhead which comes from even just a single call on a C++ file stream, knowing that the OS and the C++ stdlib will optimize the access to the device. 我建议一次读取4KB,以减少即使是对C ++文件流的单次调用所带来的开销,但要知道OS和C ++ stdlib将优化对设备的访问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM