TextEncoder
やTextDecoder
は、Uint8Arrayと文字列を相互に変換するAPIです。crypto周りのコードでよく使われています。しかし、書き方によってはパフォーマンスが低下する可能性があります。
new TextEncoder()
やnew TextDecoder()
を呼び出すタイミング
以下は文字列をUint8Arrayに変換し、SHA-256でハッシュ化する関数です。
よくあるパターン
import * as hex from "https://deno.land/std@0.122.0/encoding/hex.ts";
/** SHA-256を使用して文字列をハッシュ化する関数 */
export async function hash(message: string) {
// 関数が呼び出されるたびに毎回`new TextEncoder()`されている
const encoded = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
return new TextDecoder().decode(hex.encode(new Uint8Array(hashBuffer)));
}
一見すると問題ないように見えますが、このコードでは関数が呼び出されるたびに毎回TextEncoder
やTextDecoder
のインスタンスが生成されているため、処理が冗長になっています。
パフォーマンスの観点からは、TextEncoder
やTextDecoder
のインスタンス生成は関数の外で一回だけ行うのが正解です。
import * as hex from "https://deno.land/std@0.122.0/encoding/hex.ts";
// 関数の外側で先にインスタンス生成しておく
const encoder = new TextEncoder();
const decoder = new TextDecoder();
/** SHA-256を使用して文字列をハッシュ化する関数 */
export async function hash(message: string) {
const encoded = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
return decoder.decode(hex.encode(new Uint8Array(hashBuffer)));
}
どれくらいパフォーマンスが向上するのか
ベンチマークを取ってみましょう。ベンチマークには https://jsbench.me/ を使用します。
結果
毎回インスタンス生成する場合:33.12 ops/s ± 3.24%
先にインスタンス生成する場合:48.21 ops/s ± 1.5%
というわけで、TextEncoder
やTextDecoder
のインスタンスを関数の外側で先に生成する場合は、約35%高速という結果が得られました。
まとめ
-
new TextEncoder()
やnew TextDecoder()
を関数の中で呼び出すと遅い - 関数の外で呼び出せば、約35%高速化できる