PHP 循环的写法对性能的影响

看过一个博客讨论计算机的体系结构对程序性能的影响,源程序是使用.net写的,我想测试一下PHP是和这种情况一致,下面是程序的源码部分:

<?php 
$n = 1<<10;
$arr = array();
for($x = 0; $x < $n; $x++){
	for($y = 0; $y < $n; $y++){
		$arr[$x][$y] = $x;
	}
}

function sumA(array $arr, $n){
	$sum = 0;
	for($y = 0; $y < $n; $y++){
		for($x = 0; $x < $n; $x++){
			$sum += $arr[$x][$y];
		}
	}
	
	return $sum;
}

function sumB(array $arr, $n){
	$sum = 0;
	for($x = 0; $x < $n; $x++){
		for($y = 0; $y < $n; $y++){
			$sum += $arr[$x][$y];
		}
	}
	
	return $sum;
}

测试的结果如下:



可以看出,sumB的性能几乎是sumA的一半,而两个函数的功能完全相同,只是循环的方式不同,sumA是先循环靠近内部的,sumB是循环最外部的。两种不同的循环方式带来完全两种不同的程序性能,原博客解释说是因为CPU的高速缓存的原因。

因为,前面构造的二维数据是连续的,因此他们在内存中的存储方式也是连续的,类似这样的顺序存储:

$arr[0][0]、$arr[0][1]、$arr[0][2]、$arr[0][3]、$arr[0][4]……

而sumB的循环方式恰好是按照数据在内存中存储的顺序来读取的,这就可以充分的利用CPU的告诉缓存了,因为CPU在读取某个内存单元的数据时会顺便把其相邻单元的数据一起取出,存储到高速缓存中,也就是CPU的一级缓存、二级缓存、三级缓存等,当程序的下一条指令是需这些相邻的内存单元的数据时,CPU首先查看告诉缓存中有没有这些数据,如果有,就直接取出,不再进行寻址读取内存中的数据,从而节省了时间。而sumA的循环方式是跳跃式的读取内存单元中的数据,这样CPU每次读取数据时都无法命中高速缓存,每次都要从内存中读取,因此效率较低。

下面,我们在增加一个函数sumC,源代码如下:

function sumC(array $arr, $n){
	$sum = 0;
	for($x = $n-1; $x >= 0; $x--){
		for($y = $n-1; $y >= 0; $y--){
			$sum += $arr[$x][$y];
		}
	}
	
	return $sum;
}

测试的结果如下:




从结果可以看出,sumC的性能又稍微比sumB好一点点,这又是为什么呢?

比较sumB与sumC的代码可以看出,sumC的循环方式是从后面往前面循环的,也就是从最后赋值的的单元开始取值,这种循环方式符合栈的先进后出的特点,最先赋值的单元被push到最下面,最后赋值的单元在最上面,sumC的取值方式每次都是从栈的最上面一个单元pop出值,完全按照栈的操作方式,不会出现跳跃,因此速度也就更快了。

上面的三个函数都是从程序的源代码上分析性能的好坏,其实针对数组的循环操作,最好的方式是使用PHP自带的优化过的foreach循环,这里我们用sumD函数来说明:

function sumD(array $arr, $n){
	$sum = 0;
	foreach($arr as $item){
		foreach($item as $value){
			$sum += $value;
		}
	}
	
	return $sum;
}

下面的是最终的测试结果,可以看出PHP优化过的foreach循环还是最快的:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值