【问题标题】:What is faster: ScriptDb or SpreadsheetApp?哪个更快:ScriptDb 或 SpreadsheetApp?
【发布时间】:2013-02-28 21:23:25
【问题描述】:

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

将这些对象保存到 ScriptDB 与将它们保存到电子表格(无需在一次批量操作中完成)在性能上是否存在显着差异。

【问题讨论】:

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


    【解决方案1】:

    执行摘要

    是的,有很大的不同! 巨大!而且我不得不承认这个实验并没有达到我的预期。

    有了这么多数据,写入电子表格总是比使用 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 个数字属性)

    实验一

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

    Spreadsheet.flush()的效果

    实验二

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

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

    注意:此实验经常因超过最大执行时间而被终止。

    注意事项

    您正在互联网上阅读此内容,所以它一定是真的!但要持保留态度。

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

    【讨论】:

    • 哇,哇……这证实了我的感受。电子表格服务最近(相当)得到了改进,我注意到在我 2 年前编写的一些脚本上,它们的运行速度比我最初编写它们时快得多。还有另一系列的实验结果很有趣heremegabyte1024
    • @Serge 感谢您指出这个问题!您是否再次尝试过这些基准?我想知道他们是否有所改善。 Eric_Koleda 的回答说他会报告任何进展的更新 - 也许有些他不知道?
    • 按照您的建议,我重新运行测试并获得比以前获得的更好的结果...最快大约快 2 倍(265 完整测试,排序为 13 毫秒)但仍然有几个不时 700 毫秒(可能是 10 次中的 1 次?)。我真的不知道我今天是否幸运,或者服务器引擎是否得到了改进,只有谷歌的人才能说出来;-)但无论如何都是好消息(总是很幸运^^) - 尽管如此,我想我在某处读到他们最近改善了气体性能,但我不记得在哪里或确切时间......对不起。
    • 我只是提到这个实验没有探索数据库的查找方面。假设您有大约一百万个项目的数据库,并且想要获取单个特定项目,我觉得 ScriptDb 在查找时会表现得更好,因为您不必将整个数据集加载到数组中。但就像你说的,直到你测试你才会知道。
    • 另一个 +1 以获得出色的答案。这里有一些更平均的速度...用于查询一个属性并返回结果中的第一条记录(电子表格:使用 getDataRange().getValues() 并迭代它) - 电子表格 179 毫秒,ScriptDb 87 毫秒(请注意,对于 ScriptDb,result = db.query({query obj}) 需要 0 毫秒(平均!)和firstRecord = result.next() 需要 87 毫秒)。用于将一条记录写入数据库(电子表格:使用 appendRow())-电子表格 111 毫秒,ScriptDb 164 毫秒。
    【解决方案2】:

    ScriptDB 已被弃用。不要使用。

    【讨论】:

      猜你喜欢
      • 2010-11-10
      • 2012-10-03
      • 2011-02-15
      • 2012-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      相关资源
      最近更新 更多