我有一些数组存储某些3D打印机命令的可能参数。我用它来检查命令是否合法。我很困惑我应该把这些数组放在哪里。这些数组只能在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;
}
答案 0 :(得分:30)
TL; DR :使用类常量可获得最佳性能(请参阅答案末尾)。
让我们看看不同版本的性能特征(及其原因):
静态属性中的数组是在编译时非常快速地创建的,无需VM的参与。虽然访问静态属性比访问普通变量要慢一些,但仍然比在每次运行时重新创建数组要快得多。
在任何情况下,每次运行都会在运行时重新创建正常函数中的数组。在VM中运行时创建意味着每个元素在各个操作码中逐个添加,这意味着相当多的开销(特别是如果数组大于1-2个元素)。
正常函数[一般]中的数组创建速度稍快一些,因为通常会加快数组创建(HashTable处理中的优化)。如果它是所有常量值,则将其缓存在内部常量值数组中,但在每次访问时重复。但是,执行直接高度专业化的复制操作显然比在PHP 5中逐个添加元素更快。
Opcache在内部将它们标记为IMMUTABLE,这允许直接访问[因此您可以使用opcache获得全速]。 (另见https://blog.blackfire.io/php-7-performance-improvements-immutable-arrays.html)
数组本身总是缓存在内部常量值数组中,具有写时复制语义。
现在使用静态属性比较慢,因为查找静态属性的性能低于对变量的简单写入。 [直接访问变量没有额外的开销。]
另请注意,自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;
}
答案 1 :(得分:10)
我认为定义数组属性更有意义,因为方法内定义的数组是在每次调用时创建的。
但我想提出另一点。如果您有相当大的数组来查找值,更重要的是如何构建它们。我建议这样做:
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
上工作也更快。
答案 2 :(得分:5)
如果它们从未改变,那么您应该格式化为FILE* file = fopen("public.pem","r");
if (file != nullptr)
{
//This can be changed to this for the same results:
//RSA* rsa = PEM_read_RSAPublicKey(file, nullptr, nullptr, nullptr);
RSA* rsa = PEM_read_RSA_PUBKEY(file, nullptr, nullptr, nullptr);
//etc...
}
。在编译时有烘焙,因此将是最快的。
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)) {
...
}
}
类似,但在编译时烘焙,因此比定义和变量数组略快。答案 3 :(得分:5)
一句话外卖:类常量可能更快,但内存可能无关紧要,使用Dependency Injection设计模式将更有效,更灵活。< / p>
虽然类常量或静态属性比在函数中创建数组要快(参见bwoebi's answer),因为它只在内存中构建一次并且可以多次访问,但绝不是可用的最有效方法,或解决OP旨在解决的根本问题的推荐方法。
如果您确定以后没有数据会发生变化,或者您永远不会想要在不同时间使用不同的数据集,即使是测试,那么您也许可以获得无论如何,远离这种方法。如果您想要更灵活的代码,类常量或静态属性可能会导致一些严重的问题。正如我稍后解释的那样,使用或保存的内存量不太可能重要。更重要的考虑因素是:
- 将来修改我的代码有多容易?
- 我的代码对于不断变化的环境有多灵活
- 对我的代码进行单元测试有多容易?
在提交内存效率最高的路由之前,请务必平衡其他形式的效率,例如开发和调试时的效率。
由于现代计算机的速度,您在两个版本之间遇到的性能应该很少有所作为。磁盘I / O通常是一个问题而不是内存。如果您的服务器运行的内存非常少而且您的数量非常大,那么代码的内存效率将比您拥有适中的内存和适度的内存更重要。
为了正确看待问题,请参阅this article关于PHP中数组的效率。外卖?尽管PHP5阵列的效率非常低,但即使是100,000个整数的数组也会占用 14M 。这很多,但考虑到平均PHP脚本的内存限制为 128M ,并且最小服务器建议需要大约2 GB的内存,这突然看起来不同。
这意味着如果代码的其余部分效率低下,或者与低内存相比具有高容量,则应该担心这一点。这将导致您的应用程序变慢和/或系统崩溃。
无论如何,在您从一开始就探索建筑选择的情况下,我强烈推荐一种设计模式。即,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
}
}
现在,如果你想为不同的目的插入不同的数组值,或者你想用更少或更多的设置进行单元测试,那么很复杂。
与所有设计模式一样,依赖注入解决了一个问题。在这种情况下,问题是在不牺牲灵活性的情况下容易且有效地在多个函数/方法之间传递值。使用基本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()
。
使用这种设计模式:
答案 4 :(得分:4)
如果您真的想了解如何衡量代码性能,那么您应该熟悉Big-O表示法。
What is Big-O? Big-O cheat sheet
除此之外,将它们定义为静态数据的受保护类基本属性。
class foo
{
protected static $bar = array();
}
答案 5 :(得分: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;
}