SPREAD高速化テクニック!(2) 描画処理の効率化

当ブログでは以前に弊社製品の「SPREAD(スプレッド)」シリーズで活用できる描画処理の高速化テクニックをご紹介しました。

シートやセル、行や列など、SPREAD上で処理したいデータ範囲に対応したAPIを使うことによって、描画を高速化できるというのがこのテクニックのポイントでしたが、今回は別なアプローチによる描画処理の高速化方法をご紹介します。

なお「高速化」と記載しましたが、実際の中身としては記事のタイトルにあるように描画を「効率化」して不必要な処理を省き、パフォーマンスの向上を図ります。

SPREADの描画処理とsuspend/resume系メソッド

今回ご紹介するのは、SPREADシリーズのうち「SPREAD for Windows Forms」と「SpreadJS(スプレッドJS)」でお使いいただけるテクニックです。これらのSPREADは、シート上で発生した変更に応じて都度、データ描画を行うように設計されています。例えばSPREAD上にある任意のセルを編集すると直ちに描画処理が実行され、その結果を遅延なしに確認することが可能です。

SPREADにおける通常の描画フロー
SPREADにおける通常の描画フロー

このような動作において、プログラム処理などにより沢山のデータを連続的に設定しようとすると、パフォーマンスの低下が懸念されるようになります。これは下図に示すように、連続するデータ設定のひとつひとつに応じて都度、描画処理が実行されてしまうことに起因するものです。

複数回のデータ設定における描画フロー
複数回のデータ設定における描画フロー

上記に示す描画処理の中には「設定されたデータを描く」というもののほか、描画のための関連処理も含まれます。例えばデータ描画にあたってはシステムやブラウザから描画のためのコンテキストオブジェクトを取得する必要などがありますが、本来なら1回の取得で済むそのような処理も上記の例では都合、複数回実行されることになり、その分トータルの処理時間が長くなってしまいます。

このような場合におけるパフォーマンス低下を防ぐため、SPREAD for Windows FormsとSpreadJSでは以下のようなメソッドを用意しています。

SPREAD for Windows Forms

メソッド名機能
SuspendLayoutコンポーネントのレイアウトロジックを一時的に中断します。
ResumeLayoutコンポーネントのレイアウトロジックを再開します。

※ レイアウトはSPREAD for Windows Formsで描画関連の処理を担うオブジェクトです。

SpreadJS

メソッド名機能
suspendPaintシートとタブストリップの描画を中断します。
resumePaintシートとタブストリップの描画を再開します。

これらは描画処理の停止と再開を行うメソッドです。データ設定の前後で描画処理の停止と再開を行うことで、前述した処理の無駄を省くことが可能になります。

描画の停止・再開を利用した描画フロー
描画の停止・再開を利用した効率的な描画フロー

実演

この項ではSpreadJSを例にとり、描画処理の停止と再開を活用した場合の効果を見ていきます。はじめにSpreadJSを使い以下のようなデータ設定処理(15行x15列=225セルへのデータ設定処理)を用意します。

// SpreadJSを初期化します
var spread = new GC.Spread.Sheets.Workbook('ss');
var sheet = spread.getActiveSheet();

// 15行、15列(=225セル)のSpreadJSを用意します。
var ROW_NUM = 15;
var COL_NUM = 15;
sheet.setRowCount(ROW_NUM);
sheet.setColumnCount(COL_NUM);

// シート上のセルにデータを設定します
var i, j;
for (i = 0; i < ROW_NUM; i = i + 1) {
  for (j = 0; j < COL_NUM; j = j + 1) {
    sheet.getCell(i, j).value(data);
  }
}

上記にあるデータ設定処理に対して、描画処理の停止と再開を活用した場合とそうでない場合のパフォーマンスを検証します。

描画処理の停止と再開のやり方は簡単です。以下のようにして、データ設定処理の前後でそれぞれsuspendPaintメソッドとresumePaintメソッドをコールします(SPREAD for Windows FormsにおけるSuspendLayoutメソッドとResumeLayoutメソッドも使い方も同様です)。

// 描画処理を一時停止します
sheet.suspendPaint();

// シート上のセルにデータを設定します
var i, j;
for (i = 0; i < ROW_NUM; i = i + 1) {
  for (j = 0; j < COL_NUM; j = j + 1) {
    sheet.getCell(i, j).value(data);
  }
}

// 描画処理を再開します
sheet.resumePaint();

今回は上記2種類の処理を確認できるデモを用意しました。それぞれの処理を実行するボタンを押下し、その差を実際に確認してみてください。



弊社環境においてGoogle Chrome上で4回程度、それぞれの処理を実行した場合の平均処理速度は以下のようになりました。

データ設定(描画の停止・再開なし)2812 ミリ秒
データ設定(描画の停止・再開あり)4 ミリ秒

描画の停止と再開を利用することによってパフォーマンスにはっきりとした差が生まれてくることをご理解いただけたかと思います。

詳細をヘルプで確認

今回ご覧いただいたように、描画の停止と再開機能を利用することでSPREADを使ったアプリケーションをより高速にできる場合があります。対象処理の前後にコードを1行ずつ追加するだけで絶大な効果を得られる可能性がありますので、現在ご開発中のアプリケーションで適用できそうな箇所があればぜひ、お試しください。

なお、SpreadJSおよびSPREAD for Windows Formsにおける描画処理の停止と再開の方法は、それぞれ以下のヘルプ項目で解説されていますので、本記事とあわせてご確認いただければ幸いです。

SPREAD for Windows Forms

SpreadJS

TIPS!計算やイベントにおける停止と再開

ここまでは「描画」をテーマにSPREADに搭載された処理の停止と再開機能をご紹介しました。加えて一部のSPREAD for WindowsとSpreadJSでは、同様のコンセプトで数式の計算やイベントの発生を停止したり、再開したりすることでパフォーマンスを向上できる機能があります。そのメリットや用法については各製品のヘルプやナレッジベースにある以下の項目でご確認ください。

SPREAD for Windows Forms

SpreadJS

SPREAD for Windows Formsでは移行による性能改善も効果的

今回ご紹介したテクニックのほか、SPREAD for Windows Formsをお使いの場合は最新版「V12.0J」への移行もパフォーマンス改善に効果的です。V12.0Jでは前バージョンからの性能改善が実施され、パフォーマンスやメモリ消費量の面で性能が向上しました。こちらについては以下の記事で詳しくご紹介していますので、ぜひ、ご覧ください。