在C

时间:2016-12-18 16:07:04

标签: c file text-files stdio

我正在编写C语言编写的作业。加分点用于快速写入上传测试系统中的文件。

我试图写一些行,每行包含三个空格分隔的十进制整数字符串,然后在文件中包含'\ n'。问题是,fprintf太慢(它们的参考时间或多或少快了1/3)。

我尝试了很多可能性(一切都在一个for循环中)。 fprintf(太慢了):

fprintf(f, "%d %d %d\n", a[i], b[i], c[i]);

转换为字符串,然后将字符串放入其中 - 更糟糕的是:

sprintf(buffer, "%d", a[i]); //or: _itoa(_itoa(a[i], buffer, 10);
fputs(buffer, f);
fputc(' ', f);

有没有快速的方法可以将整数写入简单的文本文件(.txt)(最后一个解决方案有时间220ms,参考时间是140ms,你可以画出时间)?我一直在尝试谷歌搜索,但没有任何工作。但如果时间很短,那就必须有一些方法!

PS:数字一直是整数,大小是4个字节,所有时间格式为:

a0 b0 c0
a1 b1 c1
a2 b2 c2
a3 b3 c3
etc...

更多信息:当我发送解决方案时,我只发送两个文件:file.h和file.c.没有主要等...所以一切都在他们的优化。解决方案应该在命令/算法中(即使在问题的描述中声明,fprintf太慢了,我们应该尝试别的东西来加快速度)。

谢谢你的一切!

编辑:因为你想要整个代码,所以它是:

void save(const str_t * const str, const char *name)
{
  FILE* f;
  int i;

  if(str->cnt == 0)
      return;

  f = fopen(name, "w");
  if(f == NULL)
      return;

  for(i = 0; i < str->cnt; i++)
  {
      fprintf(f, "%d %d %d\n", str->a[i], str->b[i], str->c[i]);
  }
  fclose(f);
}

3 个答案:

答案 0 :(得分:3)

使用printf的任何变体,该函数必须扫描格式字符串以查找%d,并为任何额外选项(例如%-03d)解析它,并相应地工作。那是 很多 的处理时间。 printf很棒,因为它非常灵活,不是因为它

如果您使用itoa类型函数来编写每个数字,您仍然会将整数转换为字符串,然后将该字符串复制到该文件中。您将花费所有处理时间在字符串缓冲区和文件写入之间移动。

我认为你最快的方法是创建一个内存,非常大的缓冲区,将所有内容写入其中,然后执行ONE AND ONLY ONE write将整个缓冲区转储到文件中。

<强> 概要

char buffer[10000];
for(i = 0; i < str->cnt; i++)
{
    /* write to buffer */
}

fwrite(buffer, buffer_size, 1, my_file);  // One fast write.

答案 1 :(得分:1)

它可能是特定于操作系统和实现的。

也许您可以使用setvbuf(3)显式设置缓冲(我建议至少使用32Kbyte缓冲区,可能还有更多)。

请勿忘记明确要求您的编译器进行优化,例如:使用gcc -Wall -O2

您还可以将您的整数显式编码为十进制表示例程(提示:编写一个例程,其中一些int x像1234一样,用相反的数字填充给定的char小数组(例如"4321")非常简单,它会快速运行。

答案 2 :(得分:1)

您可以通过以大块写入文件来减少文件I / O的开销,以减少单个写入操作的数量。

#define CHUNK_SIZE 4096
char file_buffer[CHUNK_SIZE + 64] ;    // 4Kb buffer, plus enough 
                                       // for at least one one line
int buffer_count = 0 ;
int i = 0 ;

while( i < cnt )
{
    buffer_count += sprintf( &file_buffer[buffer_count], "%d %d %d\n", a[i], b[i], c[i] ) ;
    i++ ;

    // if the chunk is big enough, write it.
    if( buffer_count >= CHUNK_SIZE )
    {
        fwrite( file_buffer, buffer_count, 1, f ) ;
        buffer_count = 0 ;
    }
}

// Write remainder
if( buffer_count > 0 )
{
    fwrite( file_buffer, buffer_count, 1, f ) ;    
}

在单次写入中写入正好 4096字节(或其他一些2的幂)可能有一些优势,但这在很大程度上取决于文件系统,并且执行该操作的代码变得有点更复杂:

#define CHUNK_SIZE 4096
char file_buffer[CHUNK_SIZE + 64] ;
int buffer_count = 0 ;
int i = 0 ;

while( i < cnt )
{
    buffer_count += sprintf( &file_buffer[buffer_count], "%d %d %d\n", a[i], b[i], c[i] ) ;
    i++ ;

    // if the chunk is big enough, write it.
    if( buffer_count >= CHUNK_SIZE )
    {
        fwrite( file_buffer, CHUNK_SIZE, 1, f ) ;
        buffer_count -= CHUNK_SIZE ;
        memcpy( file_buffer, &file_buffer[CHUNK_SIZE], buffer_count ) ;
    }
}

// Write remainder
if( buffer_count > 0 )
{
    fwrite( file_buffer, 1, buffer_count, f ) ;    
}

您可能会尝试使用CHUNK_SIZE的不同值 - 较大可能是最佳值,或者您可能会发现它没什么区别。我建议至少 512字节。

测试结果:

使用VC ++ 2015,在以下平台上:

enter image description here

采用希捷ST1000DM003 1TB 64MB缓存SATA 6.0Gb / s硬盘。

单个测试编写100000行的结果是非常可变的,正如您在运行多个进程共享同一硬盘的桌面系统上所预期的那样,因此我每次运行测试100次并选择最小时间结果(如蜜蜂一样)在结果下面的代码中看到):

