【问题标题】:Speeding up IndexedDB search with Multiple Workers使用多个 Worker 加速 IndexedDB 搜索
【发布时间】:2017-06-06 09:56:49
【问题描述】:

问题:我正在尝试通过使用多个网络工作者来加速我的 IndexedDB 搜索,因此同时执行多个读取事务,但它并没有真正起作用,我的 CPU 只能达到大约 30-35百分比利用率。我有一个 4 核处理器,并希望生成 4 个网络工作者可以显着减少搜索时间。

我正在使用带有 WebExtension 的 Firefox 53;其他浏览器不是一个选项。

数据库:我有一个包含大约 250,000 条记录的数据存储,每条记录大约有 30 个键,其中一些包含文本段落。

任务:对给定键执行字符串搜索以查找匹配值。目前,这需要大约 90 秒才能在单个线程上完成。添加一个额外的工人将时间减少到大约 75 秒。比这更多的工人没有明显的影响。我可以接受的时间应该在 10 秒以下(有点类似于 SQL 数据库)。

当前策略:为每个处理器生成一个工作器,并创建一个在工作器发送消息时解析的 Promise。在每个worker中,打开数据库,将记录平分,然后搜索字符串。如果您是第一个工人,我会从第一个记录开始,第二个记录为第二个,等等。然后按工人的数量前进。所以第一个工人检查记录1、5、9等。第二个工人检查2、6、10等。当然,我也可以让第一个工人检查1-50,第二个工人检查51-100等。(但显然每个都有数千个)。

在单个线程上使用getAll() 几乎花费了两倍的时间和 4GB 内存。将其拆分为 4 个范围可将合并结果后的总时间减少到大约 40 秒(每次运行脚本时 40 秒变化很大)。

关于如何完成这项工作的任何想法,或其他显着加快搜索速度的建议?

background.js:

var key = whatever, val = something
var proc = navigator.hardwareConcurrency;  // Number of processors
var wPromise = [];  // Array of promises (one for each worker)
var workers = [];

/* Create a worker for each processor */
for (var pos = 0; pos < proc; pos++) {
    workers[pos] = new Worker("js/dbQuery.js");
    wPromise.push(
        new Promise( resolve => workers[pos].onmessage = resolve )
    );
    workers[pos].postMessage({key:key, val:val, pos:pos, proc:proc});
}

return Promise.all(wPromise);  // Do something once all the workers have finished

dbQuery.js:

onmessage = e => {
    var data = e.data;
    var req = indexedDB.open("Blah", 1);
    req.onsuccess = e => {
        var keyArr = [];
        var db = e.currentTarget.result;
        db.transaction("Blah").objectStore("Blah").index(data.key).openKeyCursor().onsuccess = e => {
            var cursor = e.target.result;
            if (cursor) {
                if (data.pos) {
                    cursor.advance(data.pos); // Start searching at a position based on which web worker
                    data.pos = false;
                }
                else {
                    if (cursor.key.includes(data.val)) {
                        keyArr.push(cursor.primaryKey);  // Store key if value is a match
                    }
                    cursor.advance(data.proc); // Advance position based on number of processors
                }
            }
            else {
                db.close();
                postMessage(keyArr);
                close();
            }
        }
    }
}

【问题讨论】:

  • 注意,没有尝试过IndexedDB,虽然很好奇为什么需要Promise.all()
  • 我使用Promise.all,因为我为每个工人创建了一个承诺并将它们存储在一个数组中。我只想在所有工作人员都返回他们的结果后使用这些数据。这在技术上没有必要,或者与潜在的速度问题真正相关。
  • 如果找到任何匹配项,您也可以使用 Promise.race() 返回已解析的 Promise,而不是等待所有 Promises 被解析,请参阅 How to determine which canvas object completed its random speed animation first
  • "...让第一个工作人员检查 1-50,第二个工作人员检查 51-100..." - 您是否为这种方法计时?我希望它会稍微高效一些,因为各个游标正在处理顺序数据。
  • 我不熟悉 IDB 的 FF 实现,但很可能单个工作线程正在向单个数据库线程/进程提交请求,该线程/进程正在为所有事务执行单个数据库操作。工作人员给你买的是分配“开销”——生成事件、执行事件循环、将记录反序列化为 JS 值、执行过滤逻辑等。这与你观察到的行为相匹配——你已经减少了开销,但是基础运营成本触底反弹。

标签: javascript multithreading performance indexeddb web-worker


【解决方案1】:

关于如何完成这项工作的任何想法,或其他建议 显着加快搜索速度?

一旦找到匹配项,您可以使用Promise.race() 替换Promise.all() 以返回已解析的Promise,而不是等待传递给Promise.all() 的所有Promises 被解析。

【讨论】:

  • 我不确定这如何使搜索运行得更快。主要问题是获得最终匹配需要多长时间,这是我必须检查给定键的值数量的函数。
  • @DanielH 请参阅“或其他显着加快搜索速度的建议”尝试Promise.race() 并注意基准差异(如果有)。目前,问题的javascript 似乎正在等待传递给Promise.all() 的所有Promise 对象的实现,而不是在任何Promise 值已解析时调用.then()。在该调整之外,如果搜索算法当前正在执行必要的任务,如果您得出结论认为创建更多 WebWorker 实例不会减少总过程时间,那么不确定如何改进搜索?
猜你喜欢
  • 2013-05-06
  • 1970-01-01
  • 1970-01-01
  • 2012-12-08
  • 1970-01-01
  • 2011-10-28
  • 2018-12-19
  • 1970-01-01
  • 2023-02-03
相关资源
最近更新 更多