如何在 JavaScript 中有效地将预定义大小的大块分割成较小的块,这些块是其大小的因素?

2024-01-30

假设我们有这样的结构:

// 16 bins
let BIN_OF_BINS = [
  [], // 128 bits each chunk
  [], // 256
  [], // 512
  [], // 1024
  [], // 2048
  [], // 4096
  [], // 8192
  [], // 16384
  [], // 32768
  [], // 65536
  [], // 131072
  [], // 262144
  [], // 524288
  [], // 1048576
  [], // 2097152
  [{ start: 0, count: 100 }], // 4194304
]

中的每个垃圾箱BIN_OF_BINS保存一组代表内存中插槽的节点。 n+1 bin 包含的节点大小是 n bin 大小的两倍。因此,第一个 bin 保存 128 位值,下一个保存 256 位值,接下来的 512 位值,等等。 bin 中包含的值可以是连续的,因此我们可能在“256 位值 bin”中拥有 1024 位的连续块,因此可以表示为:

bin2 = [{ count: 4, addressStartsAt: 0 }]

如果它有两个不连续的 1024 块,它将是:

bin2 = [
  { count: 4, addressStartsAt: 0 },
  { count: 4, addressStartsAt: 4096 /* let's say */ }
]

原则上,您可以在使用和释放内存时添加和删除这些垃圾箱。但对于这个问题,我们只关心使用已释放的内存(即我们不关心这个问题的释放内存)。

所以当BIN_OF_BINS开始时,只有最上面的 bin 有 100 个值。所以我们从这个开始:

// 16 bins
let BIN_OF_BINS = [
  [], // 128 bits each chunk
  [], // 256
  [], // 512
  [], // 1024
  [], // 2048
  [], // 4096
  [], // 8192
  [], // 16384
  [], // 32768
  [], // 65536
  [], // 131072
  [], // 262144
  [], // 524288
  [], // 1048576
  [], // 2097152
  [{ start: 0, count: 100 }], // 4194304
]

现在,当我们去获取 256 位值时,我们发现没有,因此它会遍历列表到更大的 bin,并将其分成两半(或者执行其他操作,我将对此进行介绍)。因此,如果我们要求全新的 1 256 值BIN_OF_BINS,我们向上、向上、向上,直到到达顶峰才发现任何东西。然后我们迭代划分。从 4194304 开始,它是这样的(在我们已经迭代了空白到达顶部之后):

// step 0
[{ start: 0, count: 100 }], // 4194304, bin 16

// step 1
[{ start: 4194304, count: 99 }], // 4194304, bin 16
[{ start: 0, count: 2 }], // 2097152, bin 15

// step 2
[{ start: 4194304, count: 99 }], // 4194304, bin 16
[{ start: 2097152, count: 1 }], // 2097152, bin 15
[{ start: 0, count: 2 }], // 1048576, bin 14

// step 3
[{ start: 4194304, count: 99 }], // 4194304, bin 16
[{ start: 2097152, count: 1 }], // 2097152, bin 15
[{ start: 1048576, count: 1 }], // 1048576, bin 14
[{ start: 0, count: 2 }] // 524288, bin 13

// etc.

我们继续这样划分,直到最终得到:

[{ start: 0, count: 2 }] // 256, bin 2

现在我们可以通过简单地执行以下操作从“bin 2”获取“256 位内存插槽”:

node.start += 256
node.count--

我们最终得到:

[{ start: 256, count: 1 }] // 256, bin 2

问题是,如何才能有效实施呢?这让我感到非常困惑和难以理解。

  • 当获取某个大小(仅是前 4 个 bin 之一)时,如果不存在,请尝试从父级获取并分成两半。
  • 如果父级没有,则细分其父级。
  • etc.

基本上就是这样。这是我到目前为止所拥有的。我想在没有递归的情况下执行此操作(仅使用带有循环的迭代方法),因为它将在没有递归的地方使用。

// 16 bins
let BINS = [
  [], // 4 32-bit values, so 128 bits each chunk
  [], // 8 32-bit values, so 256
  [], // 16 32-bit values, so 512
  [], // 32 32-bit values, so 1024
  [], // 2048
  [], // 4096
  [], // 8192
  [], // 16384
  [], // 32768
  [], // 65536
  [], // 131072
  [], // 262144
  [], // 524288
  [], // 1048576
  [], // 2097152
  [{ start: 0, count: 100 }], // 4194304
]

function fetchBlockWithAllocation(i) {
  let block = fetchBlock(i)
  if (!block) prepareBlocks(i)
  return fetchBlock(i)
}

function fetchBlock(i) {
  if (!BINS[i].length) {
    return -1
  }

  let bin = BINS[i]
  let node = bin[0]
  let address = node.start
  node.count--
  node.start += i * 32
  if (!node.count) {
    bin.shift()
  }
  return address
}

function prepareBlocks(index, howMany = 1024) {
  let startBinIndex = index + 1
  let scaleFactor = 1
  while (startBinIndex < 16) {
    let bin = BINS[startBinIndex++]
    if (bin.length) {
      for (let k = 0, n = bin.length; k < n; k++) {
        let node = bin[k]
        while (node.count) {
          howMany -= scaleFactor
          node.count--
        }
      }
      // starting to get lost
    } else {

    }
  }
}

