Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Быстро прочитать и удерживать в памяти 180Мб файл


Автор: yorc 26.12.2009, 23:19
всем привет!

взялся тут за text mining, приходится работать с большими объёмами текстовой инфы... а потом выяснилась, что лучшая на сегодня библиотека для извлечения информации из текстов написана на Perl, с которым у меня было ну уж очень короткое знакомство... поэтому обращаюсь к уважаемым гуру, дабы заинтересовать их проблемой и попросить помощи!

итак. есть текстовый файл ~180 Мб
в нём 7.500.000 строк, каждая из них в таком формате:

Код

2    6    THAILAND NATIONAL NANOTECHNOLOGY CENTER
8    14    DHIFFUSHI
16    28    DHIFFUSHI (KAAFU ATOLL)
93    508    CATHOLIC APOSTOLIC CHURCH
1    94    APOSTOLIC CHURCHES
93    508    IRVINGISM
93    508    IRVINGITE
93    508    IRVINGITES


задача стоит такая: нужно, прежде всего, считать все эти записи как можно быстрее. потом удерживать их в памяти, чтобы не приходилось заново считывать все эти мегабайты с диска при каждом запуске интерпретатора. и, наконец, организовать данные в памяти так, чтобы можно было как можно быстрее осуществить поиск в них (по сути, это будет ассоциативный массив, где ключами будут термины, а значениями - цифры).

мне просто кажется, что стандартный код типа

Код

$FILE1 = "t.txt";
open FILE1;
my @arr = <FILE1>;


не есть самое эффективное решение в данном случае...

заранее признателен за любую помощь/идеи/советы/предложения!


Автор: sir_nuf_nuf 27.12.2009, 00:29
Цитата(yorc @  26.12.2009,  23:19 Найти цитируемый пост)
организовать данные в памяти так, чтобы можно было как можно быстрее осуществить поиск в них

поиск по _чему_ ?

Если по номеру строки - оптимально хранить  в массиве.
Если полнотекстовый - нужно строить инверсный индекс.

P.S.
А еще совет - попробуйте для начала использовать обычный grep (unix) -  180 МБ хорошо ложаться в кеш операционки, поэтому это может быть довольно таки быстрый поиск.

Автор: yorc 27.12.2009, 00:56
поиск самый примитивный: если среди записей из файла ЕСТЬ слово из текста, то нужно вернуть две цифры, которые соответствуют этому слову в файле. я бы не сказал, что это полнотекстовый поиск, т.к. разделять слова не нужно. будет ли в этом случае оптимальным решением обычная операция проверки существования элемента массива с заданным ключом и извлечение данных при положительной проверке?

насчёт grep не понял?..  вы предлагаете делать системный вызов? и как это связано с кешем системы?

Автор: sir_nuf_nuf 27.12.2009, 11:50
если вы 180 метров засунете в perl - хэш - это будут все 300. которые будут висеть у вас в памяти. 
Доступ по ключу в хэше конечно быстрый.. вот только вопрос - вам точно нужна такая скорость ?

если да.. то

Код

open my $file, "< file.txt" or die "nafig";
my %index = ();
while (<$file>) {
    my ($x, $y, @words) = split /\s+/, $_;
    foreach (@words) {
        $index{$_} = [$x, $y];
    }
}


P.S. как вы планируете использовать этот файл ? опишите полностью задачу, плиз

Автор: amg 27.12.2009, 12:57
Цитата(sir_nuf_nuf @  27.12.2009,  11:50 Найти цитируемый пост)
если вы 180 метров засунете в perl - хэш - это будут все 300. которые будут висеть у вас в памяти. 
Больше (гораздо) smile

yorc, совет sir_nuf_nuf про grep, IMHO, хорош. В самом деле, попробуйте, если у Вас *nix. После первого поиска файл закешируется (будет в памяти) и при следующих поисках система фактически уже не будет читать его с диска, возьмет из памяти, а 180 М grep прошерстит быстро.

Вот еще, нашел у себя скриптик и переделал его немного под Вашу задачу. На диске будет храниться БД, надеюсь, поиск в ней будет быстрым.
Код
#!/usr/bin/perl -ws

our ($create, $DB);
use DB_File;

die "
Usage: $0 [-DB=DB_File.db] -create t.txt
       $0 [-DB=DB_File.db] word1 word2 ...
