C++中一些细微的比较

说明

  • 测速方式一:测试20次并计时,删掉5次最快和最慢,剩余10次的平均值,即为耗时。
  • 测速方式二:使用vs自带的性能计数器。
  • 环境:vs2022 Enterprise (64 位) 版本 17.1.5、win10 专业版 (64位) 版本21H1。
  • 模式:测速方式一为release、测速方式二为debug,两者都是64位。

取余运算很慢?

有两种取余方法:

int a = 0, b = 0;
std::cin >> a; // 输入999。
// 方法一:使用%运算符。
for (streamoff i = 0; i < 10000; i++) // 平均耗时:22670纳秒。
{
	a = a % 10;
}
// 方法二:使用其他运算得出余数。
for (streamoff i = 0; i < 10000; i++) // 平均耗时:22670纳秒。
{
	a = a - a / 10 * 10;
}
// 方法三:将方法二中的的除法运算独立出来。
b = a / 10;
for (streamoff i = 0; i < 10000; i++) // 平均耗时:1100纳秒。
{
	a = a - b * 10;
}

总结:

  • 减、乘、除三次运算的耗时和一次取余运算相同,可见取余远慢于减、乘、除。
  • 方法二中,除法运算最耗时,若之前正好已经得到a / 10的值,即无需进行除法运算,则快20倍以上。

提前声明for中用于循环的变量,会更快?

// 方法一,提前声明。
long a;
cin >> a; // 输入9。
streamoff i, j, k;
for (i = 0; i < 100; i++) // 平均耗时:534070纳秒。
{
	for (j = 0; j < 100; j++)
	{
		for (k = 0; k < 100; k++)
		{
			if (a > 2)
				a = a + 2;
			else
				a = a - 2;
			
		}
	}
}
// 方法二:在for中声明。
long a;
cin >> a; // 输入9。
for (streamoff  i = 0; i < 100; i++) // 平均耗时:532840纳秒。
{
	for (streamoff  j = 0; j < 100; j++)
	{
		for (streamoff  k = 0; k < 100; k++)
		{
			if (a > 2)
				a = a + 2;
			else
				a = a - 2;
			
		}
	}
}

实验中耗时差距不明显,于是将三个for中的条件都改为I<1000,实验结果分别为:

  • 方法一平均耗时595072650纳秒。
  • 方法二平均耗时594629127纳秒。

总结:

  • 完全不用担心for中变量反复创建带来的消耗。而且,从栈的使用方式来看,事先声明反而更慢也是必然情况。

ifstream+ofstream和ctrl+c/v哪个读写速度更快?

分别读取和写入个22个文件,共9.5GB。

	string p[22];
	streamoff len[22];
	char* n[22];

	for (streamoff i = 1; i <= 22; i++)
	{
		p[i - 1] = "D:\\cfg\\" + to_string(i) + ".mp4";
	}

	// 读取。
	for (streamoff i = 0; i <= 21; i++)
	{
		ifstream f(p[i], ios::binary);
		f.seekg(0, ios_base::end);
		len[i] = f.tellg();
		f.seekg(ios_base::beg);
		n[i] = new char[len[i]];
		f.read(n[i], len[i]);
		f.close();
	}

	// 写入。
	for (streamoff i = 0; i <= 21; i++)
	{
		ofstream fout("C:\\cfg\\" + to_string(i) + ".mp4", ios::binary);
		fout.write(n[i], len[i]);
		fout.close();
	}

读取22个文件,耗时79秒,速度约120MB/s。
写入22个文件,共耗时90秒,速度约105MB/s。
自己掐表计了个时,跟操作系统的crtl+c/v,速度一样。

注意:在win10系统中,内存足够时,程序退出后操作系统会保留内存中的数据,这将导致,第二次运行此代码只需极短的时间就能完成22个文件的读取,我测试时,第二次读取仅7秒。在任务管理器的内存界面,可以看到一块标注为“备用”的区域,这就是读取的文件数据。

MoveMemory的速度

分别测试三种情况下移动内存中的数据。

// 方法一:有重叠的情况下,使用MoveMemory。
// 平均耗时:642,996,430纳秒。
char* a = new char[100];
for (streamoff i = 0; i < 100000000; i++)
{
	MoveMemory(a, a+10, 20);
}
// 方法二:无重叠的情况下,使用MoveMemory。
// 平均耗时:28,249,6010纳秒。
char* a = new char[100];
for (streamoff i = 0; i < 100000000; i++)
{
	MoveMemory(a, a+50, 20);
}
// 方法三:使用for来代替MoveMemory。
// 平均耗时:2347,981,590纳秒。
char* a = new char[100];
for (streamoff i = 0; i < 100000000; i++)
{
	for (size_t i = 0; i < 20; i++)
	{
		*(a + i) = *(a + i + 10);
	}
}

可见MoveMemory的效率还是很高的,而且会分辨内存区是否重叠,用不同的方式移动内存,没有重叠的情况下,速度要快两倍多。

>>=的速度对比

long a, b;
cin >> a;
cin >> b;
// 大于等于的速度。
for (streamoff i = 0; i < 100000000; i++)
{
	if (a >= b) // 性能计数器:403毫秒。
		a++;
	else
		b++;
}
// 大于的速度。
for (streamoff i = 0; i < 100000000; i++)
{
	if (a > b) // 性能计数器:402毫秒。
		a++;
	else
		b++;
}

总结:

  • 虽然在汇编知识中提到会把大于等于转为大于,但测试下来速度差不多。

数组(栈区)和申请内存(堆区)速度比较

[]符号创建的数组存放在栈区,new关键字申请的内存则在堆区。哪个快呢?

// 对比一。
char* a = new char[20];
char b[20] = { 0 };
for (streamoff i = 0; i < 100000000; i++)
{
	a[0] = 3; // 性能计数器:467毫秒。
	a[2] = 4; // 性能计数器:462毫秒。
	a[4] = a[0] + a[2]; // 性能计数器:1552毫秒。
}
for (streamoff i = 0; i < 100000000; i++)
{
	b[0] = 3; // 性能计数器:458毫秒。
	b[2] = 4; // 性能计数器:223毫秒。
	b[4] = b[0] + b[2]; // 性能计数器:1160毫秒。
}

总结:

  • 很明显,数组容易被寄存器命中,从而大幅增加速度。若不被命中,栈区和堆区速度差不多。

以空间换速度来加速除法

除法是四则运算中最慢的,我想加快它。

char QUICK_DIV10[16];
for (char i = 0; i < 127; i++)
{
	QUICK_DIV10[i] = QUICK_DIV10[i] / 10;
}
char a[5] = { 0 };
for (streamoff i = 0; i < 100000000; i++)
{
	a[0] = 3;
	a[4] = a[0] / 10; // 性能计数器:3263毫秒。
	a[4] = QUICK_DIV10[a[0]]; // 性能计数器:934毫秒。
}

总结:

  • 这和以前学习oracle11g数据库时发现的增加速度方法差不多,就是用空间换速度。
  • 尝试了不同运算符和不同复杂度的运算,发现从数组中获取内容也是有较大消耗,有时会导致耗时更多,得不偿失。
  • 还尝试了将QUICK_DIV10char变为unsigned char,耗时降低到了917毫秒。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值