Home » Php » php – Where to put arrays with constant value that will be accessed many times?

php – Where to put arrays with constant value that will be accessed many times?

Posted by: admin April 23, 2020 Leave a comment

Questions:

I have some arrays storing the possible parameters for some 3D printer commands. I use this to check if the command is legal. I am confused about where I should put these arrays. These arrays will only be accessed in the formatcheck function, and the function will be called many times as there are thousands of commands to check. Should I put these in the formatcheck function as variables or at the beginning of the class the formatcheck function is in, as private static variables?

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

or:

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;
}
How to&Answers:

TL;DR: Use a class constant for maximum performance (see at the end of the answer).

Let’s look at the performance characteristics of the different versions (and why):

PHP 5

Arrays in static properties are created at compile time, very quickly, without involvement of the VM. Accessing static properties though is a bit slower than accessing normal variables, but still much faster than recreating the array on every run.

Arrays in normal functions get re-created at run-time with every run, in any case. And creation at run-time in VM means that every element is added one-by-one, in individual opcodes, which means quite a bit of overhead (especially if the array is larger than just 1-2 elements).

PHP 7.0

Arrays in normal functions [in general] are created a bit faster due to array creation in general being sped up (optimizations in HashTable handling). If it’s all constant values, it’s cached in the internal constant values array, but duplicated upon each access. However doing a direct highly specialized copying action is obviously faster than adding elements one-by-one to the array like in PHP 5.

Opcache is marking them as IMMUTABLE internally, which allows direct access [so you get full speed with opcache]. (See also https://blog.blackfire.io/php-7-performance-improvements-immutable-arrays.html)

PHP 7.1

Arrays are natively always cached in the internal constant values array, with copy-on-write semantics.

Now using a static property is slower as looking up a static property is less performant than a simple write to a variable. [Direct access to a variable has no extra overhead.]


Also note that since PHP 5.6 you can declare (class) constants with the value of an array. PHP 7.1 allows direct substitution of class constants of the same class and will add the array directly to the internal constant values array for direct usage with in_array.

I.e. the fastest code is (with 7.1 at least):

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

Answer:

I think that the defining array a property is making more sense, as arrays defined inside the methods are created on every call.

But I want to make another point. If you have rather large arrays to look up value in, it is more important how you structure them. I would suggest this:

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

Having this structure you can use isset (O(1)) instead of in_array (O(n)).

Here are some other questions regarding isset vs. in_array:

And here are some posts with benchmarks:

The last one is rather old, but I think the ratio holds.

So to sum up. When you use isset the searching time is constant (it actually might vary, but this can be ignored). When you use in_array the searching time depends on element position (and so on array size). Even on small arrays isset works faster.

Answer:

If they NEVER change then you should be formatting as const. There are baked in at compile time and therefore will be the fastest.

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)) {
    ...
}

or

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)) {
        ...
    }
}

Some notes:

  • const are like define but baked in at compile time, so slightly faster than defines and variable arrays.
  • you can define const at a global level or at the class level (http://php.net/manual/en/language.oop5.constants.php). As from php 7.1 you cna also declare class consts as private/public/protected etc.
  • I’ve used capital letters for defines which is an unofficial standard, but not a requirement.

Answer:

The one sentence takeaway: Class constants may be quicker, but memory probably won’t matter, and using the Dependency Injection Design Pattern will be more memory efficient and flexible.

While a class constant or static property will be faster than creating an array in a function (see bwoebi’s answer) because it’s built in memory once and can be accessed multiple times, it is in no way the most efficient method available, or the recommended way to solve the root problem the OP is aiming to solve.

If you are certain that no data is ever going to change in the future, or you’re never going to want to use different sets of data at different times, even for testing, then you may be able to get away with this method anyway. If you want more flexible code, class constants or static properties can cause some serious problems. As I’ll explain later, the amount of memory used or saved is unlikely to matter. More important considerations are:

  • How easy is it going to be to modify my code in the future?
  • How flexible is my code to changing circumstances
  • How easy is it to unit test my code?

Before committing to the most memory efficient route, be sure to balance other forms of efficiency, such as efficiency of your time in developing and debugging.

Why Memory may not matter

Due to the speed of modern computers, the performance hit you experience between the two versions should rarely make a difference. Disk I/O is more often an issue than memory. If your server is operating on a VERY small amount of memory and you expect very high volume, then the memory efficiency of your code will be more important than if you have moderate volume and moderate memory.

To put things in perspective, see this article on efficiency of arrays in PHP. The takeaway? Even though PHP5 arrays are horribly inefficient, even an array of 100,000 integers will take up about 14M. That’s a LOT, but considering that the average PHP script has a memory limit of 128M, and the minimum server recommendations call for about 2 GB of memory, this suddenly looks different.

That means that you should worry about this if the rest of your code is inefficient, or you have high volume compared to low memory. That will cause your application to slow down and/or your system to crash.

Regardless, in a situation where you’re exploring architectural choices from the beginning, I would strongly recommend a design pattern. Namely, the Dependency Injection design pattern. This is for a number of reasons, including code flexibility and unit testing, but also has a friendly memory footprint. Because of this, it would probably be considered best practices over either of the two options you are recommending.

Why not static properties

At the outset the easiest route is to use static properties. However, in my experience, the easiest route is not always the best route, and can frequently be the hardest to maintain. One problem here is that your functions/methods will probably be calling another class within. As an example, let’s create two classes: MyFooClass and DoStuff, and see how they might interact by default.

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

Now, if you ever want to insert different array values for different purposes, or you want to unit test with fewer or more settings, complications abound.

Dependency Injection to the Rescue!

Like all design patterns, Dependency Injection solves a problem. In this case, the problem is easily and efficiently passing values between multiple functions/methods without sacrificing flexibility. Using a basic DI pattern, you can get your arrays initialized in non-static properties and pass a single object containing this array property to every part of your code. That would allow you to eliminate your concern about performance.

Example:

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

In your MyBarClass, you are assigning a MyFooClass object to a property $foo. You can then call any public method or property from this object with $this->foo. For example: $this->foo->checkFileGcodeFormat().

With this design pattern:

  • When you want to develop a new unit test, it will be much easier to do so.
  • If you ever want/need to implement a subset of Gcodes for an application, just pass a different object with different array values.
  • Similarly, if you want to test a new Gcode on a new class without introducing it to every part of your script, you can.
  • The memory expended is the size of a pointer in PHP (which is same as the size of a pointer in C… 8 bytes in a 64 bit architecture).

Conclusion

  • If you can, I would recommend using the Dependency Injection Design Pattern.
  • You can choose a static property for a better memory footprint (note: This is not mutually exclusive of Dependency Injection, but is less important if you use Dependency Injection).
  • In a standard web server setup, with moderate traffic, it is unlikely your memory consumption will matter, whether you use static properties or call an array from within a function.

Answer:

If you are really looking into understanding how code performance can be measured, you should get familiar with Big-O notation.

What is Big-O?
Big-O cheat sheet

Other than that, define them as protected class base properties for static data.

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

Answer:

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