【问题标题】:Loading large amount of data into memory - most efficient way to do this?将大量数据加载到内存中——最有效的方法是什么?
【发布时间】:2010-11-11 18:50:11
【问题描述】:

我正在为客户开发一个基于 Web 的文档搜索/查看系统。该系统的一部分是允许客户搜索文档中包含的术语的搜索系统。我已经创建了必要的搜索数据文件,但是需要加载大量数据,加载所有数据需要 8-20 秒。根据需要搜索的文档,数据分为 40-100 个文件。每个文件大小在 40-350kb 之间。

此外,此应用程序必须能够在本地文件系统上运行,也可以通过网络服务器运行。

当网页加载时,我可以生成我需要加载的搜索数据文件的列表。必须先加载整个列表,然后才能将网页视为功能正常。

说完前言,让我们看看我现在是怎么做的。

知道整个网页加载完毕后,我调用了一个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 个元素。

似乎两种加载数据的方法具有大致相同的 CPU 利用率。我很惊讶地发现这一点,因为我预计第二种方法需要很少的 CPU 时间,因为数据结构是事先创建的。

请指教?

【问题讨论】:

  • 您必须一次加载所有数据吗?我会使其延迟加载 - 即仅加载所需的记录。另外,为什么不使用一些更结构化的格式来存储数据,无论是 RDBMS 还是 BerkeleyDb 等?
  • 不幸的是,我需要一次全部加载。此应用程序是具有搜索功能的文档查看系统。当用户“搜索”一个关键字时,所有搜索数据都需要立即出现......有一个初始加载时间,然后当用户使用“搜索”功能时额外的加载时间是我们在早期版本中所拥有的,并且是我们希望为重写解决的最大投诉之一。

标签: javascript jquery data-structures


【解决方案1】:

看起来有两个优化数据加载的基本领域,可以单独考虑和解决:

  1. 从服务器下载数据。而不是一个大文件,您应该从多个较小文件的并行加载中获得胜利。尝试同时加载的数量,记住浏览器限制和过多并行连接的收益递减。请参阅我在 jsfiddle 上的 parallelsequential 实验,但请记住,由于从 github 提取测试数据的变幻莫测,结果会有所不同 - 您最好在更严格控制的条件下使用自己的数据进行测试。
  2. 尽可能高效地构建数据结构。你的结果看起来像一个多维数组,this interesting article 关于 JavaScript 数组性能可能会给你一些在这方面实验的想法。

但我不确定仅靠优化数据加载你能走多远。为了解决您的应用程序的实际问题(浏览器锁定时间过长),您是否考虑过诸如此类的选项?

使用网络工作者

Web Workers 可能并非所有目标浏览器都支持,但应防止主浏览器线程在处理数据时锁定。

对于没有工作线程的浏览器,您可以考虑稍微增加setTimeout 间隔,以便让浏览器有时间为用户和您的 JS 提供服务。这实际上会使事情变得稍微慢一些,但结合下一点可能会增加用户的幸福感。

提供进度反馈

对于支持 worker 的浏览器和缺少 worker 的浏览器,请花一些时间使用进度条更新 DOM。你知道你还有多少文件要加载,所以进度应该相当一致,尽管事情实际上可能会稍微慢一些,users will feel better 如果他们得到反馈并且认为浏览器没有锁定它们。

延迟加载

正如jira 在他的评论中所建议的那样。如果 Google Instant 可以在我们键入时搜索整个网络,那么真的不可能让服务器返回一个包含当前书籍中搜索关键字的所有位置的文件吗?这个文件应该比书中所有单词的位置更小,加载速度更快,这就是我假设您目前正在尝试尽快加载的文件?

【讨论】:

  • 感谢您的响应和实验代码!我将看看以半并行方式下载搜索数据。但是,我认为现在的瓶颈不是下载文件,而是文件下载后的处理。 Web Workers 是我之前研究过的一个解决方案,虽然它们看起来是个好主意,但我在一个必须支持 IE 的公司环境中工作,因此排除了 Web Workers。进度条是个好主意,我已经在我的真实代码中实现了一些东西,但是很好的建议!这让等待变得可以忍受。
【解决方案2】:

我测试了三种将相同的 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 个包含 10,000 行数据的文件,每行包含 9 个数据元素 [0,1,2,3,4,5,6,7,8]

我尝试合并文件的第二次交互,因此我导入了 1 个包含 900 万个数据点的文件。

这比我将使用的数据集大得多,但它有助于展示各种导入方法的速度。

Separate files:                 Combined file:

JSON:        34 seconds         34
FUNC-BASED:  17.5               24
ARRAY-BASED: 23                 46

至少可以说是有趣的结果。我在加载每个网页后关闭了浏览器,每次运行测试 4 次,以尽量减少网络流量/变化的影响。 (通过网络运行,使用文件服务器)。您看到的数字是平均值,尽管各个运行的差异最多只有一两秒。

【讨论】:

  • 在您的问题中,您说“它的作用是按顺序处理一组文件,在文件之间休息 1 毫秒。”但实际上,查看您发布的代码,它并没有这样做。 recursiveCall 在再次调用 recursiveCall() 之前需要 1ms 的休息时间,几乎并行加载所有 100 个文件而不是按顺序加载。必须将 setTimeout(function(){$.getScript(string,recursiveCall());},1); 更改为 setTimeout(function(){$.getScript(string,recursiveCall);},1); 才能获得您描述的行为,这可能会在一定程度上改变您的结果?
  • 抱歉,打错了!在我的“真实”代码中,它是 $.getScript(string,recursiveCall);就像你所说。不过很好,我会更新 OP。
【解决方案3】:

考虑使用$.getJSON,而不是使用$.getScript 加载包含函数调用的JavaScript 文件。这可能会提高性能。这些文件现在看起来像这样:

{
    "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/

【讨论】:

  • 谢谢,我们曾考虑使用 JSON 作为我们的数据格式,但由于某种原因,它被搁置了。我将尝试将我的测试应用程序转换为 JSON 格式并报告回来。在最初的测试版本中,我们发现一个文件将浏览器锁定了不可接受的时间,导致用户认为浏览器被锁定。也许使用您概述的传输数据的新方法,这不会有什么大不了的。
  • 是的,我认为最初的锁定是由于加载了一个包含 90,000 个函数调用的文件。
  • 这个带有重复值名称的 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)。这会加快速度吗?
  • 我按照你的建议实现了 JSON 数据下载方法,Day,它平均比 Stephen 的原始实现快 1-3 秒。 Firebug 控制台报告的实际文件传输大约需要 2-3 毫秒。我认为大部分时间都花在了下载后处理这些数据上。
【解决方案4】:

以字符串形式获取所有数据,并使用split()。这是在 Javascript 中构建数组的最快方法。

有一篇非常相似的问题的优秀文章,来自构建 flickr 搜索的人:http://code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多