我正在开发某种1:1聊天系统,环境是Node.JS
对于每个国家/地区,都有一个国家/地区空间(大厅),对于每个套接字客户端,都将创建一个js类/对象,并且每个对象都在具有其唯一用户ID的列表中。
即使从不同浏览器选项卡等登录的用户,也会保留此唯一ID。
存储在集合中的每个对象,例如:“连接”(全部),“运算符”(仅运算符),“ {countryISO} _clients”(用户)和引用键是它们的唯一ID。
在某些情况下,我需要通过其套接字ID访问这些连接。
至此,我可以想到两个解决方案。
使用for每个循环查找所需的对象
创建另一个集合,这次使用套接字ID(或其他方法)代替唯一ID。
哪一个有意义?因为在JS中,因为此集合将是参考列表而不是副本,所以感觉像是有意义的(而且看起来很漂亮),但是我不确定。哪一个在内存/性能方面昂贵?
由于我不知道如何创建虚拟(同时)套接字连接,因此无法进行全面的测试。
预期的已连接套接字客户端数:300-1000(取决于一天中的时间)
例如用户:
"qk32d2k":{
"uid":"qk32d2k",
"name":"Josh",
"socket":"{socket.io's socket reference}",
"role":"user",
"rooms":["room1"],
"socketids":["sid1"]
"country":"us",
...
info:() => { return gatherSomeData(); },
update:(obj) => { return updateSomeData(obj); },
send:(data)=>{ /*send data to this user*/ }
}
例如国家集合:
{
us:{
"qk32d2k":{"object above."}
"l33t71":{"another user object."}
},
ca:{
"asd231":{"other user object."}
}
}
最佳答案
首先选择针对大多数常见访问进行优化的简单设计
绝对没有理想的答案。这些天,CPU速度很快,因此,如果我是我,那么我将以一种简单的存储套接字的机制开始,即使您使用一种蛮力的搜索方式,也可以通过两种方式访问所需的套接字。选择优化您期望对性能最常见或最敏感的访问机制的数据结构。
因此,如果您要最多按userID查找,那么我可能会将套接字存储在以userID为键的Map
对象中。这将为您提供快速,优化的访问权限,以获取给定用户ID的套接字。
为了通过套接字的其他属性找到套接字,只需逐项迭代Map
,直到在其他套接字属性上找到所需的匹配项为止。我可能会使用for/of
循环,因为当找到匹配项时,它既快速又容易退出循环(某些事情对于使用Map
的Array
或.forEach()
对象无法执行)。您显然可以使自己成为一个实用程序函数或方法,该函数或方法将为您执行蛮力查找,并允许您稍后在不更改太多调用代码的情况下修改实现。
以后测量并添加进一步的优化(如果需要显示数据)
然后,一旦达到规模(或在生产前测试中模拟规模),就可以查看系统的性能。如果您有足够的可用空间,就可以完成了-无需进一步检查。如果您执行的某些操作比期望的CPU使用率慢或比期望的CPU使用率高,则可以对系统进行配置并找出时间。您的性能瓶颈很可能会出现在系统中的其他位置,然后您可以专注于系统的那些方面。如果在分析中发现找到所需套接字的线性查找导致了某些问题,那么您可以使用以socketID为键的第二个并行查找Map来优化该类型的查找。
但是,我不建议您这样做,除非您实际表明这是一个问题。在拥有可以证明值得优化的实际指标之前的过早优化,只会增加程序的复杂性,而无须证明它是必需的,甚至无法解决系统中有意义的瓶颈。我们对瓶颈的直觉通常是遥不可及的。出于这个原因,我倾向于选择一个易于实施,维护和使用的智能先行设计,然后,只有当我们拥有可以测量实际性能指标的实际使用情况数据时,我才会花更多时间对其进行优化或调整或使其更复杂以使其更快。
将实现封装在类中
如果将所有操作都封装在一个类中:
向数据结构添加套接字。
从数据结构中删除套接字。
通过用户ID查找
通过socketID查找
对数据结构的任何其他访问
然后,所有调用代码都将通过该类访问此数据结构,您可以在将来的某个时间调整实现(以基于数据进行优化),而无需修改任何调用代码。如果您怀疑将来会对数据的存储或访问方式进行修改或更改,则这种类型的封装非常有用。
如果您仍然担心,请设计一个快速基准测量
我创建了一个快速代码段,测试在1000个元素Map
对象中的暴力查找有多长时间(当您希望通过除键以外的其他内容查找它)并将其与索引查找进行比较。
在我的计算机上,蛮力查找(非索引查找)每次查找大约需要0.002549毫秒(这是执行1,000,000次查找时的平均时间。为了进行比较,在同一Map
上进行索引查找大约需要0.000017毫秒。因此,您可以节省大约每次查询0.002532毫秒,因此,这是毫秒的分数。
function addCommas(str) {
var parts = (str + "").split("."),
main = parts[0],
len = main.length,
output = "",
i = len - 1;
while(i >= 0) {
output = main.charAt(i) + output;
if ((len - i) % 3 === 0 && i > 0) {
output = "," + output;
}
--i;
}
// put decimal part back
if (parts.length > 1) {
output += "." + parts[1];
}
return output;
}
let m = new Map();
// populate the Map with objects that have a property that
// you have to do a brute force lookup on
function rand(min, max) {
return Math.floor((Math.random() * (max - min)) + min)
}
// keep all randoms here just so we can randomly get one
// to try to find (wouldn't normally do this)
// just for testing purposes
let allRandoms = [];
for (let i = 0; i < 1000; i++) {
let r = rand(1, 1000000);
m.set(i, {id: r});
allRandoms.push(r);
}
// create a set of test lookups
// we do this ahead of time so it's not part of the timed
// section so we're only timing the actual brute force lookup
let numRuns = 1000000;
let lookupTests = [];
for (let i = 0; i < numRuns; i++) {
lookupTests.push(allRandoms[rand(0, allRandoms.length)]);
}
let indexTests = [];
for (let i = 0; i < numRuns; i++) {
indexTests.push(rand(0, allRandoms.length));
}
// function to brute force search the map to find one of the random items
function findObj(targetVal) {
for (let [key, val] of m) {
if (val.id === targetVal) {
return val;
}
}
return null;
}
let startTime = Date.now();
for (let i = 0; i < lookupTests.length; i++) {
// get an id from the allRandoms to search for
let found = findObj(lookupTests[i]);
if (!found) {
console.log("!!didn't find brute force target")
}
}
let delta = Date.now() - startTime;
//console.log(`Total run time for ${addCommas(numRuns)} lookups: ${delta} ms`);
//console.log(`Avg run time per lookup: ${delta/numRuns} ms`);
// Now, see how fast the same number of indexed lookups are
let startTime2 = Date.now();
for (let i = 0; i < indexTests.length; i++) {
let found = m.get(indexTests[i]);
if (!found) {
console.log("!!didn't find indexed target")
}
}
let delta2 = Date.now() - startTime2;
//console.log(`Total run time for ${addCommas(numRuns)} lookups: ${delta2} ms`);
//console.log(`Avg run time per lookup: ${delta2/numRuns} ms`);
let results = `
Total run time for ${addCommas(numRuns)} brute force lookups: ${delta} ms<br>
Avg run time per brute force lookup: ${delta/numRuns} ms<br>
<hr>
Total run time for ${addCommas(numRuns)} indexed lookups: ${delta2} ms<br>
Avg run time per indexed lookup: ${delta2/numRuns} ms<br>
<hr>
Net savings of an indexed lookup is ${(delta - delta2)/numRuns} ms per lookup
`;
document.body.innerHTML = results;
关于javascript - 在JS中有意义:通过foreach在对象集合中搜索值与使用不同的键保留多个集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58720124/