Post on 13-Dec-2014
description
パフォーマンスの良い
GASの書き方
サブタイトル
しょっちゅう「 XXX ってサイトにある、
このサンプルコードを
コピーして使ってるのだけど、うちの会社のデータ件数だと
タイムアウトしちゃうんです。
どうにか助けて下さい」って言われて、毎回同じ内容で返答していて、流石に疲れてきたので
ちゃんとパフォーマンスの話をするからお前ら聞いてろ下さい
誰?
大橋啓介と
申します
何やってる?
@soundTricker318http://goo.gl/ZpUOs
話す事
Contents
うそ…私のスクリプ
ト遅すぎ?
パフォーマンスの良い書き
方まとめ
Start End
公式ドキュメントにも有りますが…
• GAS は書き方によって非常にパフォーマンスが変わります。
うそ…私の…
質問
GAS を使ったことがある人?
質問
GAS でサンプルコードをコピーして使ったことがある人?
質問
GAS でサンプルコードをコピーして社内のシステムとして使っている人?
質問
GAS でサンプルコードをコピーして社内のシステムとして使ってタイムアウトした人?
質問
• 巷のサンプルコードは比較的パフォーマンスが悪いコードを書いている–公式ドキュメントに書いてある書き方を守ってな
いことが多い• https://developers.google.com/apps-script/
best_practices?hl=ja
–多分読みやすくするためにそうしているのだと思います。
サンプルコード…
• パフォーマンスが悪いコードを使うと…–処理が終わらなくてタイムアウトする
–なんか GAS で作るの嫌になる
–GAS 遅い…と言いふらすようになる
–困る
サンプルコード
パフォーマンスの良いスクリプトの書き方
実際に
書きながら
説明します。
• Spreadsheet に色つける処理–データは 100×100 セル
–セルに「 hoge 」と書いてあったら黄色に
–ないなら無職
今回の処理
以下のコードを Spreadsheet 上の GAS で実行すれば作れます。
今回の処理
var arr = ["hoge", "fuga", "foo", "bar" , "bus", " あいうえお ", " かきくけこ "];function make() { var grid = []; var header = []; for (var c = 0; c < 100; c++) { header.push(c + 1); } grid.push(header); for (var r = 1; r < 101; r++) { var row = []; for (var c = 0; c < 100; c++) { row.push(arr[Math.floor(Math.random() * arr.length)]); } grid.push(row); } SpreadsheetApp.getActiveSheet().getRange(1, 1, 101, 100).clear().setValues(grid);}
悪いコード
function badCode() { var spreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o/edit#gid=0"); for(var row = 2; row <= 101; row++) { for(var col = 1; col <= 100; col++) { var value = spreadsheet.getSheetByName(" シート 1").getRange(row, col).getValue(); if (value == "hoge") { spreadsheet.getSheetByName(" シート 1").getRange(row, col).setBackground("yellow"); } } }}
悪いコード
良いコード
function goodCode() { var spreadsheet = SpreadsheetApp.openById("1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o"); var backgrounds = []; var sheet = spreadsheet.getSheetByName("シート1"); var targetRange = sheet.getRange(2, 1, 100, 100); var values = targetRange.getValues(); for(var rowIndex = 0; rowIndex < values.length; rowIndex++) { var row = values[rowIndex]; var backgroundRow = []; for(var colIndex = 0; colIndex < row.length; colIndex++) { var value = row[colIndex]; if (value == "hoge") { backgroundRow.push("yellow"); } else { backgroundRow.push(""); } } backgrounds.push(backgroundRow); } targetRange.setBackgrounds(backgrounds);}
良いコード
① 悪い部分を知る
• 遅い理由を知らないとどこを直してよいかわかりません–実行トランスクリプトを使ってどこに時間がか
かっているか確認する
① 悪い部分を知る
① 悪い部分を知る
② 無駄な API アクセスを減らす
• GAS の Service 呼び出しは全てネットワーク経由で行われるため、時間がかかります。少しでも無駄な呼び出しはしないようにします。
② 減らす
ネットワーク経由
function badCode() { var spreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o/edit#gid=0"); for(var row = 2; row <= 101; row++) { for(var col = 1; col <= 100; col++) { var value = spreadsheet.getSheetByName(" シート 1").getRange(row, col).getValue(); if (value == "hoge") { spreadsheet.getSheetByName(" シート 1").getRange(row, col).setBackground("yellow"); } } }}
② 減らす
function badCode() { var spreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o/edit#gid=0");
var sheet = spreadsheet.getSheetByName("シート1"); for(var row = 2; row <= 101; row++) { for(var col = 1; col <= 100; col++) { var range = sheet.getRange(row, col); var value = range.getValue(); if (value == "hoge") { range.setBackground("yellow"); } } }}
② 減らす
③ なるべく 1 度にする
• ② 同じ原理でより呼び出し回数を減らすためにgetValues,setBackgrounds 等の Batch Operationを利用します。
③1 度にする
通常の getValue()
getValue()
1 セルで 1 回アクセス = 100×100 回アクセス
• ② 同じ原理でより呼び出し回数を減らすためにgetValues,setBackgrounds 等の Batch Operationを利用します。
③1 度にする
getValues()
getValues)
100×100 セルを 1 回アクセス = 1 回アクセス
function badCode() { var spreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o/edit#gid=0");
var sheet = spreadsheet.getSheetByName("シート1"); for(var row = 2; row <= 101; row++) { for(var col = 1; col <= 100; col++) { var range = sheet.getRange(row, col); var value = range.getValue(); if (value == "hoge") { range.setBackground("yellow"); } } }}
③1 度にする
function badCode() { var spreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o/edit#gid=0"); var backgrounds = []; var sheet = spreadsheet.getSheetByName(" シート 1"); var targetRange = sheet.getRange(2, 1, 100, 100); var values = targetRange.getValues(); for(var rowIndex = 0; rowIndex < values.length; rowIndex++) { var row = values[rowIndex]; var backgroundRow = []; for(var colIndex = 0; colIndex < row.length; colIndex++) { var value = range.getValue(); if (value == "hoge") { backgrounds.push("yellow"); } else { backgrounds.push(“"); } } backgrounds.push(backgroundRow); } targetRange.setBackgrounds(backgrounds);}
③1 度にする
• 大体のスクリプトはこの ②、③を実施すればかなりパフォーマンスが良くなります。
④ 我に返る
• 今回の処理は GAS でやる必要がありません。Sheets の標準機能でやれることはそっちでやりましょう– 今回のはセルの条件付き書式でできます。
– 新しい Spreadsheet はかなり細かい書式が使えるので確認して下さい。
④ 我に返る
番外編
openById と openByUrl
• Spreadsheet,Forms 自体などを取得する際に利用する openById と openByUrl というメソッドが有ります。
• これらは比較的 openById の方が速い– ID は URL 中のなんかよくわかない文字の部分がだいたい
それ
openById と Url
https://docs.google.com/spreadsheets/d/1kzi8pXTZZrABgTGbJFqSZrCHMCRL-LEEqQK2n4yQO-o/edit#gid=0
UiApp
• UiApp を利用するときはなるべく ClientHandler 等クライアントサイドで動くイベントハンドラを活用する– UiApp のイベントハンドラは上記以外は全てサーバコール
になりおっそい
– 適切に ClientHandler を利用するとかなり UX がよくなる
UiApp
HtmlService
• HtmlService のパフォーマンスを良くする書き方の話も公式ページに乗っている
HtmlService
• 基本的には– データの取得は非同期に行う
• doGet 内など HtmlService 呼び出し前に行わない
– <html>,<head>,<body> タグなどは利用しない• caja により自動的に <caja-v-html> に変えられるので意味が無い
• 変えられる分時間がかかるので使わない
– Javascript は最後に読み込む• <script> タグはファイルの一番末尾に書いておく
• そのほうが画面が表示されてからクライアントサイドの Javascript が実行される
HtmlService
• 大橋の感覚として– 外部の JS や CSS は使わない方が良い
• 使う場合は <script src=“xxx”> とはせず、 <script> ここにコピペ </script> して、埋め込んでしまう。
• caja は Google 経由で外部 JS 、 CSS を取得するので遅い
– 画面遷移は Javascript を使って一部分だけ切り替える• doGet を毎回すると遅い
• SPA(Single Page Application) の様に JS で画面を切り替えたほうが速い
• というより画面切り替えはしないほうが良い
HtmlService
どうしても遅い
• どうしても遅い時は…– UrlFetchApp を利用して Google API を直接呼び出す。
– 直接呼び出すと、欲しいデータが 1 回の操作で取得できる場合がある• CalendarApp や DriveApp などでよくある
• fields パラメータを利用すると不要なデータを取得しないためネットワーク的にも良い
どうしても遅い
• どうしても遅い時は…
–本当にリアルタイム ( わざわざ人が待って ) でやる必要があるか考える• Trigger を利用して遅延実行 & 通知でも良いのではない
か?
• 朝一回実行すれば大 丈夫なんじゃないか?
• 実行タイミングは定期的でも良いのじゃないか?
どうしても遅い
• どうしても遅い時は…
–ちゃんと GAS で作って良いのか考える
–金で解決できるのなら GAE や GCP を使ったほうが良い
– Spreadsheet を DB として使っている場合もそう• 大 量のデータなら場合によっては Fusion Table
もっと多いなら BigQuery を検討
どうしても遅い
なんでも
GAS症候群に
注意!
まとめ
GAS で困ったら
#gasja
Google Apps API Japanhttp://goo.gl/FcU0C
@soundTricker318http://goo.gl/ZpUOs
会社 : http://goo.gl/CfVPf
個人 : http://goo.gl/kVTv3
解決できない
かもです
が
一緒に
悩めます多分キリッ
End