お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。

Node.jsとGoogle Chrome, Microsoft Edgeを用いて、try-catchとif文でエラー処理にかかる時間がどのくらい違うのかを調べた。

計測手法

次の4パターンを100万回実行した結果を記載している。

  1. Result型としてエラーオブジェクトをreturnし、if文で判定する方式
  2. Result型としてエラーインスタンスをreturnし、if文で判定する方式
  3. エラーオブジェクトをthrowし、try-catchで判定する方式
  4. エラーインスタンスをthrowし、try-catchで判定する方式

確認に使用したコード:https://gist.github.com/Lycolia/304bc9e825e821c2d582f3ef9f700817

計測結果

CPUによって処理速度がかなり変動するが、いずれの環境でも処理速度の速さは以下の通り。

  1. Result型としてエラーオブジェクトをreturnし、if文で判定する方式
  2. エラーオブジェクトをthrowし、try-catchで判定する方式
  3. Result型としてエラーインスタンスをreturnし、if文で判定する方式
  4. エラーインスタンスをthrowし、try-catchで判定する方式

計測に使用したCPU

CPU コア スレッド クロック L2キャッシュ L3キャッシュ
Intel Core i7 13700 16 24 5.20GHz 24.00MB 30.00MB
Intel Core i7 1265U 10 12 4.80GHz 6.50MB 12.00MB
AMD Ryzen 5 5600G 6 12 3.90GHz 3.00MB 16.00MB

Intel Core i7 13700端末

16C24T, 5.20GHz, L2 24.00MB, L3 30.00MB。ミドルタワーPC。

Node.js v16.20.2

処理環境

Env Ver
CPU Intel Core i7 13700
OS Ubuntu 20.04.6 LTS (WSL2)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 802
Result型としてエラーインスタンスをreturnし、if文で判定する方式 5,942
エラーオブジェクトをthrowし、try-catchで判定する方式 1,847
エラーインスタンスをthrowし、try-catchで判定する方式 6,527
Node.js v18.19.1

処理環境

Env Ver
CPU Intel Core i7 13700
OS Ubuntu 20.04.6 LTS (WSL2)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 887
Result型としてエラーインスタンスをreturnし、if文で判定する方式 5,826
エラーオブジェクトをthrowし、try-catchで判定する方式 2,006
エラーインスタンスをthrowし、try-catchで判定する方式 6,464
Node.js v20.11.1

処理環境

Env Ver
CPU Intel Core i7 13700
OS Ubuntu 20.04.6 LTS (WSL2)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 720
Result型としてエラーインスタンスをreturnし、if文で判定する方式 5,375
エラーオブジェクトをthrowし、try-catchで判定する方式 1,690
エラーインスタンスをthrowし、try-catchで判定する方式 5,978
Microsoft Edge 121.0.2277.128

処理環境

Env Ver
CPU Intel Core i7 13700
OS Windows 11 Pro 22H2(22621.3155)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 4,246
Result型としてエラーインスタンスをreturnし、if文で判定する方式 12,329
エラーオブジェクトをthrowし、try-catchで判定する方式 11,985
エラーインスタンスをthrowし、try-catchで判定する方式 14,105
Google Chrome 122.0.6261.58

処理環境

Env Ver
CPU Intel Core i7 13700
OS Windows 11 Pro 22H2(22621.3155)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 3,507
Result型としてエラーインスタンスをreturnし、if文で判定する方式 10,793
エラーオブジェクトをthrowし、try-catchで判定する方式 10,482
エラーインスタンスをthrowし、try-catchで判定する方式 12,452

Intel Core i7 1265U端末

10C12T, 4.80GHz, L2 6.50MB, L3 12.00MB。ノートPC。

Node.js v16.20.2

処理環境

Env Ver
CPU Intel Core i7 1265U
OS Ubuntu 20.04.6 LTS (WSL2)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 972
Result型としてエラーインスタンスをreturnし、if文で判定する方式 11,416
エラーオブジェクトをthrowし、try-catchで判定する方式 2,288
エラーインスタンスをthrowし、try-catchで判定する方式 13,993
Node.js v18.19.1

処理環境

