写真検索の近傍探索ServiceをPythonからC++に置き換えた話

この記事は、Mercari Bold Challenge Month の 8 日目の記事です。

こんにちは。メルカリのAI EngineeringでSoftware Engineerをしている@wakanapoです。私は、2019年4月にメルカリに新卒入社して、6月にAI Engineering teamに配属されました。今回は、配属されてから私が最初に担当したタスク「写真検索の近傍探索Service実装のPythonからC++への置き換え」について紹介します。

写真検索システムについて

メルカリでは、今年の3月からiOSで写真検索の機能を提供しています。
about.mercari.com

メルカリの写真検索は過去のMercari Engineering Blogで詳しく説明されていますが、図1のようなアーキテクチャで動いています。
tech.mercari.com

f:id:wakanapo:20190829105534p:plain
図1

今回、この中の画像特徴ベクトルの近似最近傍探索を行うindex serviceのPythonからC++への置き換えを行いました。

なぜC++に置き換えたか

それは私がC++が好きだからです。

という冗談はさておき(採用面接や入社後の面談でもC++を書きたいですと言い続けたため、この数少ないC++タスクが私に降ってきたという背景はありますが笑)、
index serviceは、近傍探索ライブラリFaissをもちいて近傍探索を行うgRPCサーバーです。これまで はPython で実装されていました。
github.com

FaissはネイティブではC++で実装されており、Pythonから利用する際には、Pythonラッパーを使うことになります。当然ラッパーを使う分オーバーヘッドが生じることになります。
また、ネイティブなPythonにはGILがあります。そのため、マルチスレッドで実行したとしても、ネイティブPython部分は1スレッドずつしか実行されません。したがって、並列に動かすためにはマルチプロセス化する必要があります。

今回、主に以下の3点を理由にindex service全体をC++実装に置き換えることにしました。

  • ラッパーのオーバーヘッドの回避
  • gRPC部分のGILの回避
  • 今後並列実行の必要性のある機能を開発する際にマルチプロセスではなくマルチスレッドを利用することによる資源の効率化およびコスト削減

置き換えの際に気をつけたこと

f:id:wakanapo:20190829105618p:plain
写真検索ではメルカリ内製の機械学習プラットフォーム Lykeion を使用して index の自動更新をしていますが、index 生成側を C++ バージョンに置き換えてからしばらくは Python バージョンと C++ バージョンの index が共存することになります。

そこで今回、index serviceを置き換えるにあたって、Pyhonのbase imageとC++のbase imageが共存していても、どちらも問題なくLykeionがbuild、serveできるように気をつけました。具体的には、PythonバージョンとC++バージョンの差異をbase image内のみにとどめ、serving imageのbuild、 clusterへのserveは完全に同じように扱えるようにしました。
その結果、既にbuild、serveされたimageを再build、serveすることなくC++バージョンへ置き換えることができました。

今後の展望

今回の置き換えで、gRPC部分のマルチスレッド化とラッパー部分のオーバーヘッドの回避ができました。しかしながら、C++実装に置き換えた効果はまだまだ発揮できるはずです。今後は、マルチスレッドを利用してリアルタイム・インデクシングなどを実装していこうと考えています。
また、アクセスが多ければ多いほどC++実装の恩恵を受けることができるはずです。みなさんもぜひ、メルカリ写真検索を使ってみてください٩(๑òωó๑)۶

次の記事は、@kentan による「ACLにおけるマイクロサービス開発の話」です。 お楽しみに!

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加