Rayonで並列処理をするとECBモードの暗号化処理がどのぐらい早くなるか試してみた

Rayonで並列処理をするとECBモードの暗号化処理がどのぐらい早くなるか試してみた

Clock Icon2024.09.24

こんばんは、リテールアプリ共創部のmorimorikochanです。
前回、S-DESというブロック暗号の暗号化の実装をECBモードで行いました。

https://dev.classmethod.jp/articles/s-des-on-rust/

ブロック暗号はブロックに分割しそれぞれに対して暗号化を行ないますが、ECBモードはこれらブロックを並列に処理できるため、処理速度が他のモードと比べ高速です。
しかし、全てのブロックで同じ鍵情報を利用するため、データに一定のパターンが存在すると、データを完全に隠蔽することができていない可能性が存在します。
以下のWikipediaに掲載されている画像を見ていただくとイメージできると思います。

https://ja.wikipedia.org/wiki/暗号利用モード#Electronic_Codebook_(ECB)

そのため、ECBモードが利用される場面は非常に限定されてきています。
ですが、今回はあえてECBモードの持つ並列処理によりどのぐらいS-DESの暗号化が高速になるのかを、実際に実験してみました。

Rayonとは

RayonはRustでデータを並列に扱うことができるライブラリです。
同様の処理を実現するためには通常、レースコンディションやスレッドの制御などの問題を解決する必要が出てきてしまいますが、Rayonではこの問題を隠蔽してくれています。

https://github.com/rayon-rs/rayon

実験してみた

条件

条件は以下の通りです。

  • Apple M1 Pro
    • メモリ 16GB
  • cargo 1.74.0 (ecb9851af 2023-10-18)
  • rustup 1.26.0 (5af9b9484 2023-04-05)
  • edition = "2021"

また、実験に利用したソースコードは以下の通りです。
前回のS-DESの実装をベースに使っています。

https://github.com/diggymo/s-des-on-rust/blob/157f7756463d14c7d2c70a339df333bc7a088ef8/src/main.rs#L6-L22

ちなみに、Rayonの導入がめちゃくちゃ簡単で驚きました。
今回のケースではすんなり書き換えることができましたが、クロージャがFnMutの場合は大規模な修正が必要になると思われます。
そもそもFnMutである時点で並列に処理できない(=何らかの副作用がある)と思うので仕方ないと思いますが...

https://zenn.dev/termoshtt/books/b4bce1b9ea5e6853cb07/viewer/rayon#安全な並列処理の為の制約

また、テスト用の平文データとして、IMDb Non-Commercial Datasetstitle.basics.tsv.gzを利用させていただきました。ファイルサイズは約955MBです。

https://developer.imdb.com/non-commercial-datasets/

結果

結果は以下のようになりました。
Rayonを利用した並列処理の場合の方が圧倒的に処理速度が早く、直列処理の場合と比べ平均5.4倍の速度で暗号化されました

直列処理の場合 Rayonを利用した
並列処理の場合
1 102.09s 18.85s
2 103.48s 19.07s
3 102.36s 19.40s
平均 102.64s 19.11s

RayonはデフォルトだとCPUと同じ数だけスレッドを利用します。

By default, Rayon uses the same number of threads as the number of CPUs available. Note that on systems with hyperthreading enabled this equals the number of logical cores and not the physical ones.

https://github.com/rayon-rs/rayon/blob/main/FAQ.md#how-many-threads-will-rayon-spawn

私の環境ではスレッドの数が8であったため、8スレッドが並列処理を行っていたと考えられます。
ですので単純に考えると8倍早く処理されるはずなのですが、実際には並列処理のためのオーバーヘッドが存在し(スレッドの立ち上げ処理、スレッドの完了状態のチェックなど?)、今回計測したように5.4倍あたりに落ち着いたのではないかと考えられます、

ちなみに、Rayonが利用するスレッドの数はrayon::current_num_threadsで確認できました。

直列の方が速い場合がある?

並列処理のためのオーバーヘッドが存在しているということは、場合によっては直列処理の方が処理速度が早い可能性が十分にあります。

そこで、テスト用の平文データを十分に小さくした場合どのように結果が変わるか調べてみます。
前回の記事で利用したこちらをテスト用の平文データとして試してみました。

plain.txt
1234567890
hogehoge
私の名前はmorimorikochanです
Diggy-mo!!!

結果は以下の通りでした。

直列処理の場合 Rayonを利用した
並列処理の場合
1 10.46µs 97.00µs
2 11.17µs 73.63µs
3 8.38µs 230.79µs
平均 10.00µs 133.81µs

予想した通り直列処理の場合の方が処理速度が早いことがわかりました。また、その差は平均値で124µs程度でした。
今回実験したケースでは、Rayonを利用した並列処理の場合は少なくとも平均的に124µs以上のオーバーヘッドが生じていることがわかりました。

まとめ・所感

  • Rayonで並列処理をするとECBモードの暗号化処理が速くなった
    • 今回検証したケースでは約5.4倍
  • しかし、処理対象のデータが小さい場合は直列処理の方が速いことがわかった
  • どんな場合もRayonで並列処理をすればいいというわけではないので、ワークロードを適切に見積もる必要性を感じた
  • 今回のケースではRayonを利用するための修正箇所が少なく済んだ
  • InstantDebug traitが自動で単位を出力してくれて地味に便利

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.