什么是更快:ScriptDb或SpreadsheetApp?

时间:2013-02-28 21:23:25

标签: performance google-apps-script google-sheets

假设我有一个脚本迭代400个对象的列表。 每个对象具有1到10个属性。 每个属性都是合理的大小字符串或稍大的整数。

将这些对象保存到ScriptDB与将它们保存到Spreadsheet(没有在一次批量操作中执行)的性能存在显着差异。

2 个答案:

答案 0 :(得分:30)

执行摘要

是的,有显着差异! 巨大!而且我不得不承认这个实验没有达到我的预期。

有了这么多数据,写入电子表格总是比使用ScriptDB快得多。

这些实验支持Google Apps Script Best Practices中有关批量操作的断言。使用单个setValues()调用在电子表格中保存数据的速度比逐行快75%,比逐个单元格快两个数量级。

另一方面,由于性能影响,应谨慎考虑使用Spreadsheet.flush()的建议。在这些实验中,单个4000个单元格的电子表格写入时间不到50毫秒,并且添加对flush()的调用将其增加到610毫秒 - 仍然不到一秒钟,但是一个数量级的税收似乎很荒谬。为示例电子表格中的400行中的每一行调用flush()使得操作花费大约12秒,而没有它只需要164毫秒。如果您遇到超出最长执行时间错误,您可以从优化代码和删除对flush()的调用中受益。

实验结果

所有时间都是按照How to measure time taken by a function to execute中描述的技术推导出来的。时间以毫秒表示。

以下是五种不同方法的单次传递的结果,两次使用ScriptDB,三次写入Spreadsheets,所有这些都使用相同的源数据。 (400个具有5个字符串和5个数字属性的对象)

实验1

  • ScriptDB / Object测试的经过时间:53529
  • ScriptDB / Batch测试的经过时间:37700
  • 电子表格/对象测试的已用时间:145
  • 电子表格/属性测试的已用时间:4045
  • 电子表格/批量测试的经过时间:32

Spreadsheet.flush()

的影响

实验2

在此实验中,与实验1的唯一区别是我们在每次Spreadsheet.flush()调用后调用了setValue/s。这样做的成本是惊人的(大约700%),但由于速度原因,不会改变使用电子表格而不是ScriptDB的建议,因为写入电子表格仍然更快。

  • ScriptDB / Object测试的经过时间:55282
  • ScriptDB / Batch测试的经过时间:37370
  • 电子表格/对象测试的已用时间:11888
  • 电子表格/属性测试的已用时间:117388
  • 电子表格/批量测试的经过时间:610

注意:此实验经常被超过最长执行时间杀死。

警告Emptor

你在互联网上看到这个,所以一定是真的!但要带上一粒盐。

  • 这些是非常小的样本量的结果,可能无法完全重现。
  • 这些结果正在衡量一些不断变化的事情 - 当他们在2013年2月28日观察时,他们测量的系统在您阅读时可能会完全不同。
  • 这些操作的效率受到许多因素的影响,这些因素在这些实验中无法控制;缓存指令&例如,中间结果和服务器负载。
  • 也许吧,也许,谷歌的某个人会读到这个,并提高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);
    }
  }
}

答案 1 :(得分:3)

ScriptDB已被弃用。不要使用。