замораживание / блокировка 2 целей для переменных, которые стали недостижимыми

Я сделал следующую небольшую программу, чтобы определить, используется ли память для таких целей, как freeze(X,Goal) восстанавливается, когда X становится недоступным:

%:- use_module(library(freeze)). % Ciao Prolog needs this

freeze_many([],[]).
freeze_many([_|Xs],[V|Vs]) :-
   freeze(V,throw(error(uninstantiation_error(V),big_freeze_test/3))),
   freeze_many(Xs,Vs).

big_freeze_test(N0,N,Zs0) :-
   (  N0 > N
   -> true
   ;  freeze_many(Zs0,Zs1),
      N1 is N0+1,
      big_freeze_test(N1,N,Zs1)
   ).

Запустим следующий запрос ...

?- statistics, length(Zs,1000), big_freeze_test(1,500,Zs), statistics.

... с разными процессорами Prolog и посмотрите на потребление памяти. Какая разница!

(AMD64) SICStus Prolog 4.3.2 : global stack in use = 108   MB
(AMD64) B-Prolog       8.1   : stack+heap   in use = 145   MB
(i386)  Ciao Prolog    1.14.2: global stack in use =  36   MB (~72 MB w/AMD64)
(AMD64) SWI-Prolog     7.3.1 : global stack in use =   0.5 MB
(AMD64) YAProlog       6.2.2 : global stack in use =  16   MB

Выполняя больше итераций с ?- length(Zs,1000), big_freeze_test(1,10000,Zs)., я сделал следующие наблюдения:

  • Ciao Prolog сообщает {ERROR: Memory allocation failed [in Realloc()]} перед прерыванием.

  • sicstus-prolog и b-prolog выделить все больше и больше, пока машина замирает.

  • swi-prolog выполняет все итерации в 3,554 секунды.
  • yap также выполняет все итерации, но требует 36,910 секунды.

Есть идеи, почему он работает с SWI-Prolog и YAProlog, но не с другими?

Учитывая время выполнения, почему SWI-Prolog превосходит YAProlog более чем на порядок?

Моя интуиция склоняется к взаимодействию «приписываемых переменных» с «сборкой мусора». SWI-Prolog и YAProlog имеют (разделяют?) API и реализацию переменных с атрибутами, отличные от других процессоров Prolog ... и, опять же, это может быть что-то совершенно другое. Спасибо!


person repeat    schedule 28.06.2015    source источник
comment
@ j4nbur53. Спасибо, что указали на цикл! Я имел в виду недостижимость с корнями. Я согласен с тем, что такое использование freeze/2 не является ни типичным, ни образцом, а скорее отчаянной мерой, используемой как последнее средство: грязный хакер, который в некоторых случаях упрощает реализацию замороженной цели намного. .. Вероятно, лучше не будет влиять на внутреннюю реализацию или конструкцию механизма подвески.   -  person repeat    schedule 26.02.2016
comment
@ j4nbur53: freeze/2 - это действительно путь ограничений. Слишком сыро, чтобы его можно было надежно использовать.   -  person false    schedule 26.02.2016
comment
@ j4nbur53. Вы имеете в виду эмпирические пространственно-временные измерения when ((nonvar (X), nonvar (Y)), foo (X, Y)), и оба X, Y недоступны., работает / используется разные процессоры Prolog?   -  person repeat    schedule 27.02.2016
comment
Если используется call_residue_vars / 2, он все равно будет использовать около 98 МБ. См. Также здесь: github.com/SWI-Prolog/issues/issues/ 19 # issuecomment-437183547   -  person Mostowski Collapse    schedule 09.11.2018
comment
Плохой способ измерения. Вы должны позвонить garbage_collect перед statistics. SWI-Prolog довольно агрессивно вызывает сборщик мусора, в то время как другие системы предпочитают расширять стеки. Расширение стека часто происходит быстрее, но требует больше памяти, что особенно болезненно, если у вас много потоков. Масштабируемость потоков - это фокус для SWI-Prolog.   -  person Jan Wielemaker    schedule 09.11.2018


Ответы (2)


TL; DR: ошибка в SWI больше нет !

Вы создаете 500 000 замороженных целей, которые впоследствии недостижимы. Что означают эти цели? Системы Prolog не анализируют цель на предмет ее семантической значимости (до ее фактического выполнения). Поэтому мы должны предположить, что цели могут быть семантически релевантными. Поскольку цели уже не связаны друг с другом, единственный семантический эффект, который они могут иметь, состоит в том, чтобы оказаться ложным и, таким образом, сделать следующий ответ ложным.

Так что вместо этого достаточно рассмотреть freeze(_,false).

Семантически предикаты p/0 и q/0 эквивалентны:

p :-
   false.

q :-
   freeze(_,false).

Однако с процедурной точки зрения первая цель не выполняется, а вторая достигается. В таких ситуациях важно различать решения и ответы. Когда Prolog завершается успешно, он выдает ответ, обычно это называется подстановкой ответа в Прологе без ограничений, где подстановки ответов всегда содержат одно или бесконечно много решений 1. При наличии ограничений или грубой сопрограммы ответ теперь может содержать замороженные цели или ограничения, которые необходимо принять во внимание, чтобы понять, какие решения фактически описаны.

В приведенном выше случае количество решений равно нулю. Когда система теперь собирает эти замороженные цели, это фактически меняет смысл программы.

