12

我已经在 C 中使用系统调用(打开、读取和写入)来模拟 Linux 系统中的“cat”函数,它比真实的要慢...

我使用与真正的“cat”相同的缓冲区大小,并使用“strace”,我认为它进行了相同数量的系统调用。但是我的“猫”的输出比真正的“猫”要慢一点。

这是我的代码:

#define BUFSIZ 32768

int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) {
    ssize_t writtenBytes = 0;

    while(writtenBytes < readBytes) {
        writtenBytes += write(fdout,
            buffer + writtenBytes, readBytes - writtenBytes);
        if(writtenBytes == -1) {
            return -1;
        }
    }

    return 0;
}

int catPrint(int fdin, int fdout) {
    char buffer[BUFSIZ];
    ssize_t readBytes;

    do {
        readBytes = read(fdin, buffer, BUFSIZ);

        if(readBytes == -1) {
            return -1;
        }

        if(sysWriteBuffer(fdout, buffer, readBytes) == -1) {
            return -1;
        }
    } while(readBytes > 0);

    return 0;
}

我正在从文件中读取(我将其作为参数传递给 main,我认为这里不需要代码),而不是使用该文件描述符调用 catPrint() 函数,输出描述符为 1,因此它打印到标准输出。

我不明白为什么它会变慢,因为我使用相同的文件进行测试,并且两者(真正的“猫”和我的)只有一个 read() 和一个 write() 用于整个文本。整个文本不应该只出现在屏幕上吗?

PS:我已将此标记为作业,尽管我在这里的问题(为什么它变慢)不是作业的一部分。我只需要使用系统调用创建一个“cat”类型的函数,就完成了。我只是对我的代码有点慢很感兴趣。

用我的愚蠢解决的问题:
我刚决定在同一个文件上调用 linux 的原始 cat 几次,一个接一个,我只是意识到它在我调用它的某些时候也很慢,就像我的一样慢自己的。我想一切都比...

很抱歉像这些人一样浪费你的时间。

4

6 回答 6

16

啊,根据您的编辑,您被预读缓冲区咬住了。您不能通过运行一次来​​测试两个并排读取文件的程序。由于文件在磁盘上,第一个总是较慢,一旦文件在内存中,第二个将运行得更快,您必须为每个创建新数据或运行一个然后运行两个,这样它们都可以获得预读缓冲区的好处。

于 2009-04-20T19:38:33.737 回答
3

研究 mmap(2)。

您将丢弃 ftell/fread 的细节,但如果读取吞吐量真的很重要,它将跳过一层间接。

于 2009-04-20T19:10:10.140 回答
3

也许您在没有优化的情况下编译(或没有那么高的优化设置)?

此外,您的代码将调用sysWriteBuffer一次readBytes等于零 - 也许(部分)解释它?

您还可以内联 sysWriteBuffer(通过编译器开关或手动)。

“内联”意味着将函数的主体复制到其调用站点,以消除调用函数的开销。有时编译器会自动执行此操作(我认为 -O3 在 gcc 中启用了此优化)。您还可以使用gccinline中的关键字来告诉编译器内联函数。如果这样做,您的声明将如下所示:

static inline int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) {
....
于 2009-04-20T18:44:16.443 回答
2

不比较源代码,很难说。如果您将您的 cat 与 GNU cat 进行比较,请记住,您是在将几小时/几天前的代码与发展了 20 多年的代码进行比较。

您可能想要进行更全面的性能分析,以不同的输入大小、从不同的设备(RAM 磁盘会很好)并连续多次运行这两个程序。您必须尝试确定程序中的 WHERE 速度较慢。

由于 cat 本身确实微不足道(并且您在评论中说您已经在优化编译),我敢打赌您观察到的性能影响不是在实际算法中,而是在程序加载时间上。如果系统二进制文件是预链接的(这在当今大多数发行版中很常见),您会发现它的加载速度比您自己编译的任何程序都快(直到您包含程序预链接)。

于 2009-04-20T19:24:08.873 回答
1

你比较strace过两者吗?您可能会尝试使用该-tt参数,以便获得系统调用的时间。

于 2009-04-20T18:52:03.380 回答
1

多少钱?典型的猫是这样的

char bufr[BUFSIZ];
ssize_t len;

while((len=read(fdin, bufr, BUFSIZ)) >0)
     write(fdout, bufr, len);

这节省了一些指令。

于 2009-04-20T18:41:33.530 回答