【发布时间】:2013-02-28 21:23:25
【问题描述】:
假设我有一个迭代 400 个对象列表的脚本。 每个对象都有 1 到 10 个属性。 每个属性都是一个合理大小的字符串或一个较大的整数。
将这些对象保存到 ScriptDB 与将它们保存到电子表格(无需在一次批量操作中完成)在性能上是否存在显着差异。
【问题讨论】:
标签: performance google-apps-script google-sheets
假设我有一个迭代 400 个对象列表的脚本。 每个对象都有 1 到 10 个属性。 每个属性都是一个合理大小的字符串或一个较大的整数。
将这些对象保存到 ScriptDB 与将它们保存到电子表格(无需在一次批量操作中完成)在性能上是否存在显着差异。
【问题讨论】:
标签: performance google-apps-script google-sheets
是的,有很大的不同! 巨大!而且我不得不承认这个实验并没有达到我的预期。
有了这么多数据,写入电子表格总是比使用 ScriptDB 快得多。
这些实验支持Google Apps Script Best Practices 中关于批量操作的断言。使用单个 setValues() 调用在电子表格中保存数据比逐行快 75%,比逐单元快两个数量级。
另一方面,由于会影响性能,因此应仔细考虑使用 Spreadsheet.flush() 的建议。在这些实验中,单次写入 4000 个单元格的电子表格花费了不到 50 毫秒,而添加对 flush() 的调用将其增加到 610 毫秒 - 仍然不到一秒,但一个数量级的税收似乎很可笑。为示例电子表格中的 400 行中的每一行调用 flush() 会使操作花费将近 12 秒,而没有它时只需要 164 毫秒。如果您遇到 Exceeded maximum execution time 错误,您可能会从优化代码和删除对 flush() 的调用中受益。
所有时间都是按照How to measure time taken by a function to execute 中描述的技术得出的。时间以毫秒为单位。
以下是五种不同方法的单次传递的结果,其中两种使用ScriptDB,三种写入电子表格,均使用相同的源数据。 (400 个对象,具有 5 个字符串和 5 个数字属性)
Spreadsheet.flush()的效果
在这个实验中,与实验 1 的唯一区别是我们在每次调用 setValue/s 之后调用了 Spreadsheet.flush()。这样做的成本很高(大约 700%),但出于速度原因,并没有改变使用电子表格而不是 ScriptDB 的建议,因为写入电子表格仍然更快。
注意:此实验经常因超过最大执行时间而被终止。
您正在互联网上阅读此内容,所以它一定是真的!但要持保留态度。
如果您想执行(或改进)这些实验,请创建一个空白电子表格,并将其复制到其中的新脚本中。 这也可以通过as a 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);
}
}
}
【讨论】:
getDataRange().getValues() 并迭代它) - 电子表格 179 毫秒,ScriptDb 87 毫秒(请注意,对于 ScriptDb,result = db.query({query obj}) 需要 0 毫秒(平均!)和firstRecord = result.next() 需要 87 毫秒)。用于将一条记录写入数据库(电子表格:使用 appendRow())-电子表格 111 毫秒,ScriptDb 164 毫秒。
ScriptDB 已被弃用。不要使用。
【讨论】: