序:
在之前的测试中,我们比较了四种读入方式,发现使用读入优化是最快的选择,但是我们知道fread()是比它更快的方法。这一次,我们对比四种读入优化,探寻C++读取速度的极限
分别是getchar()两种方式以及fread()两种方式。

测试数据为1e5,1e6,1e7的大小,每次测试循环5次或7次力求测试结果的稳定性。(共测试6次)


首先是两种getchar()读入,由于在之前的测试中出现过,故只附代码。

inline void read1(int &curr)
{
static char c;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
curr = curr*10+c-'0';
c = getchar();
}
return ;
} inline void read2()
{
static char c;
input = 0;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
input = input*10+c-'0';
c = getchar();
}
return ;
} void Init1()//传地址
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read1(n);
for(unsigned i = 0; i != n; ++i)
{
read1(m[i]);
}
int endTime = clock();
ans[1][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
} void Init2()//利用全局变量
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read2();
n = input;
for(unsigned i = 0; i != n; ++i)
{
read2();
m[i] = input;
}
int endTime = clock();
ans[2][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}

重点是fread在windows下的使用
由于windows环境中的换行符是’\r”\n’所以直接读取int是不现实的(总之并没有成功,全是乱码)。
于是我们采取读字符的方式读取。

首先是fread函数的参数:fread(char *pos, int len_per, int num, FILE *fp)。
即读取的地址,每个部分的长度(可以是结构体,长度用sizeof(…)), 个数,文件指针

那么对于我们这种情况,且文件是使用freopen(“test.in”, “r”, stdin)打开的来说,
就是这样的:fread(char* pos, 1, int num, stdin);

对于字符串(基于目前状况)而言,我们有三种方式计算长度,
一是根据数据范围将长度置于足够大(小心炸空间),这样最快,一次便能读取全;
二是通过C语言的feek和ftell函数遍历整个文件计算出确切大小
三是设定一个大小,多次读取

那么一的代码如下:

void Get_All()
{
long long file_lenth = maxn*10;//足够大
fread(Buffer,1, file_lenth, stdin);
X = Buffer;//当前指针
return ;
}

二是这样的:

void Get_All()
{
long long file_lenth;
fseek(stdin, 0, SEEK_END);//从文件头遍历到文件尾
file_lenth = ftell(stdin);//文件字节数大小
rewind(stdin);
Buffer = (char*)malloc(1*file_lenth);//分配空间
fread(Buffer,1, file_lenth, stdin);
X = Buffer;//当前指针
return ;
}

三也类似:

inline char Get_Char()
{
if(S==T)//T是尾指针,S == T用于判断是否到尾
{
T=(S=buffer)+fread(buffer,1,maxn,stdin);//maxn预先设定好
if(S==T) return EOF;
}
return *S++; //由于是多次读取,故在从中拆除整数时使用
}
int Get_Int()
{
char c;
int re=0;
for(c=Get_Char();c<'0'||c>'9';c=Get_Char());
while(c>='0'&&c<='9')
re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
return re;
}

一二用于得到整数的函数更为简单,不需要判断字符串是否到尾,因为它读取了整个文件

int Get_Num()
{
c = *X;
re = 0;
while(c < '0' || c > '9') c = *++X;
while(c >= '0' && c <= '9')
{
re = re*10+c-'0';
c = *++X;
}
return re;
}

那么准备工作就完成了,下面开始测试

for(unsigned j = 0; j != 5; ++j)
{
for(unsigned i = 0; i != 7; ++i)//多次循环
{
k = j;
Init1(); //getchar()-1
Init2();//getchar()-2
Init3();//fread()-3
Init4();//fread()-1
Init5();//scanf()
}
}

利用time.h的函数:

