400 個のオブジェクトのリストを反復処理するスクリプトがあるとします。各オブジェクトには、1 ~ 10 個のプロパティがあります。各プロパティは、適切なサイズの文字列またはやや大きな整数です。
これらのオブジェクトを ScriptDB に保存する場合とスプレッドシートに保存する場合のパフォーマンスに大きな違いはありますか (1 回の一括操作で行う必要はありません)。
400 個のオブジェクトのリストを反復処理するスクリプトがあるとします。各オブジェクトには、1 ~ 10 個のプロパティがあります。各プロパティは、適切なサイズの文字列またはやや大きな整数です。
これらのオブジェクトを ScriptDB に保存する場合とスプレッドシートに保存する場合のパフォーマンスに大きな違いはありますか (1 回の一括操作で行う必要はありません)。
はい、大きな違いがあります!巨大!そして、この実験が私が期待したようにはならなかったことを認めなければなりません。
この量のデータでは、スプレッドシートへの書き込みは常に、ScriptDB を使用するよりもはるかに高速でした。
これらの実験は、 Google Apps スクリプトのベスト プラクティスの一括操作に関する主張を裏付けています。単一のsetValues()
呼び出しを使用してスプレッドシートにデータを保存すると、行単位よりも 75% 速く、セル単位よりも 2 桁速くなりました。
一方、Spreadsheet.flush()
パフォーマンスへの影響があるため、使用する推奨事項は慎重に検討する必要があります。これらの実験では、4000 セルのスプレッドシートの 1 回の書き込みにかかる時間は 50 ミリ秒未満でした。呼び出しを追加すると、flush()
それが 610 ミリ秒に増加しました。それでも 1 秒未満ですが、1 桁の税金はばかげているように見えます。サンプル スプレッドシートの 400 行のそれぞれを呼び出すflush()
と、操作に約 12 秒かかりましたが、それがない場合は 164 ミリ秒しかかかりませんでした。Exceeded maximum execution timeエラーが発生している場合は、コードを最適化し、flush()
.
すべてのタイミングは、関数の実行にかかる時間を測定する方法で説明されている手法に従って導出されました。時間はミリ秒単位で表されます。
以下は、5 つの異なるアプローチ (2 つは を使用ScriptDB
)、3 つはスプレッドシートへの書き込み、すべて同じソース データを使用した 1 回のパスの結果です。(5 つの文字列と 5 つの数値属性を持つ 400 個のオブジェクト)
Spreadsheet.flush()
この実験では、実験 1 との唯一の違いは、呼び出しSpreadsheet.flush()
のたびにsetValue/s
呼び出しを行ったことです。そうすることのコストは劇的 (約 700%) ですが、スプレッドシートへの書き込みは依然として高速であるため、速度の理由から ScriptDB よりもスプレッドシートを使用するという推奨事項は変わりません。
注: この実験は、Exceeded maximum execution timeで頻繁に強制終了されました。
あなたはインターウェブでこれを読んでいるので、それは本当でなければなりません! しかし、一粒の塩でそれを取ります。
これらの実験を実行したい (またはさらに改善したい) 場合は、空のスプレッドシートを作成し、これをその中の新しいスクリプトにコピーします。これはgist としても利用できます。
/**
* Run experiments to measure speed of various approaches to saving data in
* Google App Script (GAS).
*/
function testSpeed() {
var numObj = 400;
var numAttr = 10;
var doFlush = false; // Set true to activate calls to SpreadsheetApp.flush()
var arr = buildArray(numObj,numAttr);
var start, stop; // time catchers
var db = ScriptDb.getMyDb();
var sheet;
// Save into ScriptDB, Object at a time
deleteAll(); // Clear ScriptDB
start = new Date().getTime();
for (var i=1; i<=numObj; i++) {
db.save({type: "myObj", data:arr[i]});
}
stop = new Date().getTime();
Logger.log("Elapsed time for ScriptDB/Object test: " + (stop - start));
// Save into ScriptDB, Batch
var items = [];
// Restructure data - this is done outside the timed loop, assuming that
// the data would not be in an array if we were using this approach.
for (var obj=1; obj<=numObj; obj++) {
var thisObj = new Object();
for (var attr=0; attr < numAttr; attr++) {
thisObj[arr[0][attr]] = arr[obj][attr];
}
items.push(thisObj);
}
deleteAll(); // Clear ScriptDB
start = new Date().getTime();
db.saveBatch(items, false);
stop = new Date().getTime();
Logger.log("Elapsed time for ScriptDB/Batch test: " + (stop - start));
// Save into Spreadsheet, Object at a time
sheet = SpreadsheetApp.getActive().getActiveSheet().clear();
start = new Date().getTime();
for (var row=0; row<=numObj; row++) {
var values = [];
values.push(arr[row]);
sheet.getRange(row+1, 1, 1, numAttr).setValues(values);
if (doFlush) SpreadsheetApp.flush();
}
stop = new Date().getTime();
Logger.log("Elapsed time for Spreadsheet/Object test: " + (stop - start));
// Save into Spreadsheet, Attribute at a time
sheet = SpreadsheetApp.getActive().getActiveSheet().clear();
start = new Date().getTime();
for (var row=0; row<=numObj; row++) {
for (var cell=0; cell<numAttr; cell++) {
sheet.getRange(row+1, cell+1, 1, 1).setValue(arr[row][cell]);
if (doFlush) SpreadsheetApp.flush();
}
}
stop = new Date().getTime();
Logger.log("Elapsed time for Spreadsheet/Attribute test: " + (stop - start));
// Save into Spreadsheet, Bulk
sheet = SpreadsheetApp.getActive().getActiveSheet().clear();
start = new Date().getTime();
sheet.getRange(1, 1, numObj+1, numAttr).setValues(arr);
if (doFlush) SpreadsheetApp.flush();
stop = new Date().getTime();
Logger.log("Elapsed time for Spreadsheet/Bulk test: " + (stop - start));
}
/**
* Create a two-dimensional array populated with 'numObj' rows of 'numAttr' cells.
*/
function buildArray(numObj,numAttr) {
numObj = numObj | 400;
numAttr = numAttr | 10;
var array = [];
for (var obj = 0; obj <= numObj; obj++) {
array[obj] = [];
for (var attr = 0; attr < numAttr; attr++) {
var value;
if (obj == 0) {
// Define attribute names / column headers
value = "Attr"+attr;
}
else {
value = ((attr % 2) == 0) ? "This is a reasonable sized string for testing purposes, not too long, not too short." : Number.MAX_VALUE;
}
array[obj].push(value);
}
}
return array
}
function deleteAll() {
var db = ScriptDb.getMyDb();
while (true) {
var result = db.query({}); // get everything, up to limit
if (result.getSize() == 0) {
break;
}
while (result.hasNext()) {
var item = result.next()
db.remove(item);
}
}
}
ScriptDB は廃止されました。使用禁止。