Что быстрее: ScriptDb или SpreadsheetApp?
Допустим, у меня есть скрипт, который перебирает список из 400 объектов. Каждый объект имеет от 1 до 10 свойств. Каждое свойство представляет собой строку разумного размера или несколько большое целое число.
Есть ли существенная разница в производительности при сохранении этих объектов в ScriptDB по сравнению с сохранением их в Spreadsheet(без выполнения одной массовой операции).
2 ответа
Управляющее резюме
Да, есть существенная разница! Огромный! И я должен признать, что этот эксперимент оказался не таким, как я ожидал.
При таком объеме данных запись в электронную таблицу всегда выполнялась намного быстрее, чем при использовании ScriptDB.
Эти эксперименты подтверждают утверждения о массовых операциях в рекомендациях по использованию скриптов Google Apps. Сохранение данных в электронной таблице с использованием одного setValues()
Вызов был на 75% быстрее, чем построчно, и на два порядка быстрее, чем сотовая связь.
С другой стороны, рекомендации по использованию Spreadsheet.flush()
следует учитывать внимательно, из-за воздействия на производительность. В этих экспериментах одна запись таблицы из 4000 ячеек заняла менее 50 мс, и добавление вызова flush()
увеличено до 610 мсек - все равно меньше секунды, но налог на порядок кажется нелепым. призвание flush()
для каждой из 400 строк в электронной таблице образцов операция заняла почти 12 секунд, а без нее всего 164 мс. Если вы столкнулись с ошибками превышения максимального времени выполнения, вы можете воспользоваться как оптимизацией кода, так и удалением вызовов flush()
,
Результаты эксперимента
Все сроки были получены в соответствии с методикой, описанной в разделе Как измерить время, затраченное на выполнение функции. Время выражается в миллисекундах.
Вот результаты одного прохода пяти разных подходов, два с использованием ScriptDB
три записи в электронные таблицы с одинаковыми исходными данными. (400 объектов с 5 строковыми и 5 числовыми атрибутами)
Эксперимент 1
- Истекшее время для проверки ScriptDB/Object: 53529
- Истекшее время для ScriptDB / пакетного теста: 37700
- Истекшее время для проверки таблицы / объекта: 145
- Истекшее время для проверки таблицы / атрибута: 4045
- Истекшее время для таблицы / массового теста: 32
Эффект Spreadsheet.flush()
Эксперимент 2
В этом эксперименте единственным отличием от эксперимента 1 было то, что мы назвали Spreadsheet.flush()
после каждого setValue/s
вызов. Стоимость этого является значительной (около 700%), но не меняет рекомендацию использовать электронную таблицу поверх ScriptDB по соображениям скорости, поскольку запись в электронные таблицы все еще происходит быстрее.
- Истекшее время для проверки ScriptDB / объекта: 55282
- Истекшее время для ScriptDB / пакетного теста: 37370
- Истекшее время для проверки таблицы / объекта: 11888
- Истекшее время для проверки таблицы / атрибута: 117388
- Истекшее время для таблицы / массового теста: 610
Примечание. Этот эксперимент часто завершался с превышением максимального времени выполнения.
Пусть покупатель будет бдителен
Вы читаете это на паутинах, так что это должно быть правдой! Но возьмите это с зерном соли.
- Это результаты очень малых размеров выборки, которые могут быть не полностью воспроизводимыми.
- Эти результаты измеряют то, что постоянно меняется - хотя они наблюдались 28 февраля 2013 года, система, которую они измерили, может быть совершенно другой, когда вы прочитаете это.
- На эффективность этих операций влияют многие факторы, которые не контролируются в этих экспериментах; Например, кеширование инструкций и промежуточных результатов, а также загрузка сервера.
- Может быть, просто может быть, кто-то из Google прочтет это и улучшит эффективность 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);
}
}
}