高效的复制内存

4

我接手了以下代码:

void *buf1 = calloc(1,total_buf_size);
//build buf1 up with memcpy 
int index;
void *buf2 = buf1;

for (index = 0; index < log->numentries; index++)
{
  logentry_t *entry = (logentry_t*)&log->entries[index];

  memcpy(buf2,entry->entryname,sizeof(entry->entryname));
  buf2 = buf2 + (int)sizeof(entry->entryname);
  memcpy(buf2,&entry->entrysize,sizeof(entry->entrysize));
  buf2 = buf2 + (int)sizeof(entry->entrysize);
  memcpy(buf2,&entry->updatesize,sizeof(entry->updatesize));
  buf2 = buf2 + (int)sizeof(entry->updatesize);
  memcpy(buf2,&entry->numupdates,sizeof(entry->numupdates));
  buf2  = buf2 + (int)sizeof(entry->numupdates);

  int j;
  for (j = 0; j < entry->numupdates; j++)
  {   
    memcpy(buf2,&entry->offsets[j],sizeof(entry->offsets[j]));
    buf2 = buf2 + (int)sizeof(entry->offsets[j]);
  }

  int k;
  for (k = 0; k < entry->numupdates; k++)
  {
    memcpy(buf2,&entry->sizes[k],sizeof(entry->sizes[k]));
    buf2 = buf2 + (int)sizeof(entry->sizes[k]);      
  }


  memcpy(buf2,entry->data,entry->updatesize);
}

我有一个交易日志需要遍历,需要将每个日志条目的数据写入文件中的一行。目前它使用memcpy来构建所有条目的缓冲区,并一次性将它们全部写入文件。有没有更好的方法将内存复制和扩展到buf2中?


1
我认为任何形式的复制对于磁盘日志记录来说都足够高效。如果你在谈论代码可读性,那么还有一些改进的空间。此外,即使它不是文本(似乎确实不是),你也可以不必自己收集缓冲区,而是让 fwrite() 为你完成缓冲工作。 - user3125367
1
我看到了 entry->entryname。其他的 struct 字段可以使用 fprintf(..."%d",...) 输出为文本,例如?而且一个 NULL 指针可以打印为 "void" 或 "null" 等等。 - Weather Vane
如果设置一个模拟传入数据的结构和一个模拟传出数据的结构,可能会获得更好的性能。然后,您可以使用=将值分配到内存中,而不是使用memcpy,尽管编译器可能在幕后使用memcpy。这确实为编译器提供了更多的选项来帮助您。更好的方法是,如果相同的结构用于传入和传出数据,则可以直接分配整个结构。 - Michael Dorgan
1
你有 void *buf2 = buf1;。然后你执行 buf2 = buf2 + (int)sizeof(entry->entryname);buf2 + x 部分是未定义的。void 没有大小,因此你不能说:“在这个数组/指针中给我第 n 个 void-thing 的地址”。 - EOF
@user3125367:我意识到了它,认为它是疯狂的。 - EOF
显示剩余5条评论
2个回答

0

我不确定您需要什么样的效率,但这里有一个例子,将每个日志记录条目写入文件,然后在每次迭代时重置缓冲区buf2。因此,您可以将buf1的大小(由total_buf_size定义)减小到只适合一个日志记录条目的大小:

void *buf1 = calloc(1,total_buf_size);
//With this method total_buf_size can be reduced to fit just one log entry record

//build buf1 up with memcpy 
int index;
void *buf2 = buf1;

FILE * pFile;
pFile = fopen ("myfile.txt","w");

for (index = 0; index < log->numentries; index++)
{
  logentry_t *entry = (logentry_t*)&log->entries[index];

  memcpy(buf2,entry->entryname,sizeof(entry->entryname));
  buf2 = buf2 + (int)sizeof(entry->entryname);
  memcpy(buf2,&entry->entrysize,sizeof(entry->entrysize));
  buf2 = buf2 + (int)sizeof(entry->entrysize);
  memcpy(buf2,&entry->updatesize,sizeof(entry->updatesize));
  buf2 = buf2 + (int)sizeof(entry->updatesize);
  memcpy(buf2,&entry->numupdates,sizeof(entry->numupdates));
  buf2  = buf2 + (int)sizeof(entry->numupdates);

  int j;
  for (j = 0; j < entry->numupdates; j++)
  {   
    memcpy(buf2,&entry->offsets[j],sizeof(entry->offsets[j]));
    buf2 = buf2 + (int)sizeof(entry->offsets[j]);
  }

  int k;
  for (k = 0; k < entry->numupdates; k++)
  {
    memcpy(buf2,&entry->sizes[k],sizeof(entry->sizes[k]));
    buf2 = buf2 + (int)sizeof(entry->sizes[k]);      
  }


  memcpy(buf2,entry->data,entry->updatesize);

  fwrite(buf2, sizeof(logentry_t), sizeof(buf2), pFile);

  memset(&buf2, 0, sizeof(buf2)); //clear it out
  buf2 = buf1; //reset the pointer
}

fclose(pFile);
free(buf2);

每当你将磁盘I/O注入到内存操作中时,你就会使其减慢数倍。这很可能是收集内存缓冲区并执行单个写操作的原因。 - David C. Rankin
另一方面,stdio的BUFSIZ旨在进行良好调整,系统调用惩罚应该很小,仅在每个BUFSIZ字节的操作中执行一次。无论如何,您必须在某个时候将它们写入,并且如果不是每个字节的系统调用,则可以正常工作。如果OP想要真正快速地执行此操作,他应该考虑使用某种异步io队列。 - user3125367

0
在我对问题下面的评论中,我的意思是类似这样的:
FILE *file = fopen("log.file", "a"); // assert(file != NULL);

for (int i = 0; i < log->numentries, i++) {
    logentry_t *e = (logentry_t*)&log->entries[i];

#define OUT(x) do { fwrite(x, sizeof(x), 1, file) } while (0)
    OUT(e->entryname);
    OUT(e->entrysize);
    OUT(e->updatesize);
    OUT(e->numupdates);

    for (int j = 0; j < e->numupdates; j++)
        OUT(&e->offsets[j]);

    for (int k = 0; k < e->numupdates; k++)
        OUT(&entry->sizes[k]);

    fwrite(e->data, e->updatesize, 1, file);
#undef OUT
}
fflush(file);

...

也就是说,你根本不需要使用缓冲区,因为fwrite已经在stdio.h层面上执行了缓冲。这段代码未经测试,可能包含任何类型的错误。

此外,请注意,在日志文件中写入原始数据可能会导致不同平台或编译器(尽管后者不太可能,具体取决于logentry_t字段的实际类型)出现大小端和整数大小问题。您可以考虑通过像htons/htonl这样的方式将整数数据字段预格式化为可移植的固定大小表示形式,甚至将它们作为文本printf("%ld", (long)x)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接