Где размещать массивы с постоянной стоимостью, которые будут доступны во много раз?

У меня есть несколько массивов, где хранятся возможные параметры для некоторых 3D-команд принтера. Я использую это, чтобы проверить, является ли команда законной. Я смущен тем, где я должен помещать эти массивы. Эти массивы будут доступны только в функции formatcheck, и функция будет вызываться много раз, так как есть тысячи команд для проверки. Должен ли я помещать их в функцию formatcheck в качестве переменных или в начале класса, в который входит функция formatcheck, как частные статические переменные?

public function checkFileGcodeFormat() { $Ms = array(82, 83, 84, 104, 106, 107, 109, 140, 190); $Gs = array(0, 1, 20, 21, 28, 90, 91, 92); $Ts = array(0, 1); if (! ($this->hasM() && $this->hasNoXYZ() && in_array($this->M, $Ms)) || ($this->hasG() && in_array($this->G, $Gs)) || ($this->hasT() && $this->hasNoXYZ() && in_array($this->T, $Ts)) ) return false; else return true; } 

или:

 private static $Ms = array(82, 83, 84, 104, 106, 107, 109, 140, 190); private static $Gs = array(0, 1, 20, 21, 28, 90, 91, 92); private static $Ts = array(0, 1); ... ... public function checkFileGcodeFormat() { if (! ($this->hasM() && $this->hasNoXYZ() && in_array($this->M, self::$Ms)) || ($this->hasG() && in_array($this->G, self::$Gs)) || ($this->hasT() && $this->hasNoXYZ() && in_array($this->T, self::$Ts)) ) return false; else return true; } 

    TL; DR : используйте константу класса для максимальной производительности (см. В конце ответа).

    Давайте рассмотрим характеристики производительности различных версий (и почему):

    PHP 5

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

    В любом случае массивы в обычных функциях воссоздаются во время выполнения с каждым прогоном. И создание во время выполнения в виртуальной машине означает, что каждый элемент добавляется один за другим, в отдельных кодах операций, что означает довольно много накладных расходов (особенно если массив больше, чем всего 1-2 элемента).

    PHP 7.0

    Массивы в обычных функциях [в целом] создаются немного быстрее из-за создания массива, в целом ускоряющегося (оптимизация в обработке HashTable). Если это все постоянные значения, он кэшируется во внутреннем массиве постоянных значений, но дублируется при каждом доступе. Однако выполнение прямого высокоспециализированного действия копирования явно быстрее, чем добавление элементов по одному в массив, как в PHP 5.

    Opcache маркирует их как IMMUTABLE внутри, что позволяет получить прямой доступ [так что вы получаете полную скорость с opcache]. (См. Также https://blog.blackfire.io/php-7-performance-improvements-immutable-arrays.html )

    PHP 7.1

    Массивы изначально всегда кэшируются во внутреннем массиве констант с семантикой copy-on-write.

    Теперь использование статического свойства происходит медленнее, так как поиск статического свойства менее эффективен, чем простая запись в переменную. [Прямой доступ к переменной не имеет дополнительных накладных расходов.]


    Также обратите внимание, что с PHP 5.6 вы можете объявлять (класс) константы со значением массива. PHP 7.1 допускает прямую подстановку констант класса того же класса и добавит массив непосредственно во внутренний массив значений констант для непосредственного использования с in_array.

    То есть самый быстрый код (с 7.1 по крайней мере):

     private const Ms = array(82, 83, 84, 104, 106, 107, 109, 140, 190); private const Gs = array(0, 1, 20, 21, 28, 90, 91, 92); private const Ts = array(0, 1); ... ... public function checkFileGcodeFormat() { if (! ($this->hasM() && $this->hasNoXYZ() && in_array($this->M, self::Ms)) || ($this->hasG() && in_array($this->G, self::Gs)) || ($this->hasT() && $this->hasNoXYZ() && in_array($this->T, self::Ts)) ) return false; else return true; } 

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

    Но я хочу сделать еще один момент. Если у вас есть достаточно большие массивы для поиска значения, важно, как вы их структурируете . Я бы предложил следующее:

     array( 82 => true, 83 => true, 84 => true, 104 => true, 106 => true, 107 => true, 109 => true, 140 => true, 190 => true ); array( 0 => true, 1 => true, 20 => true, 21 => true, 28 => true, 90 => true, 91 => true, 92 => true ); array( 0 => true, 1 => true ); 

    Имея эту структуру, вы можете использовать isset ( O(1) ) вместо in_array ( O(n) ).

    Вот некоторые другие вопросы относительно isset vs. in_array :

    • что быстрее: in_array или isset? [закрыто]

    • in_array vs isset – производительность

    И вот несколько сообщений с ориентирами:

    Последний довольно старый, но я думаю, что соотношение имеет место.

    Итак, подведем итог. Когда вы используете isset время поиска является постоянным (оно действительно может меняться, но это можно игнорировать). Когда вы используете in_array время поиска зависит от позиции элемента (и так далее от размера массива). Даже на небольших массивах isset работает быстрее.

    Если они НИКОГДА не изменяются, вы должны форматировать как const . Во время компиляции запекается и, следовательно, будет самым быстрым.

     const MS = [82, 83, 84, 104, 106, 107, 109, 140, 190]; const GS = [0, 1, 20, 21, 28, 90, 91, 92]; const TS = [0, 1]; if (!in_array($this->M, MS)) { ... } 

    или

     class () { const MS = [82, 83, 84, 104, 106, 107, 109, 140, 190]; const GS = [0, 1, 20, 21, 28, 90, 91, 92]; const TS = [0, 1]; if (!in_array($this->M, self::MS)) { ... } } 

    Некоторые примечания:

    • const такие же, как и define но испеченные во время компиляции, что немного быстрее, чем определяет и переменные массивы.
    • вы можете определить const на глобальном уровне или на уровне класса ( http://php.net/manual/en/language.oop5.constants.php ). Начиная с php 7.1, вы также объявляете класс consts как private / public / protected и т. Д.
    • Я использовал заглавные буквы для определений, который является неофициальным стандартом, но не является обязательным требованием.

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

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

    Если вы уверены, что в будущем данные не будут меняться, или вы никогда не захотите использовать разные наборы данных в разное время, даже для тестирования, тогда вы сможете уйти с этим методом в любом случае , Если вам нужен более гибкий код, константы классов или статические свойства могут вызвать некоторые серьезные проблемы. Как я объясню позже, объем используемой или сохраненной памяти вряд ли имеет значение. Более важные соображения:

    • Насколько легко будет изменить мой код в будущем?
    • Насколько гибким является мой код для меняющихся обстоятельств
    • Насколько легко тестировать мой код?

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

    Почему память не имеет значения

    Из-за скорости современных компьютеров производительность, которую вы испытываете между двумя версиями, должна редко меняться. Дисковый ввод-вывод чаще всего является проблемой, чем память. Если ваш сервер работает с ОЧЕНЬ небольшим объемом памяти, и вы ожидаете очень большой громкости, тогда эффективность памяти вашего кода будет более важной, чем если бы вы имели умеренный объем и умеренную память.

    Чтобы взглянуть на вещи в перспективе, см. Эту статью об эффективности массивов в PHP. Вынос? Несмотря на то, что массивы PHP5 ужасно неэффективны, даже массив из 100 000 целых чисел будет занимать около 14 миллионов . Это LOT, но учитывая, что средний PHP-скрипт имеет ограничение памяти 128M , а минимальные рекомендации сервера требуют около 2 ГБ памяти, это внезапно выглядит по-другому.

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

    Несмотря на это, в ситуации, когда вы изучаете архитектурный выбор с самого начала, я настоятельно рекомендую шаблон дизайна. А именно, шаблон проектирования зависимостей впрыска . Это связано с рядом причин, включая гибкость кода и модульное тестирование, но также имеет удобную память. Из-за этого, вероятно, это будет считаться лучшей практикой по любому из двух вариантов, которые вы рекомендуете.

    Почему не статические свойства

    С самого начала самым простым путем является использование статических свойств. Однако, по моему опыту, самый простой маршрут – это не всегда лучший маршрут и часто может быть самым сложным для обслуживания. Одна из проблем заключается в том, что ваши функции / методы, вероятно, будут вызывать другой класс внутри. В качестве примера давайте создадим два класса: MyFooClass и DoStuff и посмотрим, как они могут взаимодействовать по умолчанию.

     class MyFooClass { public static $Ms = array(82, 83, 84, 104, 106, 107, 109, 140, 190); public static $Gs = array(0, 1, 20, 21, 28, 90, 91, 92); public static $Ts = array(0, 1); } class DoStuff { public function oneOfThousands() { $array = MyFooClass::$Gs; //... do stuff } } 

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

    Инъекция зависимости к спасению!

    Как и все шаблоны проектирования, Dependency Injection решает проблему. В этом случае проблема легко и эффективно передаёт значения между несколькими функциями / методами, не жертвуя гибкостью. Используя базовый шаблон DI, вы можете получить свои массивы, инициализированные в нестатических свойствах, и передать один объект, содержащий это свойство массива, в каждую часть вашего кода. Это позволит вам устранить вашу озабоченность по поводу производительности.

    Пример:

     class MyFooClass { private $Ms, $Gs, $Ts; public function __construct() { $this->Ms = array(82, 83, 84, 104, 106, 107, 109, 140, 190); $this->Gs = array(0, 1, 20, 21, 28, 90, 91, 92); $this->Ts = array(0, 1); } public function checkFileGcodeFormat() { if (! ($this->hasM() && $this->hasNoXYZ() && in_array($this->M, $this->Ms)) || ($this->hasG() && in_array($this->G, $this->Gs)) || ($this->hasT() && $this->hasNoXYZ() && in_array($this->T, $this->Ts)) ) return false; else return true; } } // DI here: $foo = new MyFooClass(); $bar = new MyBarClass(); $bar->setArrays($foo); //alternative DI approach - parameters in constructor $bar = new MyBarClass($foo); 

    В MyBarClass вы назначаете объект MyFooClass для свойства $foo . Затем вы можете вызвать любой общедоступный метод или свойство из этого объекта с помощью $this->foo . Например: $this->foo->checkFileGcodeFormat() .

    С этим шаблоном проектирования:

    • Когда вы захотите разработать новый модульный тест, это будет намного проще.
    • Если вам когда-либо понадобится / нужно реализовать подмножество Gcodes для приложения, просто передайте другой объект с разными значениями массива.
    • Аналогичным образом, если вы хотите протестировать новый Gcode в новом классе, не вводя его в каждую часть вашего скрипта, вы можете.
    • Расходуемая память – это размер указателя в PHP (который совпадает с размером указателя в C … 8 байтах в 64-битной архитектуре).

    Вывод

    • Если вы можете, я бы рекомендовал использовать шаблон проектирования инъекций зависимостей.
    • Вы можете выбрать статическое свойство для лучшего объема памяти (обратите внимание: это не является взаимоисключающим для инъекций зависимостей, но менее важно, если вы используете инъекцию зависимостей).
    • В стандартной настройке веб-сервера с умеренным трафиком маловероятно, что ваше потребление памяти будет иметь значение, независимо от того, используете ли вы статические свойства или вызываете массив из функции.

    Если вы действительно понимаете, как производительность кода может быть измерена, вы должны ознакомиться с нотой Big-O.

    Что такое Big-O? Big-O cheat sheet

    Кроме этого, определите их как защищенные базовые свойства класса для статических данных.

     class foo { protected static $bar = array(); } 
     private static $Ms = array(82, 83, 84, 104, 106, 107, 109, 140, 190); private static $Gs = array(0, 1, 20, 21, 28, 90, 91, 92); private static $Ts = array(0, 1); public function checkFileGcodeFormat(){ if (! ($this->hasM() && $this->hasNoXYZ() && in_array($this->M, self::$Ms)) || ($this->hasG() && in_array($this->G, self::$Gs)) || ($this->hasT() && $this->hasNoXYZ() && in_array($this->T, self::$Ts)) ) return false; else return true; }