Env Ver
CPU Intel Core i7 1265U
OS Ubuntu 20.04.6 LTS (WSL2)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 1,102
Result型としてエラーインスタンスをreturnし、if文で判定する方式 11,333
エラーオブジェクトをthrowし、try-catchで判定する方式 2,441
エラーインスタンスをthrowし、try-catchで判定する方式 12,827
Node.js v20.11.1

処理環境

Env Ver
CPU Intel Core i7 1265U
OS Ubuntu 20.04.6 LTS (WSL2)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 888
Result型としてエラーインスタンスをreturnし、if文で判定する方式 10,881
エラーオブジェクトをthrowし、try-catchで判定する方式 2,269
エラーインスタンスをthrowし、try-catchで判定する方式 12,254
Microsoft Edge 121.0.2277.128

処理環境

Env Ver
CPU Intel Core i7 1265U
OS Windows 11 Pro 22H2(22621.3155)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 7,526
Result型としてエラーインスタンスをreturnし、if文で判定する方式 34,761
エラーオブジェクトをthrowし、try-catchで判定する方式 39,005
エラーインスタンスをthrowし、try-catchで判定する方式 65,752
Google Chrome 122.0.6261.58

処理環境

Env Ver
CPU Intel Core i7 1265U
OS Windows 11 Pro 22H2(22621.3155)

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 6,303
Result型としてエラーインスタンスをreturnし、if文で判定する方式 16,460
エラーオブジェクトをthrowし、try-catchで判定する方式 17,979
エラーインスタンスをthrowし、try-catchで判定する方式 23,378

AMD Ryzen 5 5600G端末

6C12T, 3.90GHz, L2 3.00MB, L3 16.00MB。ミニタワーPC。

この端末はOSがUbuntuであるため、これまでのWindows環境との単純比較はできない。

Node.js v16.20.2

処理環境

Env Ver
CPU AMD Ryzen 5 5600G
OS Ubuntu 22.04.3 LTS

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 1,786
Result型としてエラーインスタンスをreturnし、if文で判定する方式 11,242
エラーオブジェクトをthrowし、try-catchで判定する方式 3,880
エラーインスタンスをthrowし、try-catchで判定する方式 12,716
Node.js v18.19.1

処理環境

Env Ver
CPU AMD Ryzen 5 5600G
OS Ubuntu 22.04.3 LTS

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 1,933
Result型としてエラーインスタンスをreturnし、if文で判定する方式 11,094
エラーオブジェクトをthrowし、try-catchで判定する方式 3,839
エラーインスタンスをthrowし、try-catchで判定する方式 12,767
Node.js v20.11.1

処理環境

Env Ver
CPU AMD Ryzen 5 5600G
OS Ubuntu 22.04.3 LTS

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 1,714
Result型としてエラーインスタンスをreturnし、if文で判定する方式 10,741
エラーオブジェクトをthrowし、try-catchで判定する方式 3,444
エラーインスタンスをthrowし、try-catchで判定する方式 11,978
Microsoft Edge 121.0.2277.128

処理環境

Env Ver
CPU AMD Ryzen 5 5600G
OS Ubuntu 22.04.3 LTS

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 4,348
Result型としてエラーインスタンスをreturnし、if文で判定する方式 21,584
エラーオブジェクトをthrowし、try-catchで判定する方式 15,543
エラーインスタンスをthrowし、try-catchで判定する方式 23,119
Google Chrome 122.0.6261.57

これだけバージョンが合わなかった。

処理環境

Env Ver
CPU AMD Ryzen 5 5600G
OS Ubuntu 22.04.3 LTS

処理結果

処理方式 処理時間(ms)
Result型としてエラーオブジェクトをreturnし、if文で判定する方式 4,842
Result型としてエラーインスタンスをreturnし、if文で判定する方式 16,607
エラーオブジェクトをthrowし、try-catchで判定する方式 15,051
エラーインスタンスをthrowし、try-catchで判定する方式 18,095

全体サマリ表

凡例

  1. Result型としてエラーオブジェクトをreturnし、if文で判定する方式
  2. Result型としてエラーインスタンスをreturnし、if文で判定する方式
  3. エラーオブジェクトをthrowし、try-catchで判定する方式
  4. エラーインスタンスをthrowし、try-catchで判定する方式

サマリ画像

