什么是更快:ScriptDb或SpreadsheetApp?

Ant*_*doi 11 performance google-sheets google-apps-script

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

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

Mog*_*dad 30

执行摘要

是的,有一个显着的区别!巨大!而且我必须承认,这个实验并没有达到我的预期.

使用这些数据,写入电子表格总是比使用ScriptDB快得多.

这些实验支持Google Apps脚本最佳实践中有关批量操作的断言.使用单个setValues()调用在电子表格中保存数据比逐行保存快75%,比逐个单元快两个数量级.

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

实验结果

所有时序都是按照如何测量函数执行所花费的时间中描述的技术得出的.时间以毫秒表示.

以下是五种不同方法的单次传递的结果,两次使用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

注意:此实验通常在超过最长执行时间时终止.

买者自负

你在网上读到这个,所以一定是真的!但要带上一粒盐.

  • 这些是非常小的样本量的结果,并且可能不是完全可再现的.
  • 这些结果正在衡量一些不断变化的事情 - 当他们在2013年2月28日被观察时,他们测量的系统在您阅读时可能完全不同.
  • 这些操作的效率受到许多因素的影响,这些因素在这些实验中没有得到控制; 例如,缓存指令和中间结果以及服务器负载.
  • 也许,只是也许,谷歌的某个人会读到这个,并提高ScriptDB的效率!

代码

如果您想要执行(或更好地改进)这些实验,请创建一个空白电子表格,然后将其复制到其中的新脚本中.这也是一个要点.

/**
 * 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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我只是提到这个实验没有探索数据库的查找方面.假设您有一百万个项目的数据库并且您想要获取单个特定项目,我觉得ScriptDb在查找时表现更好,因为您不必将整个数据集加载到数组中.但就像你说的那样,在你测试之前你不会知道. (2认同)
  • 另一个+1给出了很好的答案.这里有一些更平均的速度...用于查询一个属性并返回结果中的第一个记录(电子表格:使用`getDataRange().getValues()`并迭代它) - Spreadsheet 179ms,ScriptDb 87ms(注意ScriptDb ,`result = db.query({query obj})`需要0ms(平均来说!)和`firstRecord = result.next()`需要87ms).用于将一条记录写入数据库(电子表格:使用`appendRow()`) - 电子表格111ms,ScriptDb 164ms. (2认同)

Sca*_*ael 4

ScriptDB 已被弃用。不使用。