-4

为什么系统调用 read() 比 getc() 函数慢?

for (;;) {
        chr++;
        amr=read(file1, &wc1, 1);
        amr2=read(file2, &wc2, 1);
        if (wc1 == wc2) {
            if (wc1 == '\n')
                line++;
            if (amr == 0) {
                if (eflg)
                    return (1);
                return (0);
            }
            continue;
        }

for (;;) {
    chr++;
    c1 = getc(file1);
    c2 = getc(file2);
    if (c1 == c2) {
        if (c1 == '\n')
            line++;
        if (c1 == EOF) {
            if (eflg)
                return (1);
            return (0);
        }
        continue;
    }

当 getc() 调用它使用 read() 系统调用时,为什么更慢?

4

2 回答 2

13

read()涉及到内核的上下文切换,比较慢。当您直接使用它并一次读取一个字节时,您有很多上下文切换。但是,当您使用 时getc(),它将调用read()一次 4 或 8 kB,然后从其中返回字符而无需进一步的上下文切换,直到它耗尽缓冲区。

如果你使用read()更大的缓冲区,它会比 更快getc(),因为标准 C 库的通用缓冲有一些开销。

(编辑)请注意,对于所有常用存储介质,磁盘只能以 512 字节的块读取。所以无论如何,内核中必须有一些缓冲。而且由于内存是按 4096 字节的页面分配的,因此大多数系统(Linux 确实如此)在每次请求物理存储时至少会读取这么多的内容。但是上下文切换也很昂贵,因此在用户空间中额外的缓冲层仍然可以节省大量时间。此缓冲用于所有 libc IO,其中包括所有使用FILE*的内容(缓冲区是FILE结构的一部分),因此fread()将比read()小型读取更快。

于 2013-04-22T11:29:12.177 回答
2

答案#1:它并不慢。

答案#2:这取决于。

您不想为从文件中读取的每个字节进行系统调用(包括内存保护系统上的上下文切换)。您将在第一个字节大小的访问中,将适量的数据(比如 4k)读入内存,并将第一个字节提供给调用者。在随后的字节大小读取中,您根本不必调用内核或实际访问文件;您只需从缓冲区传递下一个字节,直到您必须读取另一个 4k 块。

这是标准 C 库的调用(fread()fgetc()fgets())默认执行的操作。您可以检查BUFSIZ以获取默认缓冲区大小。setvbuf()您可以通过调用更改缓冲区大小或完全禁用缓冲。

read()不是标准 C 库的一部分,它是一个 POSIX 系统调用。基本上,它是 POSIX 系统上标准 C 库调用的后端。(在 Windows 系统上,fgetc()将改为调用 Win32 API。)因此,read()它不缓冲,并且为字节大小的块调用它是非常低效的。如果您调用read(),您通常会这样做,因为您想自己进行缓冲。

一般来说,不要混合使用 POSIX 和标准库 I/O 调用。使用 POSIX API 进行低级访问,使用标准库以方便移植(以及良好的默认性能)。

于 2013-04-22T11:44:14.310 回答