CPUによってかなり差が出ているが、Intel CPUの13700と1265Uを比較した場合、例外を作らない1, 3のケースでは処理に大きな差がないが、例外を作る2, 4のケースでは有意な差が認められるので、モバイル向けとデスクトップ向けでCPUの処理効率に差があることがわかる。

AMD CPUと比較した場合、Node.js環境で例外を作らないケースではIntel CPUに大きく引けを取るが、ブラウザ上でのそれではパフォーマンスは高い。また例外を作るケースでもNode.js・ブラウザ共にIntel CPUと比較した場合のパフォーマンスが比較的よいという、面白い結果になっている。但しOSが違うことの影響もあるため、単純比較はできないが…。

あとがき

ひとまずtry-catchを使うと遅くなるということが確かめられたので良かったし、環境によって劇的遅くなるケースがあるというのは思わない収穫だった。

これを試そうと思った切欠はtry-catchが乱用されているコードを見てパフォーマンス劣化に繋がっているのではないかという疑問を抱いたからだ。try-catchでパフォーマンスの劣化が起きることは知識としても経験としても知っていたのだが、ちゃんとレポートしたことがなかったので今回まとめてみた。

try-catchの利用によりパフォーマンスの劣化が起きるというのは、古い時代にあった開発のお作法を知っている人であれば、それなりに常識だとは思うが、最近は知られていないか、知られていても意識していないことが少なくないと思われるので、今回記事にしてみた感じだ。

100万回の実行なので細かい話になるとは思うがパフォーマンスチューニングは細かいことの積み重ねでもあるので、むやみやたらに例外を使わないことは重要だと思う。そもそも例外は制御しづらいので、可能な限りif文で処理を書くことが望ましいだろう。

処理が遅延する原因としては二つあると考えていて、一つは例外生成時に顕著であることからスタックトレースの生成を初めとしたエラーオブジェクトの生成に時間がかかっているのと、もう一つはcatchでも遅延が出ることから、コールスタックから最寄りのcatchを辿るのに時間を取られていると思われる。今回の検証では特に出してないが、以前確認した時の記憶が確かであれば、catchに入らない限り、tryだけで顕著に処理が遅延することはなかったと思う。

あと、どうでもいいことだがconsole.log()で結果を出したときにEdgeだけObjectのKeyの順序が他と違ったので転記しているときに微妙に不便だった。何回やっても同じだったので恐らくEdgeだけキーをソートするアルゴリズムが違うのだと思う。まぁObjectとかHashとかMapとかDictionaryみたいなやつは順序が保証されないので別にいいっちゃいいんだけど、まさか実装によってソート方式が違うというのは思いもしなかった。

EdgeとChromeで処理結果が優位に違うのも、恐らくこれが原因なのだろう。分からないが多分JSのエンジンの実装が違う気がする。

それとLinuxのEdgeにはマウスジェスチャー機能がないという悲しいことも分かった。

参考

EdgeとChromeは同じだったがNode.jsでは異なる挙動をしていたのでそのメモ

確認環境

Env ver
Microsoft Edge 120.0.2210.9
Google Chrome 120.0.6099.130
Node.js 20.0.0

環境別の確認結果

Edge

DevToolsで確認

const err = new Error('test');
Object.getPrototypeOf(err);

// {name: 'Error', message: '', constructor: ƒ, toString: ƒ}

Google Chrome

DevToolsで確認

const err = new Error('test');
Object.getPrototypeOf(err);

// {name: 'Error', message: '', constructor: ƒ, toString: ƒ}

Node.js

node -iで確認

const err = new Error('test');
Object.getPrototypeOf(err);

// {}

maxlength

  • iOS Safariでは効かない
  • onInput()string.slice(0, maxlength)するとIMEの挙動が可笑しくなる
    • type="tel"など日本語が入力できない場合であれば有効
  • オートコンプリートやコピペ入力での字切れなどもあるため、根本的に使わないことが望ましい

type="number"

  • iOS Safariでは期待した動作にはならない
    • IMEが有効になり、全角入力が発生する
  • 使うならtype="tel"を使い、JSで数字以外の入力を弾くのが無難
  • 恐らく普及ブラウザの全てで半角入力を強制出来、スマホなどではNumPadが出てくる
    • アルファベットやハイフンなどの記号も打てるので必要に応じた入力制御が必要