1万件のデータ表示を16.4秒→3秒に高速化、転送データ量を94%削減。

はじめに

プロアクシアコンサルティングで開発・運用している社内アプリ hana viewer(SAP S/4HANA の OData 閲覧アプリ)を題材に、性能改善の中身を実測値でまとめました。

当初、業務データ1万件の取得・表示には16.4秒かかっていました。今回の改善で3秒まで短縮し、転送データ量も 74MB から5MB(94%減)まで削減しています。性能改善に向け調査したところ、データ量とRust から画面への転送経路に問題がありました。

この記事で伝えたいのはRust だから速いという単純な話ではありません。計測で見つけたボトルネックに対して、Rust で有効な手段を選べるという点を実際の改善例で見ていきます。あわせて、Rust やクライアント側の工夫では縮まらないサーバ処理時間についても触れます。

数値はすべて開発機1台での実測値(A_BusinessPartner 69項目・1万件 ほか)です。

アプリの目的と問題点

hana viewer はSAP S/4HANA Public Cloud の OData サービスをローカルから閲覧するデスクトップアプリです。SAP の導入コンサルタントや周辺システム開発者が「この API は何を返すのか」「データは実際どうなっているのか」を素早く確認するために使います。

Entity は階層ツリーで表示し、データは仮想化グリッド(転置表示にも切替可)で閲覧できます。検索・ソート・絞り込みはすべてサーバー側 OData クエリです。1件の伝票を起点に、関連する伝票やデータを業務プロセスをまたいでたどれる関連マップも備えています。

構成はCore プロセス(Rust)が SAP への通信・パース・整形を担い、WebView(React)が表示を受け持ちます。両者は通常のコマンド呼び出し(IPC=プロセス間通信)と、大きなデータ用の odata:// カスタムプロトコルの2経路でつながります。認証情報は Rust 側に隔離しています。

※注 Tauri は Rust 製のデスクトップアプリ用フレームワークで、OS 標準の WebView(ブラウザ描画エンジン)を利用して軽量に動作します。同一のコードから Windows / macOS / Linux 向けにビルドできるのも特徴です。wry は、その WebView を制御するライブラリ層です。

当初、項目数の多いEntity を1万件取得すると表示までに16.4秒かかっていました。これが改善の対象です。

計測で原因を特定する

遅さの原因を切り分けるため処理を段階ごとに計測しました。どの段階でどれだけ時間がかかっているかを数値で見ます。

A_BusinessPartner(SAP の取引先マスタを扱う標準 OData サービス。69項目)1万件の初期計測は次の通りです。

ネットワーク取得は1秒、JSON.parse は0.37秒でいずれも支配的な問題ではありません。ボトルネックはデータ量(71MB)とそれを IPC で画面へ運ぶ転送時間(13秒、≈5MB/s)でした。

高速化

問題に対し以下4点の改善を行いました。いずれも実測で確認しています。

  1. 並列取得(tokio spawn)
    SAP ゲートウェイは $top に上限(多くは1000件)があるため1000件ずつに分割して並列に取得します。

    直列なら9秒かかる処理も並列化すれば合計時間は最も遅い1チャンク分(1秒)に近づきます。所有権と Send 境界によりデータ競合がコンパイル時に排除されるため、並列処理も比較的安全に書けます。
    ※注 tokio は Rust の代表的な非同期ランタイムで、tokio::spawn は処理を並行タスクとして起動します。所有権と Send 境界は Rust がデータの扱いを管理する仕組みで、複数スレッドから同じデータを不正に触る「データ競合」を、コンパイル時(実行前)に検出・排除します。
  2. 転送経路の見直し(odata カスタムプロトコル)
    IPC は本来小さな引数・戻り値をやり取りするための経路です。今回のように大きなデータをそのまま IPC で運ぶと転送に時間がかかります。そこで結果は Rust 側に一時保持して id だけを IPC で返し、データ本体は画面側から fetch() で取得する方式に変えました。Tauriが備えるカスタム URI スキームはネイティブの HTTP 経路で大きなデータの受け渡しに向いています。

    実測では転送時間が13秒から0.9秒に短縮しました。大きなデータを IPC ではなくネイティブ HTTP 経路へ回せるのはRust と WebView 層(wry)を直接扱える構成によるものです。
  3. カラムナ化 + ゼロコピー + SIMD
    A_BusinessPartner は69項目とワイドで値の多くが空・null です。行 JSON は空項目でも全行に項目名を繰り返すため肥大化します。行 JSON をカラムナ化すると項目名は1回で済み、空セルのコストもほぼなくなります。


    値は &Valueで列へ振り分けるため巨大データを clone せずカラムナ化できます。SIMD 対応パーサ(sonic-rs)を使い、読み書きも高速化しました。転送データ量は 74MB から5MBへ。転送・parse とも比例して減っています。
    ※注 借用(コード中の &)はデータの所有権を移さず参照だけを借りる Rust の仕組みです。巨大データを複製せずに扱えます(ゼロコピー)。SIMD(Single Instruction, Multiple Data)は1つの命令で複数のデータをまとめて処理する CPU の仕組み、sonic-rs はその SIMD を活用した Rust 向けの高速な JSON ライブラリ(解析・整形ライブラリ)です。
  4. 中間データを作らない直接組み立て
    「サーバ JSON → serde_json::Value に展開 → 整形 → 再直列化」という Value の往復が5.3秒を占めていました。中間表現を作らず行データは文字列のまま通し、外枠だけ Vec<u8> に直接書き出すことでコマンド内部の処理は9.7秒から1.5秒に短縮しています。
    ※注 serde(serde_json)は Rust の定番のシリアライズ(データ↔JSON などの相互変換)ライブラリです。Vec<u8> はバイト列(可変長のバイト配列)を表します。

