我有一个XAMPP安装,几乎是默认配置。
性能通常不是问题,因为我主要使用PHP来运行网页和小型网络应用程序。等待几秒钟的页面并不罕见。
但是,我最近从Project Euler处理了问题,并决定用PHP来完成它们。
尽我所能,我无法让我的代码在不到1分钟的时间内运行(从差不多3分钟开始优化)并且我变得非常尴尬,特别是考虑到Pjt Euler上的大多数海报都报告了1次-3秒。 (#7,找到第10001个素数)
我将代码移植到C#,同一个任务眨眼完成。 0.4秒相同的算法,代码中唯一值得注意的差异是我在C#中使用List来替换我在PHP中使用的数组。
虽然我确实期望C#优于php,但这种差异让我怀疑总体配置问题,但我不知道在哪里看。
造成这种糟糕表现的原因是什么?
编辑:这是代码:
在PHP中:
/*
* Project Euler #7:
* By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
* What is the 10001st prime number?
*/
ini_set('max_execution_time', 300);
echo "start time:" . date("i:s:u") . "<br />";
function isPrime($number, $prevPrimes)
{
foreach ($prevPrimes as $key =>$prime)
{
if ($prime == 1)
{
continue;
}
elseif ($number % $prime == 0)
{
return 0;
}
}
// If we get to here, $number is prime
return $number;
}
$primes = array();
$i = 0;
$nbPrimes = 0;
while ($nbPrimes <10001)
{
$i++;
if ($i % 2 != 0)
{
$result = isPrime($i, $primes);
if ($result != 0)
{
$primes[] = $i;
$nbPrimes++;
}
}
}
echo "#$nbPrimes: $result<br>";
echo "End time:" . date("i:s:u") . "<br />";
在C#中:
public static void RunSnippet()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<int> primes = new List<int>();
int i = 0;
int nbPrimes = 0;
int result =0;
while (nbPrimes <10001)
{
i++;
if (i % 2 != 0)
{
result = isPrime(i, primes);
if (result != 0)
{
primes.Add(i);
nbPrimes++;
}
}
}
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}",
stopwatch.Elapsed);
Console.WriteLine ("#" + nbPrimes + ": " + result.ToString());
}
public static int isPrime(int number, List<int> prevPrimes)
{
foreach (int prime in prevPrimes)
{
if (prime == 1)
{
continue;
}
else if (number % prime == 0)
{
return 0;
}
}
// If we get to here, number is prime
return number;
}
答案 0 :(得分:4)
“使用数学的强制 ...”!只是抛出一些代码毫无意义。以下几点可以提高性能。
因此foreach函数无效 - 循环应该以{{1}}
结束示例:sqrt(64)= 8 - &gt;所有主要分隔符将从1到8.其他的将是它们的乘积(32 = 4 x 8 = 2x2x2x2x2)
使用公式跳转到下一个可能的素数
数学:
可分为2 - 2,4,6,8,10,12 - 的数字。 2k + 1 = 2x1 + 1 = 3,5,.....
数字可分为3 - 3, 6 ,9, 12 - &gt;我们已经有6和12,所以3,9,15,21 - > 3(2k-1)= 3(2x1-1)= 3,9,......
这是来自项目euler
的pseudo code管理员的一些hkfloor(sqrt(number))
PS
关于执行速度的差异 - PHP是解释语言,在浏览器中查看结果,你有3个程序在运行 - 浏览器,服务器,php解释器。你发出一个http请求,服务器调用php(可能还有一些其他的东西,例如日志记录), php读取脚本并执行它。有比C#更多的步骤。
在C#中,执行编译的代码。
答案 1 :(得分:2)
最终编辑
这是来自Bakudan逻辑的PHP代码,它返回此结果:
start time:44:25:000000
#10001: 104759
End time:44:26:000000
守则:
<?php
echo "start time:" . date("i:s:u") . "\n";
function isPrime($number, &$primes)
{
if ($number === 1) return false;
elseif ($number %2 === 0) return false;
elseif ($number < 4) return true;
elseif ($number < 9) return true;
elseif ($number %3 === 0) return false;
else $r = floor(sqrt($number));
$f = 5;
while ($f <= $r) {
if ($number % $f ===0) return false;
if ($number % ($f+2) === 0) return false;
$f = $f + 6;
}
return true;
}
$primes = array();
$nbPrimes = $i = 0;
while ($nbPrimes < 10001)
{
$i++;
if (isPrime($i, $primes) !== false)
{
$primes[] = $i;
$nbPrimes++;
}
}
echo "#$nbPrimes: " . end($primes) . "\n";
echo "End time:" . date("i:s:u") . "\n";
Bakudan给了我伪代码,我刚刚翻译并为上面的OP脚本写出来。
编辑2
我清理了一下代码,没有改进任何东西,可能会增强“可读性”。但是,我认为这是你用PHP获得的最好的,在没有apache的i7上产生5秒。
<?php
echo "start time:" . date("i:s:u") . "\n";
function isPrime($number, &$primes)
{
foreach($primes as $prime) {
if ($number % $prime === 0 && $prime > 1)
return false;
}
}
$primes = array();
$nbPrimes = $i = 1;
while ($nbPrimes <= 10001)
{
if ($i % 2 !== 0 && isPrime($i, $primes) !== false)
{
$primes[] = $i;
$nbPrimes++;
}
$i++;
}
echo "#$nbPrimes: " . end($primes) . "\n";
echo "End time:" . date("i:s:u") . "\n";
修改强>
在同一$prime === 1
声明中$number % $prime
之后将if
移至start time:29:40:000000
#10001: 104743
End time:29:45:000000
,从而敲掉了另一秒。
ini_set('max_execution_time', 300);
echo "start time:" . date("i:s:u") . "\n";
function isPrime($number, &$prevPrimes)
{
foreach ($prevPrimes as $prime) {
if ($number % $prime === 0 && $prime !== 1)
{
return false;
}
}
// If we get to here, $number is prime
$prevPrimes[] = $number;
return $number;
}
$primes = array();
$i = 0;
$nbPrimes = 0;
while ($nbPrimes < 10001)
{
$i++;
if ($i % 2 !== 0)
{
$result = isPrime($i, $primes);
if ($result !== 0)
{
$nbPrimes++;
}
}
}
echo "#$nbPrimes: $result\n";
echo "End time:" . date("i:s:u") . "\n";
考虑到Hannes建议严格检查并传递数组作为参考,并添加一些我自己的调整(修改函数内的数组):
start time:52:08:000000
#10001: 104743
End time:52:15:000000
最终成为:
start time:50:44:000000
#10001: 104743
End time:51:17:000000
VS你的代码:
{{1}}
那里有一个很好的改进,但没有像C#那样,只是为了显示编译语言的强大功能:)
答案 2 :(得分:1)
虽然我确实希望C#跑赢大盘 php,这种差异引导我 怀疑总配置问题, 但我不知道在哪里看。
启动PHP引擎会为Web服务器带来一点开销。加载PHP的方式(例如,在服务器启动时作为模块加载或按需加载每个.php请求)确定涉及多少开销。然后在Windows上有两种可用的PHP变体:线程安全和非线程安全,后者声称速度更快。
如果是XAMPP配置问题,我认为您可以通过在网络服务器上运行测试3次来隔离它并记下平均时间。然后通过 PHP CLI 运行相同的脚本3次并记下平均值。如果差异明显,那么你可能会责怪XAMPP。您应该能够在XAMPP安装文件夹中的某处找到PHP CLI二进制文件。
在我的系统上,我得到了这些结果:
PHP-CLI: #10001: 104743 -- Time taken: 30.25 second(s)
PHP on IIS/FastCGI: #10001: 104743 -- Time taken: 29.89 second(s)
PHP on Apache/CGI: #10001: 104743 -- Time taken: 29.93 second(s)
没有多大区别 - 我宁愿优化代码。
使用此修订后的代码,相同的机器和执行时间从约30秒降至约5.85秒。唯一值得一提的是,每次调用isPrime函数时,我都使用全局数组而不是按值传递(精确到104743次)。通过引用传递数组也会导致类似的执行时间,给出或花费1秒。比较运算符只削减了一两秒但不多。
/*
* Project Euler #7:
* By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
* What is the 10001st prime number?
*/
ini_set('max_execution_time', 300);
$t0 = microtime(true);
$primes = array();
function isPrime($number)
{
global $primes;
foreach ($primes as $prime)
{
if ($prime === 1)
{
continue;
}
elseif ($number % $prime === 0)
{
return 0;
}
}
return $number;
}
$i = 0;
$nbPrimes = 0;
while ($nbPrimes < 10001)
{
$i++;
if ($i % 2 !== 0)
{
$result = isPrime($i);
if ($result !== 0)
{
$primes[] = $i;
$nbPrimes++;
}
}
}
$t1 = microtime(true);
echo sprintf('#%d: %d -- Time taken: %.2f second(s)', $nbPrimes, $result, $t1 - $t0);