使用微小单元声明一个巨大的动态数组[C ++]

时间:2010-09-12 17:19:25

标签: c++ arrays memory dynamic-arrays

我有这个项目,我正在努力。以下条件适用

  1. 在这个项目中,我需要创建一个巨大的数组(希望我能够创建一个大到~7.13e + 17,但是这个目标仍然领先。)
  2. 数组中的每个单元格都可以包含三个值中的一个:0,1,2
  3. 我使用C ++作为我的语言。
  4. 我尝试使用普通的动态数组命令

    int * p;
    int i;    
    i=[size]; //This is calculated somewhere else.
    p= new (nothrow) int[i];
    

    但据我所知,这个数组使得一个数组的最大大小可能是int的最大值。如果我更改我的代码并使用以下代码

    long long * p;
    long long i;    
    i=[size]; //This is calculated somewhere else.
    p= new (nothrow) long long [i];
    

    然后,数组中的每个单元格都是“long long”类型,这使得数组的内存很重。 有没有办法用long long来创建一个数组来确定数组中的单元格数,并且每个单元格的大小都是int?

    非常感谢, 乌列尔。

    编辑:了解更多信息。

    1. 这个问题主要是理论上的,它是我硕士论文的一部分。我仍然希望这个程序能够尽可能地发挥作用。
    2. 我目前的步骤是使这个工作用于2.56e + 09项目的数组,快速计算显示我们正在谈论一个至少0.6千兆字节的数组,这是我的系统应该能够应付的。然而,即使所需的空间量确实达到4.5GB,我也无法使用当前的编码解决方案实现这一目标。

7 个答案:

答案 0 :(得分:7)

  

有没有办法用long long创建一个数组来确定数组中的单元格数,并且每个单元格的大小为int?

没有理由数组的类型必须与用于指定大小的变量的类型相同。因此,对于指定大小的变量使用long long,然后使用int作为数组类型。

int * p;
long long i;    
i=[size]; //This is calculated somewhere else.
p= new (nothrow) int [i];

但是,当你说你需要创建一个“大到~7.13e + 17”的阵列时,我很担心。我不知道你的意思是字节或元素,但对于直接数组而言,这两种方式都是非常巨大的。这已经进入了数PB的数据领域。

在32位程序中,这根本不可能。从理论上讲,你可以拥有一个高达几千兆字节的阵列(尽管在实践中大多数情况下都是如此)。

据我所知,在64位程序中,理论上你可以分配一个大的数组。但是,我怀疑大多数机器实际上可以处理它。由于此数据量将远远超过计算机中的RAM,因此操作系统将被迫将此阵列的大部分数据推送到页面文件中。但是,目前大多数典型计算机上的PB级页面文件远远超过了硬盘空间。

无论哪种方式,您可能需要认真考虑一种不同的方案,而不仅仅是一次分配整个大型数组。

答案 1 :(得分:4)

由于您希望最大化包装密度,因此最好使用位字段:

struct item_pack { 
    char a:2;
    char b:2:
    char c:2;
    char d:2;
};

然后你可以创建一个这样的数组并使用代理对象来支持读取和编写单个项目 - 条件是你可以用代理对象做多少限制,所以你必须是一个小心你如何尝试使用它。稍微查看一些关于vector<bool>的文章应该提供一些合理的提示 - 它的大部分特征源于这种一般类型的实现。尽管作为通用容器有缺点,但这可以在限制范围内工作,并且提供比大多数明显替代品更紧密的信息包装。

答案 2 :(得分:2)

  

在这个项目中,我需要创建一个巨大的数组(希望我能够创建一个大到~7.13e + 17,但是这个目标仍然领先。)

调用创建一个专用结构,la digital tree(或b-tree),其中键是索引,以避免进行大量分配。

大量分配,特别是重新分配可能会导致不必要的memory fragmentation。如果将大数组拆分成较小的块,那么不仅数组扩展变得容易,而且可以呈现稀疏数组。

N.B。 ~7.13e+17长约60位。你甚至有可以支持那么多RAM的硬件吗?这不是我跟踪行业的关键,但我简单地听说过NUMA拱门有58位地址总线 - 但没有关于60多位的拱门。

  

数组中的每个单元格都可以包含三个值中的一个:0,1,2.2。

如果单元格可能只包含3个值(2.2可以表示为2),则会使其成为2位信息。这意味着您可以打包到uint32_t 16个值和uint64_t 32个值。

您可以尝试找到一些现有的数字树实现(或自己动手)并将其用作索引的关键高位。原始索引的剩余位将是树叶的索引,该树叶将是具有打包值的数组。举例说明使用std::map代替trie,未经测试:

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个。这也可以得到改善,但访问性能成本要高得多。

答案 3 :(得分:1)

由于所有值都小于255,您可能希望将其设为char数组。 在任何情况下,指针类型都没有规定相同的最大可分配大小。

答案 4 :(得分:1)

由于存在有限的值列表,因此可以仅使用char数组。一个字节可以很容易地保存三个不同的值。

值:
0 - &gt; 0
1 - &gt; 1
2.2 - &gt; 2

存储值:

char values[i];
values[i] = 0;
values[i] = 1;
values[i] = 2;  // really the 2.2 value

检索值:

int zero = values[i] - 0;
int one  = values[i] - 0;
double two_point_two values[i] - 0;
if (two_point_two == 2)
    two_point_tow = 2.2;

需要额外注意以获得最后一个值,但数组将很小(1个字节)。

数组分配:

int main ()
{   
    // static allocation requires a const size
    const int static_array_size = 100;
    char static_array[static_array_size];
    std::cout << "static array size is:" << sizeof(static_array) << std::endl;

    // heap allocation can vary in size (i.e. non const heap_array_size variable)
    int heap_array_size = 200;
    char* heap_array = new char[heap_array_size];
    std::cout << "static array size is:" << sizeof(heap_array_size) << std::endl;
}   

答案 5 :(得分:1)

  

但据我所知,这个数组使得一个数组的最大大小可能是int的最大值。如果我更改我的代码并使用以下代码

这是绝对错误的!数组的大小完全独立于数组类型的最大值。

因此无需将其设为long long数组。相反,你甚至应该把它变成char数组甚至更低。

如果您只需要存储三个不同的值,则应该使用char内的位或任何其他类型的位。然后制作一组这些。

char通常为1个字节,因此为8位。要存储3个值,您需要2位;因此,您可以在char中存储4个值。

使用binary masks,您应该找到一种优化方法。

答案 6 :(得分:1)

用于指定数组大小的大小需要是size_t类型。 new表达式中使用的类型是数组元素的类型。无论您的示例中i的类型如何,它都将转换为size_t以创建数组。

现在在32位机器上,最大size_t大约是4e + 9,所以制作一个大小为1e + 17的数组就是正确的。在64位计算机上,size_t理论上可以达到1e + 19左右,但是你无法在任何接近该数量的内存的情况下,因此分配将失败。

因此,您需要某种稀疏数据结构,正如其他人所讨论的那样。这里的关键是确定最常见的3个值中的哪个值,并且仅存储数组是其他2个值之一的值。您可以使用std :: map来保存这些值(甚至支持使用[index]语法)或其他各种值,具体取决于您要执行的操作以及数据的详细信息。