ここまでの効果(A_BusinessPartner 1万件)

改善の内訳 ― 何が Rust 固有で、何がそうでないか

効果的だった施策を層ごとに見ると発想やフロントの工夫は言語に依存しません。ただ、その実装を安全かつ高速に成立させるうえで Rust が力を発揮しています。

  • カラムナ化の発想は言語に依存しません。一方でこの処理を巨大データを複製せずSIMD で並列に、しかもデータ競合なく実装できたのは Rust の利点です。
  • $select(表示列だけ取得)は OData の標準機能で言語とは無関係です。
  • 「1万件を快適に閲覧」できるのはフロント側の設計によるものです。仮想化グリッド(可視範囲だけ描画)などの工夫はバックエンドの速さとは別に必要になります。

速いバックエンド、軽いフロント、カラムナ化や $select などの設計判断は別々の課題で、それぞれに手当てしています。

Rust の強み・弱み

Rust が有効な場面

  • クライアント側で大量データを扱うアプリ(取得・整形・転送・表示)
  • Tauri を使ったデスクトップアプリ。単一バイナリ・低メモリ・複数 OS 向けビルドとWeb UI の手軽さを両立できます。
  • 高スループットが求められるバックエンド・バッチ処理、大量データの整形・変換・連携(SAP の OData / ファイル連携を含む)

Rust ではどうしようもない場面

  • ボトルネックがサーバや外部サービスの実行時間にある場合。たとえば複雑な CDS ビューは1000件返すだけでサーバ側に27秒かかることがあり、クライアント側の並列化や転送改善では縮みません。この場合は $select で要求する列を減らすなど、別の手段に切り替えます。

まず計測で、どの層がボトルネックかを確認する。そこが出発点になります。

Rust を選んだ理由(言語固有の利点・運用面)

ここまでの4つの改善(並列フェッチ・転送経路の見直し・ゼロコピー×SIMD・バイト列の直接組み立て)に共通するのは、GC を前提とする言語では安全・低コストには取りにくいという点です。Rust では、データ競合をコンパイル時に防ぎ、借用でゼロコピーを保証し、SIMD ライブラリを依存追加で使い、必要な部分だけバイト列を直接扱う。こうした改善を、安全性を保ったまま進められます。

※注 GC(ガベージコレクション)は、不要になったメモリを自動的に回収する仕組みです。Rust は GC を持たず、コンパイル時の所有権管理でメモリを扱うため、回収のための一時停止(GC 停止)が起きません

運用面では、メモリ安全・GC 停止がないことによる応答の安定、単一・小型バイナリでの配布、メモリ使用の小ささがあります。

なお、本記事の before/after は同一スタック内(素朴な実装と最適化後)の比較であり、他言語との速度比較ではありませんRust は「とにかく速い」のではなく、計測で見つけたボトルネックを安全性を保ったまま改善しやすい言語です。要点は、安全性を保ったまま改善を進めやすいことです。

ご支援の進め方

本アプリで得た知見は、お客様の性能改善のご相談にも同じ流れで活かせます。

  1. 計測で現状を可視化し、どの段階でどれだけ時間がかかっているかを定量化します。
  2. Rust で縮む層か、設計やサーバ側の問題かを切り分けます。
  3. ホットパスやアプリ単位で範囲を絞り、小さく試します(PoC)。
  4. メモリ安全・データ競合の排除を保ったまま、実装と運用へ進めます。

まとめ

  • Rust は「とにかく速い」のではなく、計測で見つけたボトルネックを安全性を保ったまま改善しやすい言語です。
  • UI は Web のまま、性能改善に有効な部分だけ Rust 側に置く構成が取れます。
  • できない層(サーバ律速)は無理に追わず、$select のように要求を小さくする方向へ切り替えます。

本アプリはプロアクシアコンサルティングが社内で実際に開発・運用している成果物です。こうした開発で得た知見をもとに、Rust の活用について以下のようなご相談に対応しています。

  • Rust 導入の適性診断(計測を前提に、有効な層を見極め)
  • 小規模な PoC・パイロット(ホットパスやアプリ単位で)
  • 性能ボトルネックの計測・改善
  • SAP × Rust / 周辺開発(OData 連携を含むデータ処理)

検討段階からご相談いただけます。お問い合わせください。

お問い合わせ