默认的Javascript对象在很大的时候非常慢



我正在做一个收集单词共同出现的修改版本,所以我编写了自己的javascript,并跟踪三个对象中的出现情况。然而,一旦对象变大(约800万、300万和17.2万(,每10万个句子需要5秒的函数现在需要几分钟才能完成一个包含30个单词(30个标记(的句子。我还远远没有达到我的RAM上限(我还有12 GB的RAM可以使用,而程序只使用2.2GB(。使用Node.js v17.3.1。

为什么当宾语变大时(尽管句子的长度不变(,我的函数会花这么长时间?除了Javascript的默认对象之外,我应该使用另一个对象吗?或者当这些对象太大时,有没有办法提高访问和设置这些对象的速度?

代码:

let posCounts = {};
let negCounts = {};
// the number of times each word occurs
let wordCounts = {};
let tokens = // some function that gets tokens;
for (let k = 0; k < tokens.length; k++) {
// count word occurences
if (tokens[k] in wordCounts) {
wordCounts[tokens[k]] += 1;
} else {
wordCounts[tokens[k]] = 1;
}
for(let tok = k + 1; tok < tokens.length; tok++) {
if (tok == k) {
// avoid word to self cooccurrence
// should no longer be possible
continue;
} else {
// check which form of the cooccurence exists already in either count
actual_tok = (tokens[k] + "-" + tokens[tok]);
if(actual_tok in posCounts || actual_tok in negCounts) {
// no-op
} else {
actual_tok = (tokens[tok] + "-" + tokens[k]);
}
// condition set before this block of code
if(condition) {
if (actual_tok in posCounts) {
posCounts[actual_tok] += 1;
} else {
posCounts[actual_tok] = 1;
}
} else {
if (actual_tok in negCounts) {
negCounts[actual_tok] += 1;
} else {
negCounts[actual_tok] = 1;
}
}
}
}

}

更新:我尝试过通过node train_matrices.js --max-old-space-size=12288node train_matrices.js --max_old_space_size=12288(下划线而不是短划线(来增加堆大小,但也没有成功。

可能不是代码中的主要问题,但您可以通过更改以下结构来减少查找次数:

if (tokens[k] in wordCounts) {
wordCounts[tokens[k]] += 1;
} else {
wordCounts[tokens[k]] = 1;
}

到此:

let token = tokens[k];
let cnt = wordCounts[token] || 0;
wordCounts[token] = cnt + 1;

而且,正如我在一篇评论中所说,我读到,当有很多动态创建的键时,具有.get().set()Map对象更适合,而当有很多具有所有相同键的对象时,普通对象更适合(因为JS编译器有时可以为其制作一个类似C的结构(,但当你定期添加新键时,这是无法做到的。

答案是使用增加内存标志node <YOUR_FILE_NAME>.js --max-old-space-size=12288,并改为使用Map而不是对象-感谢@jfriend00和@Norman Breau的建议。也就是说,地图的最大容量为2^24个项目或1GB,所以我最终使用了这个堆栈流中的BigMap的修改版本(修改后限制了项目的总数,最终RAM完全用完(。

修改后的代码(如果需要,可以用Map替换BigMap(:

let posCounts = new BigMap();
let negCounts = new BigMap();
let wordCounts = new BigMap();
let actual_tok;
tokens = // some code
// mark every cooccurrence
for (let k = 0; k < tokens.length; k++) {
// count word occurences
if (wordCounts.has(tokens[k])) {
wordCounts.set(tokens[k], wordCounts.get(tokens[k]) + 1);
} else {
wordCounts.set(tokens[k], 1);
}
for(let tok = k + 1; tok < tokens.length; tok++) {
if (tok == k) {
// avoid word to self cooccurrence
// should no longer be possible
continue;
} else {
// check which form of the cooccurence exists already in either count
actual_tok = (tokens[k] + "-" + tokens[tok]);
if(posCounts.has(actual_tok) || negCounts.has(actual_tok)) {
// no-op
} else {
actual_tok = (tokens[tok] + "-" + tokens[k]);
}
if(condition) {
if (posCounts.has(actual_tok)) {
posCounts.set(actual_tok, posCounts.get(actual_tok) + 1);
} else {
posCounts.set(actual_tok, 1);
}
} else {
if (negCounts.has(actual_tok)) {
negCounts.set(actual_tok, negCounts.get(actual_tok) + 1);
} else {
negCounts.set(actual_tok, 1);
}
}
}
}

}
}

最新更新