堆栈/迭代方面让我陷入困境。似乎有一些简单的东西我缺少创建一个优雅的解决方案,而且我正在偏离轨道。我有prepareBlocks to 预分配一堆块,所以当它找不到任何块时,它就会批量执行,作为一种优化。理想情况下,它无需创建任何其他临时数组即可完成此操作。

我不断地想:

  • 降低下一个级别。
  • 我们还剩多少人?
  • 降低下一个级别。
  • 我们还剩多少人?

以更递归的方式尝试,我仍然陷入同一点:

let BINS = [
  { count: 0, array: [] }, // 4 32-bit values, so 128 bits each chunk
  { count: 0, array: [] }, // 8 32-bit values, so 256
  { count: 0, array: [] }, // 16 32-bit values, so 512
  { count: 0, array: [] }, // 32 32-bit values, so 1024
  { count: 0, array: [] }, // 2048
  { count: 0, array: [] }, // 4096
  { count: 0, array: [] }, // 8192
  { count: 0, array: [] }, // 16384
  { count: 0, array: [] }, // 32768
  { count: 0, array: [] }, // 65536
  { count: 0, array: [] }, // 131072
  { count: 0, array: [] }, // 262144
  { count: 0, array: [] }, // 524288
  { count: 0, array: [] }, // 1048576
  { count: 0, array: [] }, // 2097152
  { count: 0, array: [ { start: 0, count: 100 }] }, // 4194304
]

function prepareBlocks(index, minHowMany = 1024) {
  let bin = BINS[index]
  if (bin.count === 0) {
    return prepareBlocks(index + 1, Math.ceil(minHowMany / 2))
  } else {
    let diff = Math.max(0, bin.count - minHowMany)
    if (diff <= 0) {
      return prepareBlocks(index + 1, Math.ceil(minHowMany / 2))
    } else {
      for (let k = 0, n = bin.length; k < n; k++) {
        let node = bin[k]
        if (node.count >= minHowMany) {
          node.count -= minHowMany
        } else {
          // getting lost at same point
        }
      }
    }
  }
}

几乎就好像它必须曲折地遍历每个列表中的第一个项目,然后是第二个项目,依此类推,因此它只划分它需要的内容。

最新的伪代码是:

function allocateBunch(base, size, count) {
  let desiredBits = size * count
  let totalBits = 0
  for bin, i in bins
    let blockBits = 128 << i
    while (bin.length)
      block = bin[0]
      let k = 0
      let totalNewBits = block.count * blockBits
      let totalWithNewBits = totalBits + totalNewBits
      let diff = Math.floor(totalNewBits - desiredBits / blockBits)
      block.count -= diff
      let newChildBlock = { count: diff * (2 ** i) }
      base.push(newChildBlock)
      totalWithNewBits >= desiredBits
        return
      bin.shift()
}

注意:在寻找时预分配多少并不重要,我会说最大 4096 或其他东西,只是因为看起来足够合理。因此,在尝试获取块时,只需从最近的位置开始一直向下划分,这样您就可以获得所需大小的更多块。如果还不够,那么只需重复该过程即可。只是还不知道该怎么做。

另请注意,如果您考虑如何在该系统中“释放内存”,则可以在与奇数配对时合并每个节点,并将它们合并起来,这样您就会得到越来越大的块。也许这会影响你如何实现这一点,只是想指出。

还要寻求最大效率,我的意思是如果可能的话使用缓存或非幼稚的解决方案(例如重复进行计算或做不必要的工作)。

赏金将进入最优化的版本(在执行步骤、无递归等方面),这也很简单。