int StartTime = clock();
...
int EndTime = clock();
ans[curr][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;//计算总时长

测试结果如下:

1e7*5 *5

2.961 2.874 2.888 2.914 2.854

2.733 2.764 2.793 2.722 2.733

2.493 2.440 2.446 2.464 2.479

2.113 2.134 2.210 2.135 2.157

18.586 18.552 18.613 18.493 18.529

14.491000 13.745000 12.322000 10.749000 92.773000

1e7*7 *5

4.030 4.018 4.013 4.069 4.723

3.797 3.855 3.945 3.932 4.544

3.366 3.516 3.474 3.476 3.889

3.040 3.005 3.037 3.019 3.390

25.993 25.679 26.097 26.229 30.518

20.853000 20.073000 17.721000 15.491000 134.516000

1e6*5 *5

0.301 0.277 0.303 0.271 0.284

0.284 0.274 0.308 0.267 0.285

0.252 0.237 0.251 0.236 0.242

0.219 0.204 0.219 0.208 0.210

1.872 1.840 1.925 1.802 1.861

1.436000 1.418000 1.218000 1.060000 9.300000

1e6*7 *5

0.443 0.386 0.404 0.395 0.407

0.389 0.390 0.387 0.386 0.391

0.342 0.340 0.347 0.334 0.347

0.287 0.291 0.301 0.294 0.301

2.548 2.549 2.612 2.538 2.598

2.035000 1.943000 1.710000 1.474000 12.845000

1e5*5 *5

0.044 0.038 0.041 0.040 0.040

0.044 0.038 0.040 0.038 0.037

0.033 0.033 0.034 0.031 0.033

0.030 0.030 0.027 0.028 0.030

0.280 0.271 0.258 0.264 0.255

0.203000 0.197000 0.164000 0.145000 1.328000

1e5*7 *5

0.054 0.039 0.042 0.042 0.040 0.000 0.000

0.047 0.038 0.035 0.039 0.038 0.000 0.000

0.038 0.036 0.031 0.035 0.032 0.000 0.000

0.034 0.030 0.030 0.028 0.030 0.000 0.000

0.307 0.271 0.259 0.260 0.263 0.000 0.000

0.217000 0.197000 0.172000 0.152000 1.360000

结果十分稳定:T5 >> T1 > T2 > T3 > T4。
经过计算,getchar相对于scanf()的速度有大概6-7倍的优化,而使用fread()则可以达到约9倍的优化。


那么,对于fread(二)呐?
起初我对它不抱有信心:遍历文件需要时间,我把长度规定好肯定比你快啊
事实证明差不多,速度略慢于fread(一)。
测试1e7*5 *7,三种fread对比如下
19.067000 16.611000 17.084000


完整测试代码如下:

/*
About: read-data-final
Auther: kongse_qi
date: 2017/04/17
result: Get_Num is nearly 10 times faster than scanf();
*/ #include <bits/stdc++.h>
#define maxn 10000050
using namespace std; int input, k;
int m[maxn];
double ans[10][20]; inline void read1(int &curr)
{
static char c;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
curr = curr*10+c-'0';
c = getchar();
}
return ;
} inline void read2()
{
static char c;
input = 0;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
input = input*10+c-'0';
c = getchar();
}
return ;
} void Init1()//传地址
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read1(n);
for(unsigned i = 0; i != n; ++i)
{
read1(m[i]);
}
int endTime = clock();
ans[1][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
} void Init2()//利用全局变量
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read2();
n = input;
for(unsigned i = 0; i != n; ++i)
{
read2();
m[i] = input;
}
int endTime = clock();
ans[2][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
} char buffer[maxn],*S,*T;
inline char Get_Char()
{
if(S==T)
{
T=(S=buffer)+fread(buffer,1,maxn,stdin);
if(S==T) return EOF;
}
return *S++;
} int Get_Int()
{
char c;
int re=0;
for(c=Get_Char();c<'0'||c>'9';c=Get_Char());
while(c>='0'&&c<='9')
re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
return re;
} void Init3()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
n = Get_Int();
for(unsigned i = 0; i != n; ++i)
{
m[i] = Get_Int();
}
int endTime = clock();
ans[3][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
} char *X, Buffer[maxn*10], c, *buffer_;
int re; void Get_All()
{
long long file_lenth = maxn*10;//int占8,还有换行等
fread(Buffer,1, file_lenth, stdin);
X = Buffer;
return ;
} void Get_All1()
{
long long file_lenth;
fseek(stdin, 0, SEEK_END);
file_lenth = ftell(stdin);
rewind(stdin);
buffer_ = (char*)malloc(1*file_lenth);
fread(buffer_,1, file_lenth, stdin);
X = buffer;
return ;
} int Get_Num()
{
c = *X;
re = 0;
while(c < '0' || c > '9') c = *++X;
while(c >= '0' && c <= '9')
{
re = re*10+c-'0';
c = *++X;
}
return re;
} void Init4()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
Get_All();
n = Get_Num();
for(unsigned i = 0; i != n; ++i)
{
m[i] = Get_Num();
}
int endTime = clock();
ans[4][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
} void Init5()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
scanf("%d", &n);
for(unsigned i = 0; i != n; ++i)
{
scanf("%d", &m[i]);
}
int endTime = clock();
ans[5][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
} void Init6()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
Get_All1();
n = Get_Num();
for(unsigned i = 0; i != n; ++i)
{
m[i] = Get_Num();
}
int endTime = clock();
ans[6][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
free(buffer_);
fclose(stdin);
return ;
} int main()
{
int i, j;
double tot[10] = {0}; for(unsigned j = 0; j != 5; ++j)
{
k = j;
for(unsigned i = 0; i != 7; ++i)
{
Init1();
Init2();
Init3();
Init4();
Init5();
Init6();
}
}
for(unsigned j = 1; j != 7; ++j)
{
for(unsigned i = 0; i != 5; ++i)
{
printf("%.3f ", ans[j][i]);
tot[j] += ans[j][i];
}
printf("\n");
} for(unsigned i = 1; i != 7; ++i)
{
printf("%lf ", tot[i]);
} return 0;
}

再次证明其是正确的,将其读入的数输出文件test2.out与读入文件test.in进行比较。

fc test.in test1.out
正在比较文件 test.in 和 TEST1.OUT
FC: 找不到差异

尾:
温馨提示:使用之前也需要计算好空间,不要为了省读入的时间而导致MLE。

