PHPのif文で比較する時、暗黙的型変換と!!で真偽値化した時の速度比較

## TL;DR
**`PHPで条件分岐させるには、そのまま暗黙的型変換させた方がよい`**

## 前書き
PHPには暗黙的型変換があるため、if文に真偽値以外を指定しても正常に動作[^1]します。

以前、参加させていただいた現場で
*「PHPは条件式に文字列を入れても動くけど、そのままだと暗黙的型変換の処理が入るので、真偽値に変換してから処理させた方が速い」 *
と言われたことがありました。(曰く、渡された値が真偽値だと暗黙的型変換を飛ばして処理するから速いらしい)
当時は深く考えずに従っていましたが、そういえば速度比較の実験をしたことがないなと気づき、実際にやってみました。

## 実験内容
文字列をそのまま条件にした場合と論理演算子を付けた場合とで処理速度の差を調べました。
（条件分岐１回だけでは速度が速すぎて分かりづらいので、それぞれ1000万回ループした時間を計測する）
`for (1000万回) { if (xxx) {;} }`

環境については一番下に書きます。

## 実験
### 実験1. 文字列
#### 計測内容
```
$x = 'A';
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.42295598983765 | 0.53760409355164 | 1.2710639084648
2回目 0.42900896072388 | 0.54602599143982 | 1.2727612740734
3回目 0.58640789985657 | 0.60731482505798 | 1.035652529931
4回目 0.42169904708862 | 0.51743507385254 | 1.2270245271477
5回目 0.52780699729919 | 0.58111786842346 | 1.1010044796622
6回目 0.4123899936676 | 0.5982871055603 | 1.4507798800825
7回目 0.42883205413818 | 0.46264100074768 | 1.0788395976542
8回目 0.49562096595764 | 0.55326104164124 | 1.1162987033291
9回目 0.42280101776123 | 0.50467491149902 | 1.1936463970009
```
**`暗黙的型変換での指定 < 真偽値に変更して指定`**

### 実験2. 整数
#### 計測内容
```
$x = 1;
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.53654193878174 | 0.55427598953247 | 1.0330524968673
2回目 0.54995012283325 | 0.52258706092834 | 0.95024446623643
3回目 0.63769197463989 | 0.63388586044312 | 0.99403142214715
4回目 0.53120803833008 | 0.62079620361328 | 1.1686498675073
5回目 0.60586214065552 | 0.51998591423035 | 0.85825781037868
6回目 0.48295307159424 | 0.51465702056885 | 1.065646024095
7回目 0.53456997871399 | 0.57646489143372 | 1.0783712411619
8回目 0.47688698768616 | 0.53011393547058 | 1.1116133364063
9回目 0.53528308868408 | 0.52036094665527 | 0.9721228965677
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**

#### 計測内容
```
$x = 0;
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.45366597175598 | 0.46893405914307 | 1.0336549098624
2回目 0.45791792869568 | 0.45463681221008 | 0.99283470622139
3回目 0.45413994789124 | 0.45538282394409 | 1.0027367688278
4回目 0.47224712371826 | 0.45879292488098 | 0.97151025774101
5回目 0.46725988388062 | 0.46424794197083 | 0.99355403274774
6回目 0.46070003509521 | 0.4610857963562 | 1.0008373371643
7回目 0.46788382530212 | 0.47234606742859 | 1.0095370728483
8回目 0.45703101158142 | 0.45940494537354 | 1.005194251007
9回目 0.45609092712402 | 0.46078586578369 | 1.0102938655002
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**


### 実験3. 浮動小数点数
#### 計測内容
```
$x = 0.987654321;
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.45832180976868 | 0.45277404785156 | 0.98789548784529
2回目 0.45108604431152 | 0.44858717918396 | 0.99446033598451
3回目 0.45072102546692 | 0.45461201667786 | 1.0086328149589
4回目 0.45229291915894 | 0.44836401939392 | 0.99131337326191
5回目 0.45630097389221 | 0.44867491722107 | 0.98328722245299
6回目 0.45319604873657 | 0.44980788230896 | 0.99252383943537
7回目 0.4566011428833 | 0.45077300071716 | 0.98723581345124
8回目 0.45106911659241 | 0.44858694076538 | 0.99449712752277
9回目 0.44977307319641 | 0.44870615005493 | 0.99762786346035
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**

### 実験4. 真偽値
#### 計測内容(true)
```
$x = true;
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.44422006607056 | 0.43824195861816 | 0.98654246417711
2回目 0.45388603210449 | 0.43067598342896 | 0.94886370799313
3回目 0.44796204566956 | 0.43552207946777 | 0.97222986562804
4回目 0.43387508392334 | 0.43848419189453 | 1.0106231220505
5回目 0.43495202064514 | 0.43905091285706 | 1.0094237801352
6回目 0.43521595001221 | 0.43006896972656 | 0.98817373240687
7回目 0.42762994766235 | 0.42465710639954 | 0.99304809852755
8回目 0.43169784545898 | 0.42393898963928 | 0.98202711479495
9回目 0.42658996582031 | 0.4259021282196 | 0.99838759076439
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**