function allocate(bits) {
    if ((bits & (bits - 1)) != 0) {
        throw "Parameter is not a power of 2";
    }

    if (bits < 128 || bits > 4194304) {
        throw "Bits required out of range";
    }
    
    var startBinIndex = Math.log2(bits >> 7);
    var lastBin = BIN_OF_BINS.length - 1;

    
    for (var binIndex = 0; binIndex <= lastBin ; binIndex++) {
        var bin = BIN_OF_BINS[binIndex];

        //
        // We have found a bin that is not empty...
        //
        if (bin.length != 0) {
            //
            // Calculate amount of memory this bin takes up
            //
            var thisBinMemorySize = (128 << binIndex);
            var enoughMemory = thisBinMemorySize >= bits;


            if (!enoughMemory) {
                //
                // This bin is too small, but it may have continuous blocks, so lets find a continuous block big enough to accomodate the size we want...
                //
                for (var b = 0; b < bin.length; b++) {
                    var blockOfInterest = bin[b];
                    var blockSize = blockOfInterest.count * thisBinMemorySize;
                    //
                    // We've found a continous block in the lower size bin that fits the amount we want
                    //
                    if (blockSize >= bits) {
                        //
                        // We are going to return this block
                        //
                        var allocatedMemoryBlock = {start : blockOfInterest.start, count : 1};
                        //
                        // Perfect size, we are simply going to delete the whole block
                        //
                        if (blockSize == bits) {
                            bin.splice(b);
                        }
                        else {
                            //
                            // Otherwise we'll take what we need and adjust the count and adjust the start address
                            //
                            blockOfInterest.start += bits;
                            blockOfInterest.count -= bits / thisBinMemorySize; // because we are working in power of 2 we'll never get remainder
                        }

                        return allocatedMemoryBlock;
                    }
                }
                //
                // Failed to find a block big enough so keep searching
                //
            }
            else {
                //
                // This big enough even with just 1 block...
                //
                console.log(thisBinMemorySize);

                //
                // We are going to return this block
                //
                var lastBinOfBinsIndex = bin.length - 1;
                var binBlock = bin[lastBinOfBinsIndex];
                var memoryAddress = binBlock.start;


                //
                // We are going to return this block
                //
                var allocatedMemoryBlock = {start : memoryAddress, count : 1};

                //
                // Before we return the above block, we need to remove the block if count is 1 otherwise decrease count and adjust memory start pointer by bin size
                //
                if (binBlock.count == 1) {
                    bin.pop();
                }
                else {
                    binBlock.count--;
                    binBlock.start += thisBinMemorySize;
                }
                
                //
                // if we want 1024 bits and it takes it from bin 15, we simply subtract 1024 from 4194304 which gives us 4193280 
                // if we then populate bin 3 (1024 bits) onward, until bin 14, the exact number we end up populating those bins with is 4183280
                //
                var remainingUnsedMemory = thisBinMemorySize - bits;
                var adjustmentSize = bits;
                while (remainingUnsedMemory != 0) {
                    memoryAddress += adjustmentSize;

                    BIN_OF_BINS[startBinIndex].push({start : memoryAddress, count : 1});
                    startBinIndex++;
                    remainingUnsedMemory -= bits;
                    adjustmentSize = bits;
                    bits <<= 1;
                }

                return allocatedMemoryBlock;
            }
        }
    }
    return null; // out of memory...
}

console.log("Memory returned:", allocate((128 << 1)));
for (i = 0; i < BIN_OF_BINS.length; i++) {
    console.log(JSON.stringify(BIN_OF_BINS[i]));
}

分配 4096 x 128 块

//
// Allocate 524288 bytes...
//
var memorySize = 128 << 12; 
var memoryAllocated = allocate(memorySize); 

// Adjust the count to 524288 / 128 to give 4096 blocks of 128
memoryAllocated.count = (memorySize / 128);

// Put the allocated memory back on the BIN_OF_BINS stack
BIN_OF_BINS[0].push(memoryAllocated);


for (i = 0; i < BIN_OF_BINS.length; i++) {
    console.log(JSON.stringify(BIN_OF_BINS[i]));
}

ADDED

下面的版本与第一个版本非常相似,只是它不经过较小的垃圾箱。

function allocate(bits) {
    if ((bits & (bits - 1)) != 0) {
        throw "Parameter is not a power of 2";
    }

    if (bits < 128 || bits > 4194304) {
        throw "Bits required out of range";
    }
    
    var startBinIndex = Math.log2(bits >> 7);
    var lastBin = BIN_OF_BINS.length - 1;

    
    for (var binIndex = startBinIndex; binIndex <= lastBin ; binIndex++) {
        var bin = BIN_OF_BINS[binIndex];

        //
        // We have found a bin that is not empty...
        //
        if (bin.length != 0) {
            //
            // Calculate amount of memory this bin takes up
            //
            var thisBinMemorySize = (128 << binIndex);
            var lastBinOfBinsIndex = bin.length - 1;
            var binBlock = bin[lastBinOfBinsIndex];
            var memoryAddress = binBlock.start;


            //
            // We are going to return this block
            //
            var allocatedMemoryBlock = {start : memoryAddress, count : 1};

            //
            // Before we return the above block, we need to remove the block if count is 1 otherwise decrease count and adjust memory start pointer by bin size
            //
            if (binBlock.count == 1) {
                bin.pop();
            }
            else {
                binBlock.count--;
                binBlock.start += thisBinMemorySize;
            }
            
            //
            // if we want 1024 bits and it takes it from bin 15, we simply subtract 1024 from 4194304 which gives us 4193280 
            // if we then populate bin 3 (1024 bits) onward, until bin 14, the exact number we end up populating those bins with is 4183280
            //
            var remainingUnsedMemory = thisBinMemorySize - bits;
            var adjustmentSize = bits;
            while (remainingUnsedMemory != 0) {
                memoryAddress += adjustmentSize;

                BIN_OF_BINS[startBinIndex].push({start : memoryAddress, count : 1});
                startBinIndex++;
                remainingUnsedMemory -= bits;
                adjustmentSize = bits;
                bits <<= 1;
            }

            return allocatedMemoryBlock;
        }
    }
    return null; // out of memory...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 JavaScript 中有效地将预定义大小的大块分割成较小的块,这些块是其大小的因素? 的相关文章

随机推荐