Загрузка большого объема данных в память - самый эффективный способ сделать это?

У меня есть система поиска / просмотра документации на основе Интернета, которую я разрабатываю для клиента. Частью этой системы является поисковая система, которая позволяет клиенту искать термин (и), содержащийся в документации. У меня созданы необходимые файлы поисковых данных, но нужно загрузить много данных, и загрузка всех данных занимает от 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 раз.

Я подумал, что это было довольно неэффективно, поэтому я написал скрипт для файла test.js, который содержит все вызовы функций выше, и обработал его, чтобы превратить его в гигантский массив, равный структуре данных, создаваемой BookData. Вместо того, чтобы делать все вызовы функций, которые делала старая система, я просто делаю следующее:

var test_array[..........(data structure I need).......]
BookData[BookIndex] = test_array;

Я ожидал увидеть повышение производительности, потому что я удалил все вызовы функций выше, этот метод требует немного больше времени для создания точной структуры данных. Я должен отметить, что «test_array» содержит чуть более 90 000 элементов в моем реальном тесте.

Кажется, что оба метода загрузки данных имеют примерно одинаковую загрузку ЦП. Я был удивлен, обнаружив это, так как ожидал, что второй метод потребует небольшого процессорного времени, так как структура данных создается заранее.

Пожалуйста, порекомендуйте?

 user21009911 нояб. 2010 г., 20:23
К сожалению, мне нужно загрузить все сразу. Это приложение представляет собой систему просмотра документов, которая имеет возможность поиска. Когда пользователь «ищет» ключевое слово, все данные для поиска должны присутствовать прямо тогда ... Имея начальное время загрузки, затем дополнительное время загрузки, когда пользователь использует функцию «поиска», это то, что мы имели в более ранних версиях и была одной из самых больших жалоб, которые мы хотели решить для нашей переписки.
 jira11 нояб. 2010 г., 19:57
Нужно ли загружать все данные одновременно? Я бы сделал ленивую загрузку - т.е. загружать только необходимые записи. Кроме того, почему бы не использовать более структурированный формат для хранения данных, будь то RDBMS или BerkeleyDb и т. Д.?

Ответы на вопрос(4)

split(), Это самый быстрый способ построить массив в Javascript.

Есть отличная статья, очень похожая проблема, от людей, которые создали поиск по Flickr:http://code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/

9 000 000 точек в Firefox 3.64.

1: Stephen's GetJSON Method
2) My function based push method
3) My pre-processed array appending method:

Я выполнил свои тесты двумя способами: во время первой итерации я импортировал 100 файлов, содержащих 10000 строк данных, каждая строка содержала 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

Интересные результаты, если не сказать больше. Я закрыл браузер после загрузки каждой веб-страницы и запускал тесты по 4 раза каждый, чтобы минимизировать влияние сетевого трафика / вариаций. (бегал по сети, используя файловый сервер). Число, которое вы видите, является средним, хотя отдельные пробеги отличались не более чем на секунду или две.

 user21009912 нояб. 2010 г., 16:50
Извините, это была опечатка! В моем «реальном» коде это $ .getScript (string, recursiveCall); как ты говорил. Хороший улов, хотя, я обновлю ОП.
 Day12 нояб. 2010 г., 02:51
В своем вопросе вы говорите: «Это последовательно обрабатывает массив файлов с интервалом в 1 мс между файлами». но на самом деле, глядя на код, который вы разместили, он этого не делает. recursiveCall делает перерыв 1 мс перед повторным вызовом recursiveCall (), почти загружая все 100 файлов параллельно, а не последовательно. Должны изменитьсяsetTimeout(function(){$.getScript(string,recursiveCall());},1); вsetTimeout(function(){$.getScript(string,recursiveCall);},1); чтобы получить поведение, которое вы описываете, что может несколько изменить ваши результаты?

Вместо того, чтобы использовать$.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
    ]
}

Затем вы можете сделать один запрос, загрузить все необходимые данные и двигаться дальше ... Хотя браузер может изначально заблокироваться (хотя,возможно, нет), это было бы, вероятно,МНОГО быстрее, таким образом.

Вот хороший учебник JSON, если вы не знакомы:http://www.webmonkey.com/2010/02/get_started_with_json/

 Stephen12 нояб. 2010 г., 16:31
Очень хороший звонок @ Day.
 Day12 нояб. 2010 г., 03:16
