35

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

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, $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;
}
10
  • 3
    here its a matter of preference. but i will personally go with setting it as a class property.
    – Ghostff
    Commented Jul 27, 2016 at 3:25
  • is there some performance issues. Commented Jul 27, 2016 at 3:28
  • No. but second procedure might have a slight more processing time.
    – Ghostff
    Commented Jul 27, 2016 at 3:36
  • 1
    NO. set as property and call only on page load OR varibale each time the checkFileGcodeFormat is called
    – Ghostff
    Commented Jul 27, 2016 at 3:53
  • 2
    @ChrysUgwu "but second procedure might have a slight more processing time." I measured the two versions and found that the second (with static properties) twice as fast than first (PHP 5.5.36)
    – cske
    Commented Nov 30, 2016 at 12:57

6 Answers 6

34
+50

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;
}
10
+100

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.

2
  • nicely! better now. Commented Dec 9, 2016 at 21:20
  • Good advice (+1) … even though it doesn't directly answer the question at hand (where => see my answer), it probably is helpful as OP uses in_array.
    – bwoebi
    Commented Dec 9, 2016 at 23:52
6

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.
3
  • Doesn't dependency injection get a bit silly when you have a ton of stuff on most of your business logic classes? They'll likely all have error type constants, log message constants, all kinds of constants. As long as those classes are basically data containers, it seems sensible to reference them directly. Classes effectively namespacing and housing data values don't seem like they have any problem with testing, etc. Such containers aren't other objects/classes to be tested. As long as you didn't make a typo, they're perfectly good to go.
    – ahnbizcad
    Commented Nov 13, 2020 at 6:22
  • 1
    Yeah, this answer is 4 years old. I would argue that generally such classes have static methods and are therefore stateless, so you probably wouldn't use DI anyway. Just reference the static class's values MyBarClass::foo(). If you need to instantiate the class, I'd ask why that's a requirement, and probably go back to DI. Even a log class, while it can probably be instantiated, should be pretty carefully designed to be encapsulated and not injected directly.
    – smcjones
    Commented Nov 14, 2020 at 1:42
  • So a rough rule of thumb is that DI is primarily applied to instances (and variables), not stateless classes
    – ahnbizcad
    Commented Nov 24, 2020 at 20:40
5

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.
5
  • I think you answer combine with @sevavietl's is much better, is it? Commented Dec 12, 2016 at 1:35
  • 1
    Indeed: as a general rule, use isset() as well. Not specifically tested speed with consts, but you can do const VAR = [23 => true, 24 => true]; etc so should be easy to test.
    – Robbie
    Commented Dec 12, 2016 at 1:55
  • Hmm, does that add anything to what I said in my answer? Or did I miss something?
    – bwoebi
    Commented Dec 12, 2016 at 14:21
  • @bwoebi - I didn't see your footnote about const, sorry. Why not start with the answer (use const) and then go on about less performant alternatives? Also const have been around outside class longer that 5.6, and that would be more performant than building into the class (you can still declare in same file, although it's a mute point as you should be on 5.6 now). But simple answer is I missed it when reading yours.
    – Robbie
    Commented Dec 12, 2016 at 21:46
  • @Robbie No problem, and good point; I've added a small TL;DR. Also, while const has been longer than 5.6, you can only assign arrays to a constant as of 5.6. It won't even compile with 5.5 and below.
    – bwoebi
    Commented Dec 13, 2016 at 12:01
4

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();
}
2
  • 1
    Some more info about prefer static date here will be appreciated much! +1 for the Big-O sheet. Thank you! Commented Dec 9, 2016 at 9:21
  • 1
    You should have said why using static properties? (benchmarks? knowledge about internals?) … and Big-O is not really related to this…
    – bwoebi
    Commented Dec 9, 2016 at 16:32
1
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;
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.