Загрузка большого объема данных в память - самый эффективный способ сделать это?
У меня есть веб-система поиска/просмотра документации, которую я разрабатываю для клиента. Часть этой системы - это поисковая система, которая позволяет клиенту искать термин [s], содержащийся в документации. У меня есть необходимые файлы данных поиска, но есть много данных, которые необходимо загрузить, и для загрузки всех данных требуется от 8 до 20 секунд. Данные разбиты на 40-100 файлов, в зависимости от того, какую документацию нужно искать. Каждый файл имеет размер от 40 до 350 килобайт.
Кроме того, это приложение должно иметь возможность запускать локальную файловую систему, а также через веб-сервер.
Когда страница загружается, я могу сгенерировать список файлов данных поиска, которые мне нужны. Весь этот список должен быть загружен до того, как веб-страницу можно считать функциональной.
С этим предисловием в стороне, давайте посмотрим, как я это делаю сейчас.
После того, как я знаю, что загружена вся веб-страница, я вызываю функцию loadData()
function loadData(){
var d = new Date();
var curr_min = d.getMinutes();
var curr_sec = d.getSeconds();
var curr_mil = d.getMilliseconds();
console.log("test.js started background loading, time is: " + curr_min + ":" + curr_sec+ ":" + curr_mil);
recursiveCall();
}
function recursiveCall(){
if(file_array.length > 0){
var string = file_array.pop();
setTimeout(function(){$.getScript(string,recursiveCall);},1);
}
else{
var d = new Date();
var curr_min = d.getMinutes();
var curr_sec = d.getSeconds();
var curr_mil = d.getMilliseconds();
console.log("test.js stopped background loading, time is: " + curr_min + ":" + curr_sec+ ":" + curr_mil);
}
}
То, что это делает, - это процесс массива файлов последовательно, с перерывом 1 мс между файлами. Это помогает предотвратить полную блокировку браузера во время процесса загрузки, но браузер по-прежнему стремится увязнуть, загрузив данные. Каждый из загружаемых файлов выглядит следующим образом:
AddToBookData(0,[0,1,2,3,4,5,6,7,8]);
AddToBookData(1,[0,1,2,3,4,5,6,7,8]);
AddToBookData(2,[0,1,2,3,4,5,6,7,8]);
Где каждая строка представляет собой вызов функции, который добавляет данные в массив. Функция "AddToBookData" просто выполняет следующие действия:
function AddToBookData(index1,value1){
BookData[BookIndex].push([index1,value1]);
}
Это существующая система. После загрузки всех данных "AddToBookData" можно вызвать 100 000 раз.
Я понял, что это было довольно неэффективно, поэтому я написал script, чтобы взять файл test.js, который содержит все вызовы функций выше, и обработал его, чтобы изменить его на гигантский массив, который равен структуре данных, которая BookData создает. Вместо выполнения всех вызовов функций, которые выполняла старая система, я просто делаю следующее:
var test_array[..........(data structure I need).......]
BookData[BookIndex] = test_array;
Я ожидал увидеть увеличение производительности, потому что я удалял все вызовы функций выше, этот метод занимает немного больше времени для создания точной структуры данных. Я должен отметить, что "test_array" содержит чуть более 90 000 элементов в моем реальном мире.
Кажется, что оба метода загрузки данных имеют примерно одинаковое использование ЦП. Я с удивлением обнаружил это, так как ожидал, что второй метод потребует небольшого времени процессора, так как структура данных создается заранее.
Просьба сообщить?
Ответы
Ответ 1
Похоже, есть две основные области для оптимизации загрузки данных, которые можно рассматривать и решать отдельно:
- Загрузка данных с сервера. Вместо одного большого файла вы должны получать выигрыши от параллельных нагрузок из нескольких меньших файлов. Экспериментируйте с количеством одновременных нагрузок, учитывайте границы браузера и уменьшая отдачу от слишком большого количества параллельных соединений. См. Мой parallel vs последовательные эксперименты на jsfiddle, но несут в виду, что результаты будут отличаться из-за капризов по извлечению тестовых данных из github - вам лучше всего тестировать свои собственные данные в более жестких условиях.
- Построение структуры данных максимально эффективно. Ваш результат выглядит как многомерный массив, эта интересная статья о производительности массива JavaScript может дать вам некоторые идеи для экспериментов в этой области.
Но я не уверен, насколько вы действительно сможете оптимизировать загрузку данных. Чтобы решить актуальную проблему с вашим приложением (блокировка браузера слишком долго), вы рассмотрели такие параметры, как?
Использование веб-работников
Работники Web могут не поддерживаться всеми целевыми браузерами, но должны препятствовать блокировке основного потока браузера при обработке данных.
Для браузеров без работников вы можете немного увеличить интервал setTimeout
, чтобы предоставить браузеру время обслуживания пользователя, а также вашего JS. Это сделает вещи на самом деле немного медленнее, но может увеличить счастье пользователя в сочетании со следующей точкой.
Предоставление отзывов о прогрессе
Для обозревателей, работающих на рабочих и работающих с недостаточным рабочим временем, потребуется некоторое время, чтобы обновить DOM с помощью индикатора выполнения. Вы знаете, сколько файлов вы оставили для загрузки, поэтому прогресс должен быть достаточно последовательным, и хотя все может быть немного медленнее, пользователи будут чувствовать себя лучше, если они получают обратную связь и не думают, что браузер заблокировал их.
Lazy Loading
Как было предложено jira в его комментарии. Если Google Instant может выполнять поиск по всему веб-сайту по мере ввода, действительно ли невозможно вернуть сервер во все местоположения ключевого слова поиска в текущей книге? Этот файл должен быть намного меньше и быстрее загружать, чем местоположения всех слов в книге, что я предполагаю, что вы в настоящее время пытаетесь загрузиться так быстро, как можете?
Ответ 2
Я тестировал три метода загрузки одного и того же набора данных из 9 000 000 точек в Firefox 3.64.
1: Stephen GetJSON Method
2) My function based push method
3) My pre-processed array appending method:
Я провел тесты двумя способами: первая итерация тестирования я импортировал 100 файлов, содержащих 10 000 строк данных, каждая строка содержит 9 элементов данных [0,1,2,3,4,5,6,7,8]
Второе взаимодействие я попробовал комбинировать файлы, так что я импортировал 1 файл с 9 миллионами точек данных.
Это намного больше, чем набор данных, который я буду использовать, но это помогает продемонстрировать скорость различных методов импорта.
Separate files: Combined file:
JSON: 34 seconds 34
FUNC-BASED: 17.5 24
ARRAY-BASED: 23 46
Интересные результаты, если не сказать больше. Я закрыл браузер после загрузки каждой веб-страницы и каждые четыре раза выполнял тесты, чтобы минимизировать влияние сетевого трафика/изменения. (работает через сеть, используя файловый сервер). Число, которое вы видите, является средним, хотя отдельные прогоны отличаются не более чем на секунду или два.
Ответ 3
Вместо использования $.getScript
для загрузки файлов JavaScript, содержащих вызовы функций, рассмотрите возможность использования $.getJSON
. Это может повысить производительность. Теперь файлы будут выглядеть следующим образом:
{
"key" : 0,
"values" : [0,1,2,3,4,5,6,7,8]
}
После получения ответа JSON вы можете вызвать AddToBookData
на нем, например:
function AddToBookData(json) {
BookData[BookIndex].push([json.key,json.values]);
}
Если ваши файлы имеют несколько наборов вызовов AddToBookData, вы можете их структурировать следующим образом:
[
{
"key" : 0,
"values" : [0,1,2,3,4,5,6,7,8]
},
{
"key" : 1,
"values" : [0,1,2,3,4,5,6,7,8]
},
{
"key" : 2,
"values" : [0,1,2,3,4,5,6,7,8]
}
]
И затем измените функцию AddToBookData
, чтобы компенсировать новую структуру:
function AddToBookData(json) {
$.each(json, function(index, data) {
BookData[BookIndex].push([data.key,data.values]);
});
}
Добавление
Я подозреваю, что независимо от того, какой метод вы используете для переноса данных из файлов в массив BookData
, истинным узким местом является огромное количество запросов. Должны ли файлы разбиваться на 40-100? Если вы измените формат JSON, вы можете загрузить один файл, который выглядит так:
{
"file1" : [
{
"key" : 0,
"values" : [0,1,2,3,4,5,6,7,8]
},
// all the rest...
],
"file2" : [
{
"key" : 1,
"values" : [0,1,2,3,4,5,6,7,8]
},
// yadda yadda
]
}
Затем вы можете сделать один запрос, загрузить все необходимые вам данные и перейти... Хотя браузер может сначала заблокировать (хотя, может быть, и нет), он, вероятно, будет MUCH быстрее таким образом.
Вот хороший учебник JSON, если вы не знакомы: http://www.webmonkey.com/2010/02/get_started_with_json/
Ответ 4
Извлеките все данные в виде строки и используйте split()
. Это самый быстрый способ создания массива в Javascript.
Там отличная статья - очень схожая проблема, от людей, которые построили поиск flickr: http://code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/