Как читать большие файлы пролога в ECLiPSe?

#prolog #eclipse-clp

Вопрос:

Я сгенерировал большой файл путей через eclipse. Каждая строка содержит предложение для списка из 27 пунктов.

 $ wc -l snake_points.pl
240917 snake_points.pl

$ ls -lh snake_points.pl
-rw-rw-r-- 1 carl carl 72M Sep  6 02:39 snake_points.pl

$ head -n 1 snake_points.pl 
snake_points([(2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 2), (2, 1, 1), (2, 1, 0), (2, 2, 0), (2, 2, 1), (2, 2, 2), (1, 2, 2), (0, 2, 2), (0, 1, 2), (0, 0, 2), (0, 0, 1), (0, 1, 1), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 2, 1), (1, 2, 0), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 0, 2), (1, 0, 1), (1, 0, 0), (0, 0, 0)]).
 

Однако я не могу загрузить файл в память (даже с 8 г кучи):

 $ time eclipse -f snake_points.ecl -e 'halt.'
*** Overflow of the global/trail stack in spite of garbage collection!
You can use the "-g kBytes" (GLOBALSIZE) option to have a larger stack.
Peak sizes were: global stack 8388576 kbytes, trail stack 59904 kbytes

________________________________________________________
Executed in  128.05 secs   fish           external 
   usr time  122.92 secs  297.00 micros  122.92 secs 
   sys time    5.01 secs   37.00 micros    5.01 secs 
 

Сравните это с swipl :

 $ time swipl -f snake_points.pl -g 'halt.'

________________________________________________________
Executed in   53.56 secs   fish           external 
   usr time   53.27 secs  272.00 micros   53.27 secs 
   sys time    0.28 secs   41.00 micros    0.28 secs 
 

Ни то, ни другое не впечатляет, но я бы ожидал, что ECLiPSe завершится с разумным объемом памяти.

Является ли это ожидаемым поведением? Что можно сделать?

Я понимаю, что решением может быть «использование базы данных» или EXDR, но разве это не должно быть сделано эффективно?

Ответ №1:

Проблема в том, что вы не только читаете данные, вы пытаетесь скомпилировать их как единый предикат с 240917 предложениями, и компилятор действительно не создан для такого рода использования.

Вместо этого вы можете читать и утверждать предложения из файла данных по одному, например так:

 assert_from_file(File) :-
        open(File, read, S),
        repeat,
            read(S, Term),
            ( Term == end_of_file ->
                true
            ;
                assert(Term),
                fail
            ),
        !, close(S).
 

Это загружает ваши данные за конечное время

 ?- assert_from_file("snake_points.pl").
Yes (19.38s cpu)
 

и затем вы можете вызвать результирующий предикат, как и ожидалось

 ?- snake_points(X).
X = [(2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 2), (2, 1, 1), (2, 1, 0), (2, 2, 0), (2, 2, 1), (2, 2, 2), (1, 2, 2), (0, 2, 2), (0, 1, 2), (0, 0, 2), (0, 0, 1), (0, 1, 1), (0, 1, 0), (0, 2, 0), (0, ..., ...), (..., ...), ...]
Yes (0.04s cpu, solution 1, maybe more) ? ;
 

Но какую бы проблему вы ни пытались решить, это не выглядит как самый многообещающий подход…

Комментарии:

1. Не было бы проще просто написать файл с директивами a dynamic/1 и an include/1 , объявляющими snake_points/1 предикат динамическим и включающими исходный snake_points.pl файл?

2. @Paulo, к сожалению, нет. Компилятор, как написано в настоящее время, все еще всасывает все предложения для предиката за один раз, вызывая проблему.

3. Использование dynamic/1 и include/1 фактически завершено с 8 ГБ примерно за ~60 секунд.

4. Использование assert_from_file в комплекте с 1 ГБ за ~22 секунды. Использование assert_from_file с предложением @PauloMoura привело к лучшей производительности с 1 ГБ менее чем за 9 секунд!

Ответ №2:

Вы пробовали изменить представление очков? Использование ','/2 для представления кортежей с более чем двумя элементами на кортеж-плохая идея. Обратите внимание, что:

 [eclipse 30]: write_canonical((1,2,3)).
','(1, ','(2, 3))
Yes (0.00s cpu)
 

Попробуйте вместо этого, например p(1,2,3) . Это должно немного снизить требования к памяти и может иметь значение.

Комментарии:

1. Это сокращает использование памяти до ~500 МБ и завершается за 40 секунд. Спасибо!