" unless @ARGV || $create; 

my $db = $DB || 'DB_File.db';

die "No $db. Try\n$0 -create t.txt\n" if ! $create && ! -f $db;
unlink $db if $create;

tie %hash, "DB_File", $db or die "Can't open $db: $!\n";

if ($create) {
  while (<>) {
    if (s/^\s*(\d+\s+\d+)\s+//) {
      my $two_numbers = $1;
      while (m/\b(\w+)\b/g) {
        print "\r$.";
        $hash{$1} .= "$two_numbers|";
      }
    }
  }
  print "\nCreation $db done\n";
}
else {
  while (@ARGV) {
    my $key = shift;
    my $numbers = $hash{$key} || "";
    $numbers =~ s/\|/\n/g;
    print "$key\n$numbers\n";
  }
}
untie %hash;


Автор: yorc 27.12.2009, 12:58
sir_nuf_nuf : спасибо за помощь!

запустил код на VDS с 800 MHz ядром + 1 Гб памяти
загрузка занимает в среднем 2,5 минуты

после окончания загрузки поиск моментальный! здорово))

заметил только 3 момента, когда тестировал:

1. print $index{"DHIFFUSHI"}[1]."\n"; выдало 28 вместо 14. как видим:

Код

8    14    DHIFFUSHI
16    28    DHIFFUSHI (KAAFU ATOLL)


2. print $index{"CATHOLIC APOSTOLIC CHURCH"}[0]."\n"; выдало

Код

Use of uninitialized value in concatenation (.) or string at db.pl line 16, <$file> line 7482719.


В строке 16 при этом как раз:

Код

print $index{"CATHOLIC APOSTOLIC CHURCH"}[0]."\n";


3. print scalar (keys %index); выдаёт каждый раз разные результаты, далёкие от истины: 1930032, 1930033 и т.д.
это может быть связано с ограничением памяти на 1 процесс, я думаю...

вывод: чем заморачиваться с хэшами, лучше залью всё в БД!
была ещё мысль использовать биндинг memcached для перла
но это только если тесты на БД будут неудовлетворительными

задача же моя крайне критична по времени и ресурсам: обрабатываю текст, извлекаю слова, считаю определённые параметры исходя из данных таблицы. при этом всё делается на лету, планируется онлайн-сервис. скорость должна быть максимальной!

и тогда такой вопрос: что, по Вашему, будет быстрее, MySQL или, может, BerkeleyDB ? или PostgreSQL ?

Автор: Vaneska 28.12.2009, 19:28
Цитата(yorc @  27.12.2009,  12:58 Найти цитируемый пост)
и тогда такой вопрос: что, по Вашему, будет быстрее, MySQL или, может, BerkeleyDB ? или PostgreSQL ? 

Быстрее всего будет BerkleyDB / memcachedb
Но если нужна нормальная сортировка, выборка, то лучще юзать какую-нибудь реляционную бд. ( mysql или postgresql )

Если нужно очень быстро, то использовать для этого списка BerkleyDB/memcached(b), а для хранения юзеров и т.д. реляционную.

Добавлено через 2 минуты и 46 секунд
Цитата(yorc @  27.12.2009,  12:58 Найти цитируемый пост)
заметил только 3 момента, когда тестировал:

Фразы эти лучше хешировать ( md5, sha1 ), тогда проблем не будет



Автор: sir_nuf_nuf 28.12.2009, 19:44
Цитата(Vaneska @  28.12.2009,  19:28 Найти цитируемый пост)
Быстрее всего будет BerkleyDB / memcachedb
Но если нужна нормальная сортировка, выборка, то лучще юзать какую-нибудь реляционную бд. ( mysql или postgresql )

сдается мне - быстрее всего будет может быть grep. А если и не быстре - то проще всего.

Автор: Bulat 29.12.2009, 11:04
yorc, я бы еще посоветовал почитать как ведут себя такие функции как substring, index.. регулярные выражения с текстовыми строками большого объема.. Ибо в перле есть не самое хорошее поведение, при работе с большими данными, когда окажется что памяти расходуется много больше чем предполагалось. smile

Автор: yorc 13.1.2010, 13:26
решил проблему установкой Redis-сервера с Перл-биндингом

всем спасибо!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)