2015-03-30 5 views
2

Я только что провел этот очень интересный эксперимент, и результаты вышли довольно неожиданно.Возвращаемый массив быстрее, чем возврат элемента массива

Целью теста было определить наилучший способ, с точки зрения производительности, получить элемент массива. Причина в том, что у меня есть класс конфигурации, который содержит настройки в ассоциативном муфтийном массиве, и я не был уверен, что получаю эти значения наилучшим образом.

данных (это на самом деле не нужны для вопроса, но я решил включить его, так что вы видите, что это вполне разумное количество данных для выполнения тестов с)

$data = array('system' => [ 
    'GUI' => 
    array(
     'enabled' => true, 
     'password' => '', 
    ), 
    'Constants' => array(
     'URL_QUERYSTRING' => true, 
     'ERRORS_TO_EXCEPTIONS' => true, 
     'DEBUG_MODE' => true, 
    ), 
    'Benchmark' => 
    array(
     'enabled' => false, 
    ), 
    'Translations' => 
    array(
     'db_connection' => 'Default', 
     'table_name' => 
     array(
      'languages' => 'languages', 
      'translations' => 'translations', 
     ), 
    ), 
    'Spam' => 
    array(
     'honeypot_names' => 
     array(
      0 => 'name1', 
      1 => 'name2', 
      2 => 'name3', 
      3 => 'name4', 
      4 => 'name5', 
      5 => 'name6', 
     ), 
    ), 
    'Response' => 
    array(
     'supported' => 
     array(
      0 => 'text/html', 
      1 => 'application/json', 
     ), 
    ),] 
); 

Методы

function plain($file, $setting, $key, $sub){ 
    global $data; 

    return $data[$file][$setting][$key][$sub]; 
} 

function loop(...$args){ 
    global $data; 

    $value = $data[array_shift($args)]; 
    foreach($args as $arg){ 
     $value = $value[$arg]; 
    } 

    return $value; 
} 

function arr(){ 
    global $data; 

    return $data; 
} 

Параметры (при вызове функции)

loop('system', 'Translations', 'table_name', 'languages'); 
plain('system', 'Translations', 'table_name', 'languages'); 
arr()['system']['Translations']['table_name']['languages']; 

Оставив в стороне любые другие возможные недостатки и сосредоточив внимание только на производительности, я провел 50 тестов с использованием 10000 циклов. Каждая функция была вызвана всего 500000 раз. Результаты приведены в среднем секундах на 10000 петель:

loop: 100% - 0.0381 сек. Возврат: языки

равнина: 38% - 0.0146 сек. Возвращения: Языки

обр: 23% - 0.0088 сек. Возвращает: языки

Я ожидал loop быть довольно медленным, потому что есть логика внутри, но, глядя на результаты двух других я был очень удивлен. Я ожидал, что plain будет самым быстрым, потому что я возвращаю элемент из массива, и по другой причине arr будет самым медленным, потому что он возвращает весь массив.

Учитывая исход эксперимента, у меня есть 2 вопроса.

  • Почему arr почти в 2 раза быстрее, чем plain?
  • Есть ли какие-либо другие методы, которые я пропустил, которые могут превзойти arr?
+4

Ваши ожидания ошибочны, почему ** не будет ** быстрее возвращать массив, а затем на самом деле перебирать весь массив, чтобы найти определенное значение по ключу? Даже когда вы используете скобки и поставляете ключ, итерация должна произойти, чтобы найти этот ключ. – adeneo

+0

@adeneo в случае 'plain', он не выполняет итерацию массива, кроме того, когда я вызываю' arr', а затем предоставляю ключи элементу, к которому я хочу перейти, я фактически делаю то же, что и 'plain' внутренне, так как результат, это то, что мне интересно. –

+0

Все, что вам нужно сделать, чтобы вернуть массив, - это вернуть _reference_. Если вы возвращаете один элемент, вы также можете возвращать ссылку, _but_ он должен делать косвенный поиск в массиве _before_ return –

ответ

2

Я сказал это в комментарии, но я решил, что это довольно близко к ответу. Ваш вопрос в основном сводится к тому, почему 2+2; не быстрее, чем просто 2;

Массивы - это просто объекты, хранящиеся в памяти. Чтобы вернуть объект из функции, вы возвращаете адрес памяти (32 или 64-разрядное целое без знака), что подразумевает не что иное, как толкание одного целого в стек.

В случае возврата индекса массива этот индекс действительно представляет собой смещение с базового адреса массива, поэтому каждый раз, когда вы видите доступ к массиву в квадратных скобках, внутренне PHP (скорее, внутренний C реализация PHP) преобразует «индекс» в массив в целое число, которое он добавляет к адресу памяти массива, чтобы получить адрес памяти сохраненного значения в этом индексе.

Так что, когда вы видите этот вид кода:

return $data[$file][$setting][$key][$sub]; 

Это говорит:

Найдите мне адрес $data. Затем вычислите смещение, которое строка, хранящаяся в $file (которая включает в себя поиск того, что $file в памяти). Затем сделайте то же самое для $setting, $key и $sub. Наконец, добавьте все эти смещения вместе, чтобы получить адрес (в случае объектов) или значение (в случае собственных типов данных), чтобы вставить в стек как возвращаемое значение.

Неудивительно, что возвращение простого массива происходит быстрее.

+0

Это имеет смысл. Если я переключу значения в тестовой функции, вызывающей 'arr', вместо переменных, вместо переменных, она будет медленнее, чем ожидалось. В конце концов, мои ожидания не были полностью неправильными, я думаю: D –

+0

[ссылка в случае, если вы хотите это проверить] (http://sandbox.onlinephpfunctions.com/code/03438c97479108525f88b5ede423f9a92636043e) –

1

Вот так PHP работ. Вы ожидаете, что здесь возвращается копия $data. Это не.

То, что вы acutaly have, является указателем (что-то вроде идентификатора к определенному месту в памяти), которое ссылается на данные.

Что вы возвращаете, это ссылка на данные, а не данные сами.

В методе plain вы сначала ищете значение. Это затраты времени. Посмотрите на this great article, которые показывают, как массивы работают внутри.


Иногда код говорит больше, чем слова. Что вы предполагаете это:

function arr(){ 
    global $data; 
    //create a copy 
    $newData = $data; 
    //return reference to $newData 
    return $newData; 
} 

Также не следует использовать global, что это плохая практика. Вы можете указать свой массив в качестве параметра.

//give a copy of the data, slow 
function arr($data) { 
    return $data; 
} 

//give the reference, fast 
function arr(&$data) { 
    return $data; 
} 
+0

Это похоже на действительный ответ, я полагаю, единственной причиной для голосования было бы, если бы оно было не соответствует действительности. Я обязательно буду читать статью позже сегодня вечером! –

Смежные вопросы