自此测试结束。
箜瑟_qi 2017.04.17 20:38

fread读入优化,寻找速度极限的更多相关文章

  1. HDU 1556 BIT区间修改+单点查询(fread读入优化)

    BIT区间修改+单点查询 [题目链接]BIT区间修改+单点查询 &题解: BIT区间修改+单点查询和求和的bit是一模一样的(包括add,sum) 只不过是你使用函数的方式不一样: 使用区间的 ...

  2. 【模板】fread读入优化 & fwrite输出优化

    #include <iostream> #include <cstdio> #include <cctype> #define SIZE (1 << 2 ...

  3. Sql server2005 优化查询速度50个方法小结

    Sql server2005 优化查询速度50个方法小结   Sql server2005优化查询速度51法查询速度慢的原因很多,常见如下几种,大家可以参考下.   I/O吞吐量小,形成了瓶颈效应.  ...

  4. ACM:读入优化

    两个简单的读入优化 int getin(){ ;; while(!isdigit(tmp=getchar()) && tmp!='-'); ,tmp=getchar(); )+(ans ...

  5. c++读入优化

    对于输入数据非常大的一些可(变)爱(态)题目,scanf就会大大拖慢程序的运行速度,cin就更不用说了,所以我们要用一种高大上的东西——读入优化. 读入优化的原理其实就是一个一个字符的读入,再组成数字 ...

  6. c++ 读入优化、输出优化模板

    0. 在有些输入数据很多的变态题中,scanf会大大拖慢程序的时间,cin就更慢了,所以就出现了读入优化.其原理就是一个一个字符的读入,输出优化同理,主要使用getchar,putchar函数. 1. ...

  7. Visual Studio 使用 Parallel Builds Monitor 插件迅速找出编译速度慢的瓶颈,优化编译速度

    原文:Visual Studio 使用 Parallel Builds Monitor 插件迅速找出编译速度慢的瓶颈,优化编译速度 嫌项目编译太慢?不一定是 Visual Studio 的问题,有可能 ...

  8. Webpack 打包优化之速度篇

    在前文 Webpack 打包优化之体积篇中,对如何减小 Webpack 打包体积,做了些探讨:当然,那些法子对于打包速度的提升,也是大有裨益.然而,打包速度之于开发体验和及时构建,相当重要:所以有必要 ...

  9. C++ 读入优化&输出优化

    读入优化:读入优化只是针对整数,由于getchar()读字符非常的快,所以采用getchar()来进行读入,下设输入的数为x 负数处理:用一个标志变量f,开始时为1,当读入了'-'时,f变为-1,最后 ...

随机推荐

  1. SpringMVC中@RequestBody引起的400异常处理,返回校验失败具体信息

    问题 使用@RequestBody接收一个json数据的时候,如果传入的参数不符合条件,就会直接返回400的error page. 但究竟是为什么会400并没有抛出来.这对大量参数字段的我们来说,排错 ...

  2. WPF中MeasureOverride ArrangeOverride 的理解

    1. Measure Arrange这两个方法是UIElement的方法 MeasureOverride ArrangeOverride这两个方法是FrameworkElement的方法,Framew ...

  3. TreeSet集合如何保证元素唯一

    TreeSet: 1.特点 TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列 2.使用方式 a.自然顺序(Comparable) TreeSet类的add()方法中会 ...

  4. MCMC(一)蒙特卡罗方法

    MCMC(一)蒙特卡罗方法 MCMC(二)马尔科夫链(待填坑) MCMC(三)M-H采样和Gibbs采样(待填坑) 作为一种随机采样方法,马尔科夫链蒙特卡罗(Markov Chain Monte Ca ...

  5. Dapper源码学习和源码修改(下篇)

    目录: Dapper源码学习和源码修改(上篇主要讲解入参解析) Dapper源码学习和源码修改(下篇主要讲解出参解析) 继上篇讲了下自己学习Dapper的心得之后,下篇也随之而来,上篇主要讲的入参解析 ...

  6. Linux之第一个shell命令

    今天在学习shell脚本的编写,网上看了一个helloworld的栗子: #!/bin/sh #print hello world in the console window a = "he ...

  7. XJOI1424解压字符串

    解压字符串 给你一个字符串S,S是已经被加密过的字符串.现在要求你把字符串S还原.字符串S可能会出现这样的格式:k(q),它表示字符串q重复了k次,其中q是0个或多个字符,而k是一个数字,范围是0至9 ...

  8. Android中的java层的线程暂停和恢复实现

    /**  * 基础线程对象.  *  * @author jevan  * @version (1.0 at 2013-6-17)  * @version (1.1 at 2013-7-2) 增加on ...

  9. 轻量级代码生成器-OnlyCoder

    程序猿利器:代码生成器,使用代码生成器已经好几年了,增删改查各种生成,从UI到DATA层均生成过.之前有使用过动软的,T4模板等....  T4生成实体还是没有问题的,但是生成MVC视图就有点烦杂了, ...

  10. Java线程池使用和分析(二) - execute()原理

    相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...