В SICStus это отображается следующим образом:

| ?- q.
prolog:freeze(_A,user:false) ? ;
no

В SWI и YAP эти цели не отображаются по умолчанию, и поэтому есть вероятность, что таких ошибок, как эта, не обнаружено.


PS: В прошлом было сравнение между различными системами, касающимися GC и ограничений, при этом SICStus в то время был единственным, кто прошел все тесты. Тем временем некоторые системы улучшились.

Я сначала посмотрел на цифры SICStus: 216 байт на замораживание! Это 27 слов, из которых 13 только для термина, представляющего цель. Так что просто 14 слов для остановки. Не так плохо.

PPS: замороженная цель была throw/2, она должна была быть throw/1


Мелкий шрифт 1: некоторые примеры: подстановка ответов X = 1 содержит ровно одно решение, а X = [_A] содержит бесконечно много решений, таких как X = [a] и многие, многие другие. Все это становится намного сложнее в контексте ограничений.

person false    schedule 28.06.2015
comment
Спасибо за развернутый ответ! Что касается семантики, я все еще не согласен ... Какова семантика цели, которую я сформулировал freeze(V,throw(error(uninstantiation_error(V),big_freeze_test/3)))? - person repeat; 28.06.2015
comment
@repeat: Дело в том, что цель никогда не проверяется до создания экземпляра V. Следовательно, это могло быть равносильно false. Я думал, что говорил об этом выше. - person false; 28.06.2015
comment
Думаю, я понял. Разработчик должен предположить, что замороженная цель может повлиять на логическую семантику. То, что я искал, не подходит freeze/2. Должен ли я работать напрямую с атрибутированными переменными? Собираются ли приписываемые переменные (и вспомогательные данные), когда они становятся недоступными? - person repeat; 28.06.2015
comment
Нет, для постройки subsumes_term_t/3. Надеемся, что эти никогда не использовавшиеся замороженные цели помогают нам оставаться логически обоснованными. (У меня он работал с SICStus и трепло. Ура! Однако утечка памяти.) - person repeat; 29.06.2015
comment
?? Вы также можете ответить на свои вопросы! - person false; 29.06.2015
comment
Ссылаясь на github.com/SWI-Prolog/issues/issues/19. .. означает ли это, что эти переменные остаются ссылочными при использовании call_residue_vars/2, и в противном случае они становятся недоступными? - person repeat; 02.07.2015
comment
@repeat: всегда предполагайте, что присутствует call_residue_vars/2. - person false; 02.07.2015

Восстановление замороженных целей невозможно в определенных настройках. Например, в CC это будет запрещено, поскольку программа имеет как минимум 3 результата:

Есть статья Эвана Тика, объясняющая CC:

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

РАЗВИТИЕ СОВМЕСТНЫХ ЯЗЫКОВ ЛОГИЧЕСКОГО ПРОГРАММИРОВАНИЯ
Эван Тик - 1995
https://core.ac.uk/download/pdf/82787481.pdf

Вот пример, иллюстрирующий проблему. freeze / 2 более примитивен, чем CLP (FD). Таким образом, CLP (FD) иногда может делать следующее:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.21)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.

?- [user].
test(A,B) :- A #< X, X #< B.

?- test(1,2).
false

?- test(1,3).
true

Но теперь, если я сделаю то же самое с замораживанием, я потеряю всю информацию, поскольку SWI-Prolog восстанавливает замороженные цели:

?- [user].
test2(A,B) :- freeze(X,(integer(X),A<X)), freeze(X,(integer(X),X<B)).

?- test2(1,2).
true.

?- test2(1,3).
true.

Возможно, требуется более тонкий контроль над поведением сборки мусора.

Редактировать 1: для более тонкого управления в SWI-Prolog предлагается обернуть его с помощью call_residue_vars / 2. Я не знаю, является ли это самым умным решением, но, по крайней мере, это решение:

?- call_residue_vars(test2(1,2),L).
L = [_5538],
freeze(_5538,  (integer(_5538), 1<_5538)),
freeze(_5538,  (integer(_5538), _5538<2)).

?- call_residue_vars(test2(1,3),L).
L = [_5544],
freeze(_5544,  (integer(_5544), 1<_5544)),
freeze(_5544,  (integer(_5544), _5544<3)).
person Mostowski Collapse    schedule 08.11.2018
comment
Оберните свои цели call_residue_vars(G_0, Vs), чтобы увидеть, что SWI не вернет замороженные цели. - person false; 08.11.2018
comment
Таким образом, примеры, которые полагаются на этот сборщик мусора, такие как «библиотека (pio)», больше не будут работать с высокой производительностью или даже сбой, когда я вызываю его с помощью call_residue_vars / 2? Или есть способ более точного контроля? - person Mostowski Collapse; 08.11.2018
comment
SWI не возвращает замороженные цели (по крайней мере, при наличии хорошего верхнего уровня), но цели, которые больше не заморожены (и когда нет возможности вернуться к ним), возвращаются. В ваших примерах X остается переменной. В случае library(pio) замороженных голов не осталось. - person false; 08.11.2018
comment
В прошлом поведение SWI несколько раз было непоследовательным. Я не знаю точного состояния. Вместо этого я рекомендую использовать SICStus для всех таких случаев. - person false; 08.11.2018