【问题标题】:Default Javascript objects are very slow when large?默认 Javascript 对象在大时非常慢?
【发布时间】:2022-02-13 15:43:03
【问题描述】:

我正在做一个收集词共现的修改版本,所以我编写了自己的 javascript,我正在跟踪三个对象中的出现。然而,一旦对象变大(约 800 万、300 万和 172000 个),一个每 100000 个句子需要 5 秒的函数现在需要几分钟才能完成一个包含 30 个单词(30 个标记)的句子。我离我的 RAM 上限还差得很远(我还有 12 GB 的 RAM 可以使用,而程序只使用 2.2 GB)。使用 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(下划线而不是破折号)增加堆大小,但这也不起作用。

【问题讨论】:

  • and the program is only using 2.2GB). 您正在接近默认的最大堆大小,因此您可能会遇到很多 GC 命中。您可以尝试使用节点标志--max_old_space_size=4096 启动您的节点程序,这将扩展 RAM 节点愿意使用的范围。他们不建议超过 2GB(即使我的一些生产服务器有后台节点进程使用 >= 6GB 有时没有太大问题)
  • 也许Map 对象会更合适。我读过它比普通对象更好地处理动态插入的键。

标签: javascript node.js


【解决方案1】:

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

  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 的结构),但是当您定期添加新键时,这是无法做到的。

【讨论】:

    【解决方案2】:

    答案是同时使用增加内存标志node &lt;YOUR_FILE_NAME&gt;.js --max-old-space-size=12288 并改为使用Map 而不是对象 - 感谢@jfriend00 和@Norman Breau 的建议。也就是说,地图的最大容量为 2^24 个项目或 1 GB,所以我最终使用了来自 this stackoverflow 的 BigMap 的修改版本(修改以限制项目的总数 - 最终完全耗尽了 RAM )。

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

      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);
                 }
               }
             }
          }
    
    
        }
      }
    

    【讨论】:

      猜你喜欢
      • 2022-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-29
      相关资源
      最近更新 更多