我正在编写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);
}
答案 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,在以下平台上:
采用希捷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 ) ;
}