优化我的read()循环C(两个循环合二为一)

Optimize my read() loop C (two loops in one)

本文关键字:循环 两个 合二为一 我的 优化 read      更新时间:2023-10-16

我需要读取文件并将它们存储在mainbuff和mainbuff2中。

我应该只使用系统调用,如open(), read(), write()

我不想把它们存储在堆栈中,如果它非常大怎么办?堆分配更好。

此代码有效:

...
    char charbuf;
    char *mainbuff1=malloc(100);
    char *mainbuff2=malloc(100);
    while (read(file1, &charbuf, 1)!=0)
            mainbuff1[len++]=charbuf;
    while (read(file2, &charbuf, 1)!=0)
            mainbuff2[len2++]=charbuf;
...

但是mainbuff只有100字符。更好的解决方案是在计算文件中的字符后分配mainbuff,如下所示:

...
    char charbuf;
    while (read(file1, &charbuf, 1)!=0)
            len++;
    while (read(file2, &charbuf, 1)!=0)
            len2++;
    char *mainbuff1=malloc(len);
    char *mainbuff2=malloc(len2);
...

然后再次重复while循环并将字节读取到mainbuff中。

但是2个循环(第一个将读取并计数,第二个将读取)对于大文件来说将是低效和缓慢的。需要用一个或其他更有效的方法来完成。请帮助!不知道!

您可以使用fstat来获取文件大小,而不是读取两次。

#include <sys/stat.h>
int main() {
    struct stat sbuf;
    int fd = open("filename", O_RDWR);
    fstat(fd, &sbuf);
    char *buf = malloc(sbuf.st_size + 1);
}

但是,实际上,当 工作太慢时,才需要担心效率。

如果这确实是一个需要优化的地方,那么您真正应该优化的是以下两件事:

    <
  • 缓冲区分配/gh>
  • read()write()呼叫数

对于100到1000字节的小缓冲区,没有理由使用malloc()之类的,只要在堆栈上分配缓冲区,它将是最快的。当然,除非您希望从函数返回指向这些缓冲区的指针,在这种情况下,您可能应该使用malloc()。否则,你应该考虑使用全局/静态数组,而不是动态分配的数组。

对于I/O调用,使用整个缓冲区大小调用read()write()。不要调用它们来读取或写入单个字节。转换到内核和返回内核是有成本的。

此外,如果您希望在RAM中处理相当大的文件,请考虑使用文件映射。

stat等允许您获取文件大小。http://linux.die.net/man/2/fstat

或者,如果不能使用,可以使用lseek http://linux.die.net/man/2/lseek(特别注意返回值)

如果你不能使用它,你可以随时realloc你的缓冲区。

我把它留给你来实现,因为这显然是一个赋值。div;)

在优化任何东西之前,您必须配置您的代码。有很多工具可以做到这一点:

  • valgrind
  • 英特尔VTune
  • AQTime
  • AMD CodeAnalyst

定义一个自动直接扩展的数组。这样的

#include <stdio.h>
#include <stdlib.h>
typedef struct dynarray {
    size_t size;
    size_t capacity;
    char *array;
} DynArray;
DynArray *da_make(size_t init_size){
    DynArray *da;
    if(NULL==(da=(DynArray*)malloc(sizeof(DynArray)))){
        perror("memory not enough");
        exit(-1);
    }
    if(NULL==(da->array=(char*)malloc(sizeof(char)*init_size))){
        perror("memory not enough");
        exit(-1);
    }
    da->size = 0;
    da->capacity=init_size;
    return da;
}
void da_add(DynArray *da, char value){
    da->array[da->size] = value;
    if(++da->size == da->capacity){
        da->array=(char*)realloc(da->array, sizeof(char)*(da->capacity += 1024));
        if(NULL==da){
            perror("memory not enough");
            exit(-1);
        }
    }
}
void da_free(DynArray *da){
    free(da->array);
    free(da);
}
int main(void) {
    DynArray *da;
    char charbuf;
    int i;
    da = da_make(128);
    while(read(0, &charbuf, 1)!=0)
        da_add(da, charbuf);
    for(i=0;i<da->size;++i)
        putchar(da->array[i]);
    da_free(da);
    return 0;
}

为什么需要内存中的所有内容?你可以有读取块,处理,读取下一个块等,
除非你有足够的记忆,否则你不可能把所有的事情都记在脑子里。你的目标是什么?

如果,正如您所说,您只使用系统调用,那么您可以使用整个堆作为缓冲区。

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
size_t sz;
void fix(x){signal(SIGSEGV,fix);sbrk(sz *= 2);}
int main() {
    sz = getpagesize();
    signal(SIGSEGV,fix);
    char *buf = sbrk(sz);
    int fd = open("filename", O_RDWR);
    read(fd, buf, -1);
}

但是如果你碰巧调用了一个使用malloc的库函数,那就太糟糕了!

brksbrk函数允许您直接访问malloc使用的堆。但是没有任何malloc的"开销"。没有malloc的特性,像free, reallocsbrk以字节为单位调用,并返回void *brk使用指针值(即:您只需想象指针存在,并声明为brk(某种程度上),并返回void *

通过使用brksbrk来分配内存,它使用了malloc在第一次调用mallocrealloc时尝试设置和使用的相同空间。而且许多库函数在底层使用malloc ,所以这段代码有很多可能会出错的地方。这是一个非常奇怪和有趣的领域。

这里的信号处理程序也非常危险。它自动为您提供无限的空间,但当然,如果您遇到任何其他类型的分段冲突,例如解引用null指针,处理程序无法修复它,并且它不会再崩溃。所以这会让程序陷入恶性循环:重新尝试内存访问,分配更多的空间,重新尝试内存访问,分配更多的空间。