Для этого JSON-объекта требуется немало дополнительных байтов и обработки с повторяющимися именами значений. Как насчет данных JSON в формате, который совпадает с тем, что заканчивается вBookData[BookIndex]? то есть данные JSON более плотные[[0,[0,1,2,3,4,5,6,7,8]],[1,[0,1,2,3,4,5,6,7,8]],...] а такжеAddToBookData становится простоArray.prototype.push.apply(BookData[BookIndex], json), Это ускоряет вещи еще?
 user21009911 нояб. 2010 г., 20:25
Спасибо, мы рассматривали возможность использования JSON для нашего формата данных, но по какой-то причине он был отложен. Я попытаюсь преобразовать мое тестовое приложение в формат JSON и сообщу. В начальных версиях тестирования мы обнаружили, что один файл заблокировал браузер на недопустимое количество времени, что заставляет пользователей думать, что браузер заблокирован. Возможно, с новыми методами транспортировки данных, которые вы обрисовали в общих чертах, это не будет такой большой проблемой.
 Stephen11 нояб. 2010 г., 20:30
Да, я бы предположил, что первоначальная блокировка была вызвана загрузкой одного файла с 90 000 вызовов функций.
 user21009912 нояб. 2010 г., 22:58
Я реализовал метод загрузки данных JSON, как вы предложили, Day, и он в среднем на 1-3 секунды быстрее, чем оригинальная реализация Стивена. Фактическая передача файлов, как сообщает консоль Firebug, занимает около 2-3 мс. Я думаю, что большая часть времени уходит на обработку этих данных после загрузки.
Решение Вопроса

есть две основные области для оптимизации загрузки данных, которые можно рассмотреть и решить отдельно:

Загрузка данных с сервера, Вместо одного большого файла вы должны выиграть от параллельной загрузки нескольких меньших файлов. Поэкспериментируйте с количеством одновременных загрузок, имейте в виду ограничения браузера и уменьшение отдачи от слишком большого количества параллельных соединений. См мойпараллельно противпоследовательный эксперименты на jsfiddle, но имейте в виду, что результаты будут отличаться из-за капризов извлечения тестовых данных из github - лучше всего тестировать свои собственные данные в более жестко контролируемых условиях.Построение вашей структуры данных максимально эффективно, Ваш результат выглядит как многомерный массив,эта интересная статья на производительности массива JavaScript может дать вам некоторые идеи для экспериментов в этой области.

Но я не уверен, насколько далеко вы действительно сможете пойти с оптимизацией загрузки данных в одиночку. Чтобы решить реальную проблему с вашим приложением (браузер блокируется слишком долго), вы рассматривали такие варианты, как?

Использование веб-работников

Веб-работники может не поддерживаться всеми вашими целевыми браузерами, но должно препятствовать блокировке основного потока браузера во время обработки данных.

Для браузеров без рабочих, вы можете рассмотреть возможность увеличенияsetTimeout немного интервал, чтобы дать браузеру время для обслуживания пользователя, а также ваш JS. Это на самом деле сделает вещи немного медленнее, но может увеличить счастье пользователя в сочетании со следующим пунктом.

Предоставление обратной связи о прогрессе

Как для рабочих, так и для рабочих браузеров, потратьте некоторое время, чтобы обновить DOM с помощью индикатора выполнения. Вы знаете, сколько файлов осталось загрузить, поэтому прогресс должен быть достаточно последовательным, и хотя на самом деле все может быть немного медленнее,пользователи будут чувствовать себя лучше если они получают обратную связь и не думают, что браузер заблокировал их.

Ленивая Загрузка

По предложениюJIRA в своем комментарии. Если Google Instant может выполнять поиск по всей сети по мере ввода, действительно ли невозможно, чтобы сервер возвращал файл со всеми местоположениями поискового ключевого слова в текущей книге? Этот файл должен быть намного меньше и быстрее загружаться, чем расположение всех слов в книге, что, как я полагаю, вы сейчас пытаетесь загрузить как можно быстрее?

 user21009912 нояб. 2010 г., 17:11
Спасибо за ответ и экспериментальный код! Я собираюсь взглянуть на загрузку поисковых данных полупараллельным способом. Тем не менее, я думаю, что узким местом сейчас является не загрузка файла, а обработка файла после его загрузки. Web Workers - это решение, которое я исследовал ранее, и, хотя они выглядят как отличная идея, я работаю в корпоративной среде, в которой мы должны поддерживать IE, чтобы исключить Web Workers. Индикатор выполнения - отличная идея, и я уже реализовал ее в своем реальном коде, но отличное предложение! Это делает ожидание терпимым.

Ваш ответ на вопрос