【发布时间】: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