#### 計測内容(false)
```
$x = false;
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.43245792388916 | 0.45596098899841 | 1.0543476343268
2回目 0.51344895362854 | 0.44799184799194 | 0.87251487187964
3回目 0.44170999526978 | 0.44388008117676 | 1.0049129200838
4回目 0.46668100357056 | 0.46692395210266 | 1.0005205880039
5回目 0.45312309265137 | 0.47339701652527 | 1.0447426410234
6回目 0.44755101203918 | 0.43737101554871 | 0.97725399738435
7回目 0.43689513206482 | 0.43476891517639 | 0.99513334726716
8回目 0.45366501808167 | 0.45786309242249 | 1.0092536875745
9回目 0.47272491455078 | 0.46173310279846 | 0.97674797453237
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**

### 実験4. 配列
#### 計測内容(空配列)
```
$x = []
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.47338700294495 | 0.47073698043823 | 0.99440199563945
2回目 0.47693490982056 | 0.46730518341064 | 0.97980913912648
3回目 0.47239995002747 | 0.47227692604065 | 0.99973957663033
4回目 0.46277499198914 | 0.48173809051514 | 1.0409769301588
5回目 0.5937340259552 | 0.58697509765625 | 0.98861623554743
6回目 0.52379703521729 | 0.65919494628906 | 1.2584930840924
7回目 0.55998396873474 | 0.58978295326233 | 1.0532139957416
8回目 0.49033308029175 | 0.47464108467102 | 0.96799727317726
9回目 0.47041201591492 | 0.50262999534607 | 1.0684888530504
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**

#### 計測内容(多次元配列)
```
$x = ['a'=>['b'=>['c'=>'d']]];
if ($x) {;}　// ① 暗黙的型変換での指定
if (!!$x) {;} // ② 真偽値に変更して指定
```

#### 計測結果
```
① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合
1回目 0.46190595626831 | 0.47691893577576 | 1.0325022427265
2回目 0.47171902656555 | 0.46748399734497 | 0.99102213609781
3回目 0.48003792762756 | 0.48539113998413 | 1.0111516445857
4回目 0.46184802055359 | 0.46015000343323 | 0.99632342882335
5回目 0.46872305870056 | 0.47194695472717 | 1.0068780401706
6回目 0.46079087257385 | 0.46298098564148 | 1.0047529436844
7回目 0.46109390258789 | 0.45801615715027 | 0.99332512223573
8回目 0.4629819393158 | 0.47691798210144 | 1.0301006186251
9回目 0.47094893455505 | 0.46756291389465 | 0.99281021696418
```
**`暗黙的型変換での指定 ≒ 真偽値に変更して指定`**


## まとめ
文字列のみ、明示的にbool型にした場合より、 なにもせずPHPに型変換させた方が1.0~1.3倍ほど速いという結果になりました。  
それ以外は特にどちらが速いというのはありませんでした。
**-> `PHPで条件分岐させるには、そのまま暗黙的型変換させた方がよい`**

-----
## 環境
- OS  
<img src="https://qiita-image-store.s3.amazonaws.com/0/15368/c9779e09-9b8d-e585-5ef6-2183d3bc44e8.png" width=30%>
- PHP  
<img src="https://qiita-image-store.s3.amazonaws.com/0/15368/f28ceadf-672e-7a15-071a-689f549fc2c9.png" width=30%>
- 計測方法  
以下の`timeMeasurement`関数の第一引数に実処理を書くと、その処理の処理時間が返ってくる。  
その処理時間を計測。

```php
<?php

echo "① 暗黙的型変換での指定 | ② 真偽値に変更して指定 | ① に比べて②　の処理時間割合", PHP_EOL;

for ($count=1; $count<10; $count++) {
    // 式を暗黙変換してもらって条件指定
    $processingTime1 = timeMeasurement(function () use($x) {
        if ($x) {;} 
    }); 

    // 式を真偽値にしてから条件指定
    $processingTime2 = timeMeasurement(function () use($x) {
        if (!!$x) {;} 
    }); 

    echo "${count}回目 ";
    echo $processingTime1, " | ", $processingTime2, " | ";
    echo ($processingTime2 / $processingTime1);
    echo PHP_EOL;
}


function timeMeasurement (callable $process, int $count = 10000000): float {
    $startTime = microtime(true);
    for ($i=0; $i<$count; $i++) {
        $process();
    }   
    $endTime = microtime(true);

    $processingTime = ($endTime - $startTime);
    return $processingTime;
}

```

[^1]: 笑うところ