Perl - массив целых чисел, использующих слишком много памяти? - PullRequest
7 голосов
/ 22 июня 2011

Когда я запускаю следующий скрипт:

my @arr = [1..5000000];

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

Потребляет около 500 МБ памяти. Используемый для высокоуровневых скомпилированных языков, я ожидал, что он будет примерно 5M * 4B = 20MB (4 байта на число).

Полагаю, это потому, что каждое значение является скалярным, а не простым двоичным числом. Можно ли уменьшить объем памяти, обрабатывая эти значения как числа, или 500 МБ для этой задачи - единственный способ?

Ответы [ 8 ]

4 голосов
/ 22 июня 2011

Если вы имеете дело с такими большими массивами, вы можете использовать инструментарий, такой как PDL .

(О, да, вы правы: это занимает столько памяти, потому что это массив скаляров Perl.)

3 голосов
/ 22 июня 2011

Вы всегда можете использовать C или C ++ в Perl. Это, вероятно, даст вам небольшой след в некоторых тяжелых работах. Просто идея с использованием 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];
}
3 голосов
/ 22 июня 2011

Все значения Perl внутренне представлены как скаляры perl, которые потребляют на больше памяти, чем простое int. Даже если скаляр содержит только int. Даже если скаляр undef!

Как и предполагали другие, PDL может быть чем-то, на что стоит обратить внимание, если вы действительно хотите работать с огромными массивами такого типа.

2 голосов
/ 26 апреля 2012

Вот несколько строк из интерактивного 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 для хорошее введение в то, для чего может использоваться PDL. Списки рассылки PDL являются лучшими источник информации об использовании и развитии PDL. Ответы часто быстрое.

2 голосов
/ 22 июня 2011

Полный пересмотр моего ответа. Глядя на то, что у вас есть в коде, я вижу некоторые странные вещи.

my @arr = [1..5000000];

Здесь вы присваиваете анонимную ссылку на массив $arr[0]. Этот массив содержит только одно значение: ссылка на массив. Скрытый анонимный массив содержит 5 миллионов номеров.

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

Здесь вы заполняете массив 5 миллионами последовательных чисел, перезаписывая ссылку на массив в объявлении.

Гораздо более короткий способ сделать это:

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

Возможно, это сэкономит вам память.

1 голос
/ 22 июня 2011

Возможно, вы могли бы использовать итератор вместо такого большого списка целых чисел.

Итератор оплачивает накладные расходы при вызове функции для каждого нового значения, но экономит память. Проверьте MJD высшего порядка Perl, глава 4 ( 4.2.1 ).

Если я правильно помню, оператор диапазона не создает такой огромный список в последних perls.

1 голос
/ 22 июня 2011

Вместо создания массива вы можете создать двоичную строку длиной ровно 5000000 * 4 символов, используя пакет:

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

Это определенно должно занимать меньше места. Следовательно, вы можете получить значения с помощью substr и unpack.

0 голосов
/ 22 июня 2011

Или, косвенно обрабатывая пакет для вас, есть 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 также может представлять интерес.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...