提高本地服务器上的PHP性能

时间:2011-05-24 16:07:26

标签: php performance xampp

我有一个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;  
}   

3 个答案:

答案 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管理员的一些hk
floor(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);