在 C 中将整数写入文件的最快方法
fastest way to write integer to file in C
我正在做 C 语言编程的作业。加分是为了快速写入上传测试系统中的文件。
我正在尝试编写多行,每行包含三个 space 分隔的十进制整数字符串,然后在文件中写入 '\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。没有 main 等......所以一切都在优化中。解决方案应该在 commands/algorithm 中(即使在问题的描述中也是声明,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);
}
它可能是特定于操作系统和实现的。
也许您可以使用 setvbuf(3) 显式设置缓冲(我建议至少使用 32Kbyte 缓冲区,可能更多)。
不要忘记明确要求编译器进行优化,例如使用 gcc -Wall -O2
你也可以明确地编码你的整数到十进制表示例程(提示:编写一个例程,其中 ginven 一些 int x
像 1234,用反向数字填充给定的 char
小数组订单(例如 "4321"
)非常简单,而且 运行 很快。
使用 printf
的任何变体,该函数将必须扫描格式字符串以找到 %d
,并解析它以获得任何额外选项(例如 %-03d
),并且相应地工作。那是 很多 的处理时间。 printf
很棒,因为它超级灵活,而不是因为它快。
如果您使用 itoa
类型的函数来写入每个数字,您仍然会将整数转换为字符串,然后将该字符串复制到文件中。您将花费所有处理时间在字符串缓冲区和文件写入之间移动。
我认为最快的方法是在内存中创建一个非常大的缓冲区,将所有内容写入其中,然后执行一次且仅一次写入以将整个缓冲区转储到文件中。
大纲:
char buffer[10000];
for(i = 0; i < str->cnt; i++)
{
/* write to buffer */
}
fwrite(buffer, buffer_size, 1, my_file); // One fast write.
您可以减少文件 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 字节(或其他一些二次方)可能会有一些优势,但这在很大程度上取决于文件系统和代码这样做会变得有点复杂:
#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 次并选择了最短时间结果(在结果下方的代码中可以看到):
使用默认 "Debug" 构建设置(4K 块):
line_by_line: 0.195000 seconds
block_write1: 0.154000 seconds
block_write2: 0.143000 seconds
使用默认 "Release" 构建设置(4K 块):
line_by_line: 0.067000 seconds
block_write1: 0.037000 seconds
block_write2: 0.036000 seconds
优化对所有三种实现的影响比例相似,固定大小的块写入速度略快于 "ragged" 块。
当使用 32K 块时,性能仅稍高,固定版本和不规则版本之间的差异可以忽略不计:
使用默认 "Release" 构建设置(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 ) ;
}
我正在做 C 语言编程的作业。加分是为了快速写入上传测试系统中的文件。
我正在尝试编写多行,每行包含三个 space 分隔的十进制整数字符串,然后在文件中写入 '\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。没有 main 等......所以一切都在优化中。解决方案应该在 commands/algorithm 中(即使在问题的描述中也是声明,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);
}
它可能是特定于操作系统和实现的。
也许您可以使用 setvbuf(3) 显式设置缓冲(我建议至少使用 32Kbyte 缓冲区,可能更多)。
不要忘记明确要求编译器进行优化,例如使用 gcc -Wall -O2
你也可以明确地编码你的整数到十进制表示例程(提示:编写一个例程,其中 ginven 一些 int x
像 1234,用反向数字填充给定的 char
小数组订单(例如 "4321"
)非常简单,而且 运行 很快。
使用 printf
的任何变体,该函数将必须扫描格式字符串以找到 %d
,并解析它以获得任何额外选项(例如 %-03d
),并且相应地工作。那是 很多 的处理时间。 printf
很棒,因为它超级灵活,而不是因为它快。
如果您使用 itoa
类型的函数来写入每个数字,您仍然会将整数转换为字符串,然后将该字符串复制到文件中。您将花费所有处理时间在字符串缓冲区和文件写入之间移动。
我认为最快的方法是在内存中创建一个非常大的缓冲区,将所有内容写入其中,然后执行一次且仅一次写入以将整个缓冲区转储到文件中。
大纲:
char buffer[10000];
for(i = 0; i < str->cnt; i++)
{
/* write to buffer */
}
fwrite(buffer, buffer_size, 1, my_file); // One fast write.
您可以减少文件 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 字节(或其他一些二次方)可能会有一些优势,但这在很大程度上取决于文件系统和代码这样做会变得有点复杂:
#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 次并选择了最短时间结果(在结果下方的代码中可以看到):
使用默认 "Debug" 构建设置(4K 块):
line_by_line: 0.195000 seconds
block_write1: 0.154000 seconds
block_write2: 0.143000 seconds
使用默认 "Release" 构建设置(4K 块):
line_by_line: 0.067000 seconds
block_write1: 0.037000 seconds
block_write2: 0.036000 seconds
优化对所有三种实现的影响比例相似,固定大小的块写入速度略快于 "ragged" 块。
当使用 32K 块时,性能仅稍高,固定版本和不规则版本之间的差异可以忽略不计:
使用默认 "Release" 构建设置(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 ) ;
}