【问题标题】:How do I split array by memory limit?如何按内存限制拆分数组?
【发布时间】:2020-04-02 07:43:03
【问题描述】:

我想将对象数组拆分为内存限制为每个块不应大于 4MB 的块,有什么办法吗?通过下面的代码,我得到了输入数组的大小。

var sizeof = require('object-sizeof')

var arr = [{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"g1ob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"}];

console.log(sizeof(arr))

【问题讨论】:

  • 你的数组中有哪些数据?
  • 为什么需要这样做?在 Node 进程中,对象在内存中的大小是不同的,或者例如JSON 表示。
  • 感谢您的回复。我的数组包含普通对象,每个对象有 100 个字段
  • 实际上我需要将该分块数组传递给有效负载限制为 4MB 的 API

标签: javascript arrays node.js algorithm memory-management


【解决方案1】:

如果您想限制数据,那么您必须定义分块算法以在 配额 内工作。步骤其实很简单:

  1. 创建一个新块。
  2. 在配额用完之前添加项目 - 不要超过它。
  3. 如果达到配额,从1开始。
  4. 如果到达数组的末尾 - 完成。

这是实现的样子。

注意:我相信object-sizeof 库会正确计算对象的大小。我不确定是否真的需要考虑对象引用。所以,假设实现是正确的

注意 2:我找不到该库的 CDN 副本,因此我实现了一个非常愚蠢的替换算法以作为示例。每个键的大小为1,每个值的值为1。对象值是递归计算的。

/*
 * dumb implementation of `sizeof` for example purposes.
 * The "size" is 1 for each key and 1 for each simple value.
 *  {a: "b"} has size = 2
 *  {a: {b: "c"}} has size = 3, etc.
 */
var sizeof = obj => Object.entries(obj)
  .reduce((sum, [key, value]) => 
    sum + 1 + (typeof value !== "object" ? 1 : sizeof(value)),
    0
  )

var arr = [{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"g1ob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"}];

function chunkToLimit(arr, limit) {
  var result = [];
  
  //variables needed for the loop. Initialised properly later
  var chunk;
  var remainingQuota = -Infinity;
  
  for (var i = 0; i < arr.length; i++) {
    var item = arr[i];
    var size = sizeof(item);

    if (size > remainingQuota) {
      //the current chunk that will be filled
      chunk = [];
      //account for the size of the empty chunk itself
      remainingQuota = limit - sizeof(chunk);
      
      //add to result
      result.push(chunk);
    }
    
    remainingQuota -= size
    chunk.push(item);
  }
  
  return result;
}

console.log(chunkToLimit(arr, 10))

这将导致 内存限制,但是如果您需要通过 Internet 发送它,那么您将使用 JSON 序列化,因此您的有效负载可能具有与普通负载完全不同的大小对象会,因为每个项目都将被编码为一个字符串,而且{a: "b", c:"d"} 甚至不会是"a" + "b" + "c" + "d" 的大小,但还将包括{}表示对象,, 分隔属性," 围绕每个键和值。因此,大小看起来与对象的大小不同。

幸运的是,JSON 的大小更容易计算。这是一个字符串,所以你只需要知道它的大小。为此,您可以在 Node.js 中使用 BufferBuffer.byteLength可以用来直接计算计算对象序列化为JSON时的字节大小。

var sizeof = obj =>  Buffer.byteLength(JSON.stringify(obj), 'utf8')

在这种情况下,您将需要与以前不同的步骤:

  1. 如果添加新项目,请检查块是否会超出限制。
  2. 如果是这样,完成当前块并开始一个新块。
  3. 将当前项目添加到块中。
  4. 重复直到阵列用完为止。

这是一个实现:

注意:我使用的是Blob,因为它在浏览器中可用。操作应该是一样的,只是sizeof函数改变了。

var sizeof = obj => new Blob([JSON.stringify(obj)], {type : 'application/json'}).size;

var arr = [{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"g1ob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"}];

function chunkToLimit(arr, limit) {
  //variables needed for the loop
  
  //start first chunk
  var chunk = [];
  //add it to the array
  var result = [chunk];
  var size;
  
  for (var i = 0; i < arr.length; i++) {
    var item = arr[i];
    //concat in order to not modify the chunk and do a check before actually adding
    size = sizeof(chunk.concat(item))

    //check if the limit would be exceeded
    if (size > limit) {
      //if so, start a new chunk
      
      chunk = [];
      result.push(chunk);
    }
    
    //add item to chunk
    chunk.push(item);
  }
  
  return result;
}

var result = chunkToLimit(arr, 60)

for (var chunk of result) {
  var prettyPrint = `${JSON.stringify(chunk)}
  size: ${sizeof(chunk)}`;
  
  console.log(prettyPrint);
}

【讨论】:

    猜你喜欢
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多