Улучшить производительность php на локальном сервере - PullRequest
8 голосов
/ 24 мая 2011

У меня установлена ​​XAMPP, с настройками по умолчанию.

Производительность не является большой проблемой, так как я использую PHP в основном для запуска веб-страниц и небольших веб-приложений.Ожидание пары секунд для страницы не является чем-то необычным.

Однако я недавно взялся за проблемы с Project Euler и решил сделать их на PHP.

Как ни старайся, я не смог заставить свой код работать менее чем за 1 минуту 1 секунду (оптимизирован по сравнению с почти 3 минутами), и я очень смутился, особенно учитывая, что большинство постеров на Pjt Euler сообщилираз 1-3 секунды.(# 7, найдите 10001-е простое число)

Я перенес свой код на C #, и та же задача была выполнена в мгновение ока.0,4 секундыТот же алгоритм, единственное заметное отличие в коде состоит в том, что я использовал List в C # для замены массива, который я использовал в 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 ]

4 голосов
/ 24 мая 2011

"Используйте force ..." из math !Просто бросать какой-то код бессмысленно.Вот лишь несколько моментов, которые могут повысить производительность.

  • , почему вы используете массив для сравнения числа с?
  • функция foreach, таким образом, неэффективна - циклдолжен заканчиваться на floor(sqrt(number))

    пример: sqrt (64) = 8 -> все простые делители будут от 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 -> у нас уже есть 6 и 12, поэтому 3, 9, 15, 21 -> 3 (2k-1) = 3 (2x1-1) = 3, 9, ...

вот какой-то псевдокод от hk admin в проекте euler

isPrime ( number )
{
    if ( number == 1 )      return false
    elseif ( number < 4 )       return true
    elseif ( number % 2 == 0 )  return false
    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
}

PS

О разнице в скорости выполнения -PHP - интерпретируемый язык, для просмотра результатов в браузере у вас запущены 3 программы - браузер, сервер,PHP переводчик.Вы делаете http-запрос, сервер вызывает php (и, возможно, кучу других вещей, например, регистрирует), php читает скрипт и выполняет его.Есть намного больше шагов, чем в C #.

В C # выполняется скомпилированный код.

2 голосов
/ 24 мая 2011

ОКОНЧАТЕЛЬНОЕ РЕДАКТИРОВАНИЕ

Вот код 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";

Бакудан дал мне псевдокод, который я только что перевел и написал для сценария ОП выше.


РЕДАКТИРОВАТЬ 2

Я немного очистил код, ничего не улучшил, может улучшить «читабельность». Но да, я думаю, что это лучшее, что вы получите с PHP, который на i7 без apache дает 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";

EDIT

Сбил еще одну секунду, переместив $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";

Который в итоге оказался:

start time:52:08:000000
#10001: 104743
End time:52:15:000000

VS ваш код:

start time:50:44:000000
#10001: 104743
End time:51:17:000000

Хорошее улучшение, но ничего похожего на C #, просто показывает мощь скомпилированного языка:)

1 голос
/ 25 мая 2011

Хотя я ожидал, что C # превзойдет php, эта разница приводит меня к подозреваю грубую проблему конфигурации, но я понятия не имею, где искать.

Запуск PHP-движка создает небольшие накладные расходы для веб-сервера. Способ загрузки PHP (например, загружаемый в качестве модуля при запуске сервера или загружаемый по требованию для каждого запроса .php) определяет, сколько задействовано. Затем в Windows доступны два варианта PHP: поточно-безопасный и не поточно-безопасный, последний считается более быстрым.

Если это проблема конфигурации XAMPP, я думаю, что вы можете изолировать ее, запустив тест 3 раза на вашем веб-сервере и записав среднее время. Затем запустите тот же скрипт с помощью PHP CLI 3 раза и запишите среднее значение. Если разница заметна, вы можете обвинить XAMPP. Вы должны быть в состоянии найти двоичный файл PHP CLI где-нибудь в папке установки XAMPP.

В моей системе я получаю следующие результаты:

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)

Не большая разница - я бы лучше оптимизировал код.

EDIT

Та же машина и все, кроме времени выполнения, с этим исправленным кодом сократились с ~ 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...