在这个项目中,我需要创建一个巨大的数组(希望我能够创建一个与 ~7.13e+17 一样大的数组,但这个目标仍然遥遥领先。)
这需要创建一个专用结构,即以键为索引的 la数字树(或b-tree),以避免进行大量分配。
大量分配,尤其是重新分配可能会导致不必要的内存碎片。如果将大数组拆分成更小的块,那么不仅数组扩展变得容易,而且稀疏数组的表示也成为可能。
NB~7.13e+17
大约 60 位长。您甚至有可以支持这么多 RAM 的硬件吗?这并不是说我密切关注行业,而是我简要地听说过具有 58 位地址总线的 NUMA 拱门——但对 60 位以上的拱门一无所知。
数组中的每个单元格都可以包含以下三个值之一:0、1、2.2。
如果单元格可能仅包含 3 个值(2.2 可以表示为 2),则使其成为 2 位信息。这意味着您可以打包到uint32_t
16 个值和uint64_t
32 个值中。
您可以尝试找到一些现有的数字树实现(或自己滚动)并用作索引的关键高位。原始索引的剩余位将是到树叶的索引,这将是一个包含压缩值的数组。举例说明使用std::map
代替特里,未经测试:
enum {
LS_BITS = 16,
MS_BITS = 64-LS_BITS
};
enum {
VALUE_BITS = 2,
VALUE_MASK = ((1<<VALUE_BITS)-1)
};
// this represents an array of `1<<LS_BITS` values
struct leaf_node {
uint64_t packed_data[ ((1<<LS_BITS)*VALUE_BITS) / (sizeof(uint64_t)*8) ];
};
// that should be a trie, to provide faster look-up
typedef std::map< uint64_t, leaf_node > big_array_type;
void
big_array_set_value( big_array_type &b, uint64_t index, uint64_t value )
{
leaf_node &n = b[index >> LS_BITS];
uint64_t li = index & ((1<<LS_BITS)-1);
li *= VALUE_BITS; // convert into bit offset
uint64_t &x = n.packed_data[ li / (sizeof(uint64_t)*8) ];
li %= (sizeof(uint64_t)*8);
x = (x & (VALUE_MASK<<li)) | (value << li);
}
int
big_array_get_value( big_array_type &b, uint64_t index, uint64_t value )
{
leaf_node &n = b[index >> LS_BITS];
uint64_t li = index & ((1<<LS_BITS)-1);
li *= VALUE_BITS; // convert into bit offset
uint64_t &x = n.packed_data[ li / (sizeof(uint64_t)*8) ];
li %= (sizeof(uint64_t)*8);
return (x >> li) & VALUE_MASK;
}
这种方式仍然浪费 0.5 位信息,因为存储是 2 位,允许 4 个值,但只使用 3 个。这也可以改进,但访问性能成本要高得多。