【问题标题】:perl - array of integers using way too much memory?perl - 使用太多内存的整数数组?
【发布时间】:2011-09-20 09:48:15
【问题描述】:

当我运行以下脚本时:

my @arr = [1..5000000];

for($i=0; $i<5000000; $i++) {
        $arr[$i] = $i;
        if($i % 1000000 == 0) {
                print "$i\n";
        }
}

它消耗大约 500 MB 内存。习惯于更高级别的编译语言,我预计它大约为 5M * 4B = 20MB(每个数字 4 个字节)。

我猜这是因为每个值都是标量,而不是简单的二进制数。是否可以通过将这些值视为数字来减少内存占用,或者是 500 MB 用于此任务的唯一方法?

【问题讨论】:

  • 部分问题在于my @arr = [1..5000000]; 是一个错误,并没有按照您的意愿行事。如果你写了my @arr = (1..5000000);,你可能会看到更少的使用。或者只是my @arr;,因为你没有使用任何你初始化的值。
  • @hobbs - 我做了一个最简单的例子。我的真实脚本确实使用了数组。

标签: perl types


【解决方案1】:

如果您要处理如此大的数组,您可能需要使用像 the PDL 这样的工具包。

(哦,是的,你是对的:它需要大量内存,因为它是一个 Perl 标量数组。)

【讨论】:

    【解决方案2】:

    完整修改我的答案。查看您的代码中的内容,我发现了一些奇怪的东西。

    my @arr = [1..5000000];
    

    在这里,您将匿名数组引用分配给$arr[0]。这个数组只有一个值:数组引用。隐藏的匿名数组包含 500 万个数字。

    for($i=0; $i<5000000; $i++) {
            $arr[$i] = $i;
            if($i % 1000000 == 0) {
                    print "$i\n";
            }
    }
    

    在这里,您用 500 万个序列号填充数组,覆盖声明中的数组引用。

    一个更短的方法是:

    my @arr = (1 .. 5_000_000);
    

    也许这会节省一些记忆。

    【讨论】:

    • [1 .. 5_000_000] 消耗的内存量与它是否为常量一样多。
    【解决方案3】:

    除了创建一个数组之外,您还可以使用 pack 创建一个长度正好为 5000000 * 4 个字符的二进制字符串:

    my $numbers = "";
    $numbers .= pack("N", $_) for (1..5000000);
    

    它绝对应该占用更少的空间。因此,您可以使用substrunpack 获取值。

    【讨论】:

      【解决方案4】:

      所有 Perl 值在内部都表示为 perl 标量,它比简单的 int 消耗更多的内存方式。即使标量仅持有一个 int。即使标量是undef!

      正如其他人所建议的那样,如果您真的想使用这种巨大的数组,PDL 可能值得一看。

      【讨论】:

      【解决方案5】:

      您始终可以在 Perl 中使用 C 或 C++。这可能会让您在一些繁重的工作中占据很小的空间。 只是一个使用 C 的想法!

      #!/usr/bin/perl
      use Inline C;
      use strict;
      
      for(my $i=0; $i<5000000; $i++) {
              set_array_index($i,$i);
              if($i % 1000000 == 0) {
                      #print "$i\n";
                      print get_array_index($i)."\n";
              }
      }
      
      __END__
      __C__
      
      int array[5000000];
      
      void set_array_index(int index,int value) {
          array[index]=value;
      }
      
      int get_array_index(int index) {
      
          if (array[index]==NULL)
              return 0;
      
          return array[index];
      }
      

      【讨论】:

        【解决方案6】:

        也许您可以使用迭代器来代替这么大的整数列表。

        迭代器为每个新值支付函数调用的开销,但可以节省内存。查看 MJD 高阶 Perl 第 4 章 (4.2.1)。

        如果我没记错的话,范围运算符不会'在最新的 perls 中构建如此庞大的列表。

        【讨论】:

          【解决方案7】:

          或者,为你隐式处理包,有Tie::Array::PackedC

          use Tie::Array::PackedC;
          # make @arr use $arr_storage for storing packed elements, by default using 'l!' pack format
          tie my @arr, 'Tie::Array::PackedC', my $arr_storage;
          

          vec 也可能感兴趣。

          【讨论】:

          • 我刚刚阅读了 Tie::Array::PackedC 文档,这似乎是保持低内存使用率的有趣解决方案。
          【解决方案8】:

          以下是互动式pdl2 会话中的几行代码,展示了如何 这可以使用基本的 PDL 结构来完成:

          pdl> $arr = sequence(long, 5000000) + 1;  # create pdl data array (a.k.a. a piddle)
          
          pdl> help vars                            # see, it is only ~19MB
          PDL variables in package main::
          
          Name         Type   Dimension       Flow  State          Mem
          ----------------------------------------------------------------
          $arr           Long D [5000000]            P           19.07MB
          $Pi          Double D []                   P            0.01KB
          
          pdl> p which( $arr%1000000 == 0 )         # which returns indexes which are true
          [999999 1999999 2999999 3999999 4999999]
          

          见在线PDL book 很好地介绍了 PDL 的用途。 PDL mailing lists 是最好的 有关 PDL 使用和开发的信息来源。回复往往是 快速。

          【讨论】:

            猜你喜欢
            • 2012-05-23
            • 1970-01-01
            • 1970-01-01
            • 2014-06-16
            • 1970-01-01
            • 1970-01-01
            • 2012-08-09
            • 2015-06-18
            • 2010-12-02
            相关资源
            最近更新 更多