使用默认的“调试”构建设置(4K块):

line_by_line: 0.195000 seconds
block_write1: 0.154000 seconds
block_write2: 0.143000 seconds

使用默认的“发布”构建设置(4K块):

line_by_line: 0.067000 seconds
block_write1: 0.037000 seconds
block_write2: 0.036000 seconds

优化对所有三种实现具有成比例相似的效果,固定大小的块写入比“粗糙”块略快。

当使用32K模块时,性能仅略高,固定版本和不规则版本之间的差异可以忽略不计:

使用默认的“发布”构建设置(32K块):

block_write1: 0.036000 seconds
block_write2: 0.036000 seconds

使用512字节块与4K块没有明显不同:

使用默认的“Release”构建设置(512字节块):

block_write1: 0.036000 seconds
block_write2: 0.037000 seconds

以上所有内容均为32位(x86)版本。构建64位代码(x64)产生了有趣的结果:

使用默认的“Release”构建设置(4K块) - 64位代码:

line_by_line: 0.049000 seconds
block_write1: 0.038000 seconds
block_write2: 0.032000 seconds

参差不齐的块稍慢(尽管可能没有统计意义),固定块的速度明显快于逐行写入(但不足以使其比任何块写入更快)。

测试代码(4K块版本):

#include <stdio.h>
#include <string.h>
#include <time.h>


void line_by_line_write( int count )
{
  FILE* f = fopen("line_by_line_write.txt", "w");
  for( int i = 0; i < count; i++)
  {
      fprintf(f, "%d %d %d\n", 1234, 5678, 9012 ) ;
  }
  fclose(f);       
}

#define CHUNK_SIZE (4096)

void block_write1( int count )
{
  FILE* f = fopen("block_write1.txt", "w");
  char file_buffer[CHUNK_SIZE + 64];
  int buffer_count = 0;
  int i = 0;

  while( i < count )
  {
      buffer_count += sprintf( &file_buffer[buffer_count], "%d %d %d\n", 1234, 5678, 9012 );
      i++;

      // if the chunk is big enough, write it.
      if( buffer_count >= CHUNK_SIZE )
      {
          fwrite( file_buffer, buffer_count, 1, f );
          buffer_count = 0 ;
      }
  }

  // Write remainder
  if( buffer_count > 0 )
  {
      fwrite( file_buffer, 1, buffer_count, f );
  }
  fclose(f);       

}

void block_write2( int count )
{
  FILE* f = fopen("block_write2.txt", "w");
  char file_buffer[CHUNK_SIZE + 64];
  int buffer_count = 0;
  int i = 0;

  while( i < count )
  {
      buffer_count += sprintf( &file_buffer[buffer_count], "%d %d %d\n", 1234, 5678, 9012 );
      i++;

      // if the chunk is big enough, write it.
      if( buffer_count >= CHUNK_SIZE )
      {
          fwrite( file_buffer, CHUNK_SIZE, 1, f );
          buffer_count -= CHUNK_SIZE;
          memcpy( file_buffer, &file_buffer[CHUNK_SIZE], buffer_count );
      }
  }

  // Write remainder
  if( buffer_count > 0 )
  {
      fwrite( file_buffer, 1, buffer_count, f );
  }
  fclose(f);       

}

#define LINES 100000

int main( void )
{
    clock_t line_by_line_write_minimum = 9999 ;
    clock_t block_write1_minimum = 9999 ;
    clock_t block_write2_minimum = 9999 ;

    for( int i = 0; i < 100; i++ )
    {
        clock_t start = clock() ;
        line_by_line_write( LINES ) ;
        clock_t t = clock() - start ;
        if( t < line_by_line_write_minimum ) line_by_line_write_minimum = t ;

        start = clock() ;
        block_write1( LINES ) ;
        t = clock() - start ;
        if( t < block_write1_minimum ) block_write1_minimum = t ;

        start = clock() ;
        block_write2( LINES ) ;
        t = clock() - start ;
        if( t < block_write2_minimum ) block_write2_minimum = t ;
    }

    printf( "line_by_line: %f seconds\n", (float)(line_by_line_write_minimum) / CLOCKS_PER_SEC ) ;
    printf( "block_write1: %f seconds\n", (float)(block_write1_minimum) / CLOCKS_PER_SEC ) ;
    printf( "block_write2: %f seconds\n", (float)(block_write2_minimum) / CLOCKS_PER_SEC ) ;
}