【JavaScript】最速のOSS Data Table/Grid/SpreadSheetを探して、

この記事は[フューチャーアーキテクト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/future)の15日目の記事です。

タイトルに色々な表現を書きましたが、`Data Table`なのか`Grid`なのか`Spreadsheet`なのかどう表現するのが正解なのでしょうか？

※この記事は[Cheetah Grid](https://github.com/future-architect/cheetah-grid)のステマ（全然ステルスじゃない）です。

# 発端
去年の終わりごろ同僚と「ブラウザで表示するGridってパフォーマンスボトルネックになること多いよね」的な雑談をしました。
その時の原因予想は、

* アプリケーション的にデータ量が増えやすい部分。その結果生成するhtmlがデカい。
* `<table>`タグやGridライブラリを利用して表示すること自体、htmlがデカくなりがち。
* 見える部分だけ描画する機構のGrid使わないと厳しい。（ページングは場合によってはユーザービリティが下がる）

みたいな感じだったと思います。
有名な[SlickGrid](https://github.com/6pac/SlickGrid)なんかは見える部分だけ描画する仕組みなので、とっても速いですが、やはり多くのDOMを生成しますし、周りのDOMの大きさに影響して遅くなったりします。

で、「`<canvas>`使って描画したら速いんじゃね？」という話になり、

* `<canvas>`ベースのGridライブラリの調査
* 普通のGridライブラリの速度調査
* ついでに`<canvas>`ベースのGridライブラリを作ってみる（`<canvas>`で遊ぶ）

をやることにしました。

# `<canvas>`ベースのGridライブラリ調査
２つほど使えそうなのを見つけました。

* [fin-hypergrid](https://github.com/openfin/fin-hypergrid)
* [canvas-datagrid](https://github.com/TonyGermaneri/canvas-datagrid)

（ライブラリではないので関係ないですがGoogle スプレッドシートも`<canvas>`ベースのようです。）

[fin-hypergrid](https://github.com/openfin/fin-hypergrid)はそこそこ早いです。
ただ単純な初期表示で[SlickGrid](https://github.com/6pac/SlickGrid)に負けます。（なんで？）
[canvas-datagrid](https://github.com/TonyGermaneri/canvas-datagrid)は思っていたより速くないのと、一番下のcellを選択しようとすると途端に動作が遅くなるという欠点を見つけました。

この結果を受けて「`<canvas>`ベースのGridライブラリを作ってみる」は本格的に進めることになりました。

# 普通のGridライブラリの速度調査

結果は、[SlickGrid](https://github.com/6pac/SlickGrid)一強でした。

[w2ui](http://w2ui.com/web/)のGridが超速いという噂をきき、ワクワクしながら調べたのですが、
速かったのはバージョン1.3のころで、最新の1.5は全然速くなかったです。。。

## 確認ページ
パフォーマンス確認のためのページをgh-pagesで作成しました。
https://ota-meshi.github.io/data-table-performance-test/

一応、注意事項として書いておくと、
[SlickGrid](https://github.com/6pac/SlickGrid)等の見える範囲のみを描画する機構のGridは、画面サイズ（ブラウザのWindowサイズ・ディスプレイのサイズ）にも依存しますので、例えば、通常PCとAndroidでの速度差はOSのパフォーマンス等の参考にはなりません。

あと、結構ブラウザをいじめるので実行するGridによってはPCが固まる等あると思います。
`open`を押す前にPCのメモリ使用量等確認したりしたほうが良いかもしれません。


# `<canvas>`ベースのGridライブラリ作った。

[Cheetah Grid](https://github.com/future-architect/cheetah-grid)というGridライブラリを作成しました。

機能はまだまだ少ないですが、100万件表示しても数十`ms`で表示できます。

ここから、先ほどの[パフォーマンス確認ページ](https://ota-meshi.github.io/data-table-performance-test/)で試した結果を書いていきます。

## パフォーマンス確認

「*Number of records: 1,000,000
Number of trials: 100
Dispose instance after init:* ***true***」

とあるリンク先の、「[Cheetah Grid](https://github.com/future-architect/cheetah-grid)」と「[SlickGrid](https://github.com/6pac/SlickGrid)」の結果を記載します[^1]。

[^1]: 他のコンポーネントは比較にならないぐらいの結果だったので、ここには記載しません。気になる方はページを開いてお試しください。例えば、`Handsontable`は700msかったりします。（大量データパフォーマンス重視のコンポーネントではないので仕方ないですが。）

このページでやっていることは、以下です。

* インスタンス生成・100万レコード描画までの初期処理の時間計測
* 生成した、インスタンスを破棄
* 100回繰り返し、平均時間等を表示

100万レコード表示することなんて、ふつうに綺麗にアプリケーション設計したらあり得ませんが、
ライブラリとして、どの程度この許容できるかという参考という感じで見てもらえればと思います。

---



### 環境（それぞれ）
* MacBook Pro[^mac]  
[^mac]: 娘 ３歳 のPCということになっている。娘がベシベシ叩くからか最近`J`キーが反応悪い。 <br> 手元のPCの中では計測値にぶれが少ないので今回の計測で一番信用できそう。  
* Widows10[^win10]  
[^win10]: Windows7の頃から使ってる古いやつ。家族の共有物。とっても安かった。手元にEdgeはこれしかない。  
* Widows7[^win7]  
[^win7]: セキュリティソフトが常に無双してる。ミリ秒単位の計測は難しい。  
* iPad[^ipad]  
[^ipad]: 嫁の。主にCookpadが表示される。いつもおいしいご飯をありがとう！  
* Android[^android]  

[^android]: 僕のスマホ。4年ぐらい連れ添ってる。もともと防水防塵仕様だが色々な蓋が外れてて、防水も防塵もあったもんじゃない。  

### 今回の計測対象グリッド説明
|name	|個人的説明|
|:---|:---|
|[Cheetah Grid](https://github.com/future-architect/cheetah-grid)|最速描画を目指して作り始めたGridコンポーネント。|
|[SlickGrid](https://github.com/6pac/SlickGrid)|個人的に前職から愛用のgrid/spreadsheetコンポーネント。<br>とっても優秀で、とっても有名。<br>スクロール時に一瞬白くなるのが、個人的に唯一嫌いなことろ。。。|
|Cheetah Grid<br> (customize : extends DrawGrid)|`Cheetah Grid`の汎用機能なしのコアモジュールを継承して作る版。<br>ミリ秒単位の速度が求められ、`<canvas>`の知識（これから勉強する気）がある程度あるならこういう使い方もできる。<br>というか最初は`<canvas>`を利用してGridを実装させるつもりで`Cheetah Grid`作ったけど、さすがに汎用機能がないと誰も使わないよね。ってことで、汎用機能も作り始めた。コアモジュールのドキュメントはまだない。|

### 各計測結果

#### Chrome(on Mac) :v:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br> (customize : extends DrawGrid)|7.83ms | max: 16ms/min: 5ms|
|Cheetah Grid	|12.51ms | max: 42ms/min: 8ms|
|SlickGrid	|15.66ms | max: 42ms/min: 14ms|


#### Chrome(on Windows7) :v:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br> (customize : extends DrawGrid)|12.88ms | max: 135ms/min: 4ms|
|Cheetah Grid	|23.15ms | max: 105ms/min: 7ms|
|SlickGrid	|31.03ms | max: 96ms/min: 13ms|

#### Chrome(on Android) :v:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br> (customize : extends DrawGrid)|37.58ms | max: 158ms/min: 19ms|
|Cheetah Grid	|50.11ms | max: 123ms/min: 25ms|
|SlickGrid	|137.24ms | max: 375ms/min: 85ms|


#### FireFox(on Mac) :v:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br>(customize : extends DrawGrid)	|9.01ms | max: 41ms/min: 7ms|
|Cheetah Grid	|17.78ms | max: 74ms/min: 12ms|
|SlickGrid	|23.6ms | max: 66ms/min: 18ms|


#### FireFox(on Windows7) :scream:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br>(customize : extends DrawGrid)	|27.56ms | max: 108ms/min: 15ms|
|SlickGrid	|42.72ms | max: 158ms/min: 17ms|
|Cheetah Grid	|48.94ms | max: 212ms/min: 25ms|

#### IE11 (on Windows7) :v:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br> (customize : extends DrawGrid)|43.85ms | max: 163ms/min: 16ms |
|Cheetah Grid|68.17ms | max: 295ms/min: 30ms|
|SlickGrid|77.84ms | max: 301ms/min: 33ms |


#### Edge (on Windows10) :v:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br> (customize : extends DrawGrid)|70.31ms | max: 108ms/min: 23ms|
|Cheetah Grid|110.1ms | max: 198ms/min: 48ms|
|SlickGrid|111.98ms | max: 227ms/min: 46ms|

#### Safari(on Mac) :scream:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br>(customize : extends DrawGrid)|7.09ms | max: 22ms/min: 6ms|
|SlickGrid	|8.34ms | max: 30ms/min: 7ms|
|Cheetah Grid	|9.7ms | max: 31ms/min: 8ms|


#### Safari(on iPad) :scream:

|name	|100回生成（毎破棄）<br>1,000,000レコード<br><br>平均|<br><br><br><br>min/max|
|:---|---:|:---|
|Cheetah Grid<br>(customize : extends DrawGrid)|32.21ms | max: 103ms/min: 25ms|
|SlickGrid	|41.58ms | max: 106ms/min: 31ms|
|Cheetah Grid	|48.8ms | max: 280ms/min: 33ms|

---

今回は詳細を書きませんが、[パフォーマンス確認ページ](https://ota-meshi.github.io/data-table-performance-test/)には「インスタンス破棄」をせず、100個の同一コンポーネントを画面に表示していくタイプも作成しています。
「*Number of records: 1,000,000
Number of trials: 100
Dispose instance after init:* ***false***」
のリンク先で実際のパフォーマンスを確認できます。



こちらは、試してもらえるとわかりますが、[SlickGrid](https://github.com/6pac/SlickGrid)ではコンポーネントが増えるほど生成にかかる時間が遅くなっていきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/84259/61969b9f-c268-2498-a67a-81805c12cf2e.png)

いかにDOMが増えると計算が遅くなるのかが確認出来るかと思います。
（[Cheetah Grid](https://github.com/future-architect/cheetah-grid)は生成するDOMが少ないおかげか、数の影響は受けませんでした）
興味ある方は開いて試してみてください。


# 感想
[Cheetah Grid](https://github.com/future-architect/cheetah-grid)のコアモジュール利用（`Cheetah Grid(customize : extends DrawGrid)`）の場合は、すべての結果で最速を出しましたが、通常の汎用的な機能からの呼び出しだとFirefox(Windows)・Safariで負けてしまいました。。。
それにしても[SlickGrid](https://github.com/6pac/SlickGrid)速いですね。

どちらも体感だとどっちが速いとかわからないレベルの速さなので、アプリケーションのDOMの量が[SlickGrid](https://github.com/6pac/SlickGrid)の生成コストに影響しないようなの場合は、みんな使ってて情報も機能も多い[SlickGrid](https://github.com/6pac/SlickGrid)使いますよね。。。

---

是非、興味ある方は[Cheetah Grid](https://github.com/future-architect/cheetah-grid)使ってみていただけると僕が喜びます。
もし面白いと思っていただけたらスター付けてもらえると僕が泣いて喜びます。

<a href="https://github.com/future-architect/cheetah-grid">
<img src="https://github.com/future-architect/cheetah-grid/raw/master/docs/logo.png">
</a>
https://github.com/future-architect/cheetah-grid

---