Подтвердить что ты не робот

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

У меня есть несколько массивов, сохраняющих возможные параметры для некоторых 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;
}
4b9b3361

Ответ 1

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;
}

Ответ 2

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

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

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 против in_array:

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

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

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

Ответ 3

Если они НИКОГДА не изменяются, вы должны форматировать как 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 и т.д.
  • Я использовал заглавные буквы для определений, который является неофициальным стандартом, но не является обязательным.

Ответ 4

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

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

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

  • Насколько легко будет модифицировать мой код в будущем?
  • Насколько гибким является мой код для меняющихся обстоятельств.
  • Насколько легко это сделать unit test мой код?

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

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

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

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

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

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

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

С самого начала самым простым путем является использование статических свойств. Однако, по моему опыту, самый простой маршрут - это не всегда лучший маршрут и часто может быть самым сложным для обслуживания. Одна из проблем заключается в том, что ваши функции/методы, вероятно, будут вызывать другой класс внутри. В качестве примера позвольте создать два класса: 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
    }
}

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

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

Как и все шаблоны проектирования, 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().

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

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

Заключение

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

Ответ 5

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

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

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

class foo
{
    protected static $bar = array();
}

Ответ 6

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;
}