♠
php8 で追加され使えるようになった match 式。
従来の switch に似たようなものではあるけれど、一番の違いは switch が == による弱い比較であることに対して、match は型と値の一致チェックである === によるものであること。これにより弱い比較により内在する想定外のバグのために使いづらいというか、使用不可としていた switch の代用として使えるものということ。
match 式の書式は連想配列に似てる。
以下、php.net からの引用でまとめたもの。
match 式と switch 文の似て異なるいくつかの違い
match 式は 緩い比較により使えなかった switch 文の代替として考えられたものであろうから、その switch 文においての問題を払拭するように刷新されているのだと思われる。
- match 式の比較は、 switch 文が行う弱い比較ではなく、 厳密に値を比較(
===
) する
- match 式は値を返す( match 式の結果は、必ずしも使う必要はない )
- match 式の分岐は、switch 文のように後の分岐に抜けたりはしない
- match 式は、全ての場合を網羅していなければいけない
と、いうことでこの match 式。これはもしかしたら if で書くよりも速くなる気がして期待大である。ならば早速ベンチをやってみるしかない。で、もう一つ、使えるところが限られるけれど、if よりも断然高速な配列のルックアップテーブルを使った方法。これも交えてまずは単純な文字列を返す処理で試してみた。
if 文においてはその条件項が多くなればなるほど当然遅くなる。その場合、階層にできるなら階層化して、通過する条件項の数を極力減らすことが高速化の手段となる。それゆえ、条件が少ない場合と多い場合とで違いが出るであろうということが想像にやさしい。なので、条件が5個の場合と10個の場合で調べてみた。ループのカウンターから作った数値を指定した個数で割った余りの数で分岐させるというだけの単純なもの。
<?php
echo '<p> match 文字列 5pcs 10,000回</p>';
$result = array();
for ( $j = 1; $j < 11; ++$j ) {
$ansnum = '';
$gettime = array();
$ans = 0;
$gettime[0] = microtime( true );
for ( $i = 1; $i < 10001; ++$i ) {
$tar = ( $i * $j ) % 5;
$ret_val = match ( $tar ) {
0 => 'zero',
1 => 'one',
2 => 'two',
3 => 'three',
default => 'four',
};
$ansnum = $ret_val;
}
$gettime[1] = microtime( true );
$ans = $gettime[1] - $gettime[0];
$result[] = $ans;
echo '<p>result : ' . sprintf( '%0.5f', $ans ) . '秒</p>';
}
echo '<p>ave : ' . sprintf( '%0.5f', array_sum( $result ) / count ( $result ) ) . ' max : ' . sprintf( '%0.5f', max( $result ) ) . ' min : ' . sprintf( '%0.5f', min( $result ) ) . '</p>';
$result = array();
echo '<p> if 文字列 5pcs 10,000回</p>';
for ( $j = 1; $j < 11; ++$j ) {
$ansnum = '';
$gettime = array();
$ans = 0;
$gettime[0] = microtime( true );
for ( $i = 1; $i < 10001; ++$i ) {
$tar = ( $i * $j ) % 5;
if ( 0 === $tar ) {
$ret_val = 'zero';
} elseif ( 1 === $tar ) {
$ret_val = 'two';
} elseif ( 2 === $tar ) {
$ret_val = 'two';
} elseif ( 3 === $tar ) {
$ret_val = 'three';
} else {
$ret_val = 'four';
}
$ansnum = $ret_val;
}
$gettime[1] = microtime( true );
$ans = $gettime[1] - $gettime[0];
$result[] = $ans;
echo '<p>result : ' . sprintf( '%0.5f', $ans ) . '秒</p>';
}
echo '<p>ave : ' . sprintf( '%0.5f', array_sum( $result ) / count ( $result ) ) . ' max : ' . sprintf( '%0.5f', max( $result ) ) . ' min : ' . sprintf( '%0.5f', min( $result ) ) . '</p>';
$result = array();
echo '<p> Array-lookup-table 文字列 5pcs 10,000回</p>';
$funkname = array( 'zero', 'one', 'two', 'three', 'four', );
for ( $j = 1; $j < 11; ++$j ) {
$ansnum = '';
$gettime = array();
$ans = 0;
$gettime[0] = microtime( true );
for ( $i = 1; $i < 10001; ++$i ) {
$tar = ( $i * $j ) % 5;
$ansnum = $funkname[ $tar ];
}
$gettime[1] = microtime( true );
$ans = $gettime[1] - $gettime[0];
$result[] = $ans;
echo '<p>result : ' . sprintf( '%0.5f', $ans ) . '秒</p>';
}
echo '<p>ave : ' . sprintf( '%0.5f', array_sum( $result ) / count ( $result ) ) . ' max : ' . sprintf( '%0.5f', max( $result ) ) . ' min : ' . sprintf( '%0.5f', min( $result ) ) . '</p>';
$result = array();
echo '<br><br><br><p> match 文字列 10pcs 10,000回</p>';
for ( $j = 1; $j < 11; ++$j ) {
$ansnum = '';
$gettime = array();
$ans = 0;
$gettime[0] = microtime( true );
for ( $i = 1; $i < 10001; ++$i ) {
$tar = ( $i * $j ) % 10;
$ret_val = match ( $tar ) {
0 => 'zero',
1 => 'one',
2 => 'two',
3 => 'three',
4,5 => 'five',
6,7 => 'six',
8 => 'eight',
default => 'nine',
};
$ansnum = $ret_val;
}
$gettime[1] = microtime( true );
$ans = $gettime[1] - $gettime[0];
$result[] = $ans;
echo '<p>result : ' . sprintf( '%0.5f', $ans ) . '秒</p>';
}
echo '<p>ave : ' . sprintf( '%0.5f', array_sum( $result ) / count ( $result ) ) . ' max : ' . sprintf( '%0.5f', max( $result ) ) . ' min : ' . sprintf( '%0.5f', min( $result ) ) . '</p>';
$result = array();
echo '<p> if 文字列 10pcs 10,000回</p>';
for ( $j = 1; $j < 11; ++$j ) {
$ansnum = '';
$gettime = array();
$ans = 0;
$gettime[0] = microtime( true );
for ( $i = 1; $i < 10001; ++$i ) {
$tar = ( $i * $j ) % 10;
if ( 0 === $tar ) {
$ret_val = 'zero';
} elseif ( 1 === $tar ) {
$ret_val = 'two';
} elseif ( 2 === $tar ) {
$ret_val = 'two';
} elseif ( 3 === $tar ) {
$ret_val = 'three';
} elseif ( 4 === $tar or 5 === $tar ) {
$ret_val = 'five';
} elseif ( 6 === $tar or 7 === $tar ) {
$ret_val = 'six';
} elseif ( 8 === $tar ) {
$ret_val = 'eight';
} else {
$ret_val = 'nine';
}
$ansnum = $ret_val;
}
$gettime[1] = microtime( true );
$ans = $gettime[1] - $gettime[0];
$result[] = $ans;
echo '<p>result : ' . sprintf( '%0.5f', $ans ) . '秒</p>';
}
echo '<p>ave : ' . sprintf( '%0.5f', array_sum( $result ) / count ( $result ) ) . ' max : ' . sprintf( '%0.5f', max( $result ) ) . ' min : ' . sprintf( '%0.5f', min( $result ) ) . '</p>';
$result = array();
echo '<p> Array-lookup-table 文字列 10pcs 10,000回</p>';
$funkname = array( 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine' );
for ( $j = 1; $j < 11; ++$j ) {
$ansnum = '';
$gettime = array();
$ans = 0;
$gettime[0] = microtime( true );
for ( $i = 1; $i < 10001; ++$i ) {
$tar = ( $i * $j ) % 10;
$ansnum = $funkname[ $tar ];
}
$gettime[1] = microtime( true );
$ans = $gettime[1] - $gettime[0];
$result[] = $ans;
echo '<p>result : ' . sprintf( '%0.5f', $ans ) . '秒</p>';
}
echo '<p>ave : ' . sprintf( '%0.5f', array_sum( $result ) / count ( $result ) ) . ' max : ' . sprintf( '%0.5f', max( $result ) ) . ' min : ' . sprintf( '%0.5f', min( $result ) ) . '</p>';
?>
CopyExpand‹›‹›‹›‹›‹›
それぞれ10,000回のループで得られる結果を、各10回づつとり、その平均値、最大、最小の値が下の表となる。テスト環境はといえば。
Windows10 Pro 64bit
Aapche2.4
php8.2
> Firefox Developer Edition 109.0b7 (64bit)
5pcs
match
if
lookup table
ave
.00053
.00061
.00032
max
.00067
.00070
.00034
min
.00045
.00040
.00031
10pcs
match
if
lookup table
ave
.00045
.00090
.00030
max
.00056
.000128
.00031
min
.00043
.00039
.00030
> Chrome canary 111.0.5509.0(64bit)
5pcs
match
if
lookup table
ave
.00047
.00078
.00031
max
.00060
.00135
.00032
min
.00042
.00039
.00031
10pcs
match
if
lookup table
ave
.00044
.00093
.00033
max
.00046
.00157
.00041
min
.00042
.00061
.00030
5pcs とある表は条件項が5個、10pcs は10個のテスト。予想通り if においては条件分岐が増えれば遅くなる。が match はほぼ影響がないよう。array lookup table においても同様でこちらも数が増えようが全く関係ないようである。
そして今度は、各条件により呼び出す関数を指定する場合。
これまで、こういう場合は、ほぼ if 文を使っていたというか、使うしかなかったわけで。これも配列による lookup table で可変関数を使って関数を呼び出すことは出来るけれど、たしか以前にベンチしたときは、 if 文よりもかなり遅くなってしまったという記憶があり、使うことがなかったということであるけれど。
ちなみに可変関数に関してはここにあり -> php.net 可変関数
php は、変数名の後に括弧が付いている場合、その値が何であろうと同名の関数を探し実行を試みるということ。簡単にいえば、変数に入れてある文字列にて関数を指定し呼び出せるということ。配列の lookup-table と組み合わせて使えば速そうなんだけれどね・・・。
こちらもそれぞれ10,000回のループで得られる結果を、各10回づつとり、その平均値、最大、最小の値が下の表となる。
> Firefox Developer Edition 109.0b7 (64bit)
\
match
if
lookup table 可変関数
ave
.00090
.00101
.00108
max
.00118
.00144
.00143
min
.00083
.00072
.00092
> Chrome canary 111.0.5509.0(64bit)
\
match
if
lookup table 可変関数
ave
.00087
.00106
.00114
max
.00093
.00158
.00166
min
.00083
.00074
.00095
関数呼び出しに関しては match を使うべきのよう。やはりというか可変関数を使うと遅くなってしまう。
各条件が単純なリテラルで得られ( 配列のキーとなり得るものという意味 )、かつ不確定なものが無く、関数呼び出しではない場合のみ、配列での lookup-table を使い、あとは迷うことなく match を使うべきという結果である。
Comments feed
Trackback URL : https://strix.main.jp/wp-trackback.php?p=173742