2015-09-26 77 views
0

使用Logtalk时,看起来我的程序用Logtalk对象执行比普通Prolog更长。我做了一个标杆纯的Prolog与logtalk对象的封装相当于下面的简单谓词的执行比较:Logtalk方法调用性能优化

%% 
% plain prolog predicate 
plain_prolog_simple :- 
    fail. 

%% 
% object encapsulation 
:- object(logtalk_obj). 

    :- public([simple/0]). 
    simple :- 
     fail. 

:- end_object. 

这里就是我得到:

?- benchmark(plain_prolog_simple). 

Number of repetitions: 500000 
Total time calls: 0.33799099922180176 seconds 
Average time per call: 6.759819984436035e-7 seconds 
Number of calls per second: 1479329.3346604244 
true. 

?- benchmark(logtalk_obj::simple). 

Number of repetitions: 500000 
Total time calls: 2.950408935546875 seconds 
Average time per call: 5.90081787109375e-6 seconds 
Number of calls per second: 169468.0333888435 
true. 

我们可以看到logtalk_obj::simple call慢于plain_prolog_simple通话。 我使用SWI Prolog作为后端,我试图设置一些日志谈话标志,但没有成功。

编辑:我们可以找到标杆代码样本https://github.com/koryonik/logtalk-experiments/tree/master/benchmarks

有什么不对?为什么这种表现差异?如何优化Logtalk方法调用?

回答

2

简而言之,你的标杆::/2目标Logtalk编译在顶层解释器。这是一个经典的基准测试错误。顶层的目标,简单的Prolog目标,模块明确定义的谓词目标,或者消息发送目标总是要被解释,即时编译。

您可以在编译的源文件中获得性能接近普通Prolog的邮件发送目标,这是最常见的情况。请参阅Logtalk发行版中的benchmarks示例,以了解避免上述陷阱的基准测试解决方案。

性能差距(普通的Prolog和Logtalk目标之间)取决于所选的后端Prolog编译器。当可以进行静态绑定时,成熟的Prolog VM(例如SICStus Prolog或ECLiPSe)可以忽略该差距。然而,一些Prolog虚拟机(例如SWI-Prolog)缺少一些可以使间隙更大的优化,特别是在紧密循环中。

P.S. Logtalk是一款开箱即用的设置配置,而不是性能。请特别参阅有关optimize标志的文档,该标志应打开以进行静态绑定优化。

UPDATE

从您的存储库中的代码开始,并假设SWI-Prolog的作为后端的编译器,尝试:

----- code.lgt ----- 
% plain prolog predicate 
plain_prolog_simple :- 
    fail. 

% object encapsulation 
:- object(logtalk_obj). 

    :- public(simple/0). 
    simple :- 
     fail. 

:- end_object. 
-------------------- 

----- bench.lgt ----- 
% load the SWI-Prolog "statistics" library 
:- use_module(library(statistics)). 

:- object(bench). 

    :- public(bench/0). 
    bench :- 
     write('Plain Prolog goal:'), nl, 
     prolog_statistics:time({plain_prolog_simple}). 
    bench :- 
     write('Logtalk goal:'), nl, 
     prolog_statistics:time(logtalk_obj::simple). 
    bench. 

:- end_object. 
--------------------- 

保存这两个文件,然后启动Logtalk:

$ swilgt 
... 
?- set_logtalk_flag(optimize, on). 
true. 

?- {code, bench}. 
% [ /Users/pmoura/Desktop/bench/code.lgt loaded ] 
% (0 warnings) 
% [ /Users/pmoura/Desktop/bench/bench.lgt loaded ] 
% (0 warnings) 
true. 

?- bench::bench. 
Plain Prolog goal: 
% 2 inferences, 0.000 CPU in 0.000 seconds (69% CPU, 125000 Lips) 
Logtalk goal: 
% 2 inferences, 0.000 CPU in 0.000 seconds (70% CPU, 285714 Lips) 
true. 

谓词time/1是一个元谓词。 Logtalk编译器使用meta-predicate属性来编译time/1参数。 {}/1控制结构是一个Logtalk编译器旁路。它确保它的参数在普通的Prolog数据库中按原样调用。

+0

谢谢,真的。你说“为了编译源文件中的消息发送目标而使性能接近普通Prolog”,但是如何从swilgt('log talk_load/2-3'只能在编译时编译)进行编译? – Koryonik

+0

'logtalk_load/1-2'既可以编译和加载源文件(对于'logtalk_load/1',您可以在顶层使用上面示例中的{{}/1'快捷键)。解释顶层的任何调用,但只影响调用谓词的性能,而不影响谓词。 –

0

一个标杆技巧与SWI-Prolog的和YAP(可能还有其他),提供一个time/1元谓词是使用这个谓词Logtalk的<</2调试控制结构和内置对象的logtalk工作。使用SWI-Prolog的作为后台编译:

?- set_logtalk_flag(optimize, on). 
... 
?- time(true). % ensure the library providing time/1 is loaded 
... 
?- {code}. 
... 
?- time(plain_prolog_simple). 
% 2 inferences, 0.000 CPU in 0.000 seconds (59% CPU, 153846 Lips) 
false. 
?- logtalk<<(prolog_statistics:time(logtalk_obj::simple)). 
% 2 inferences, 0.000 CPU in 0.000 seconds (47% CPU, 250000 Lips) 
false. 

快速解释,<</2控制结构调用前编译的目标参数。由于optimize标志已打开且time/1是元谓语,因此其参数已完全编译,并且静态绑定用于发送消息。因此,我们得到的推理数量相同。因此,这个技巧允许您在Logtalk消息发送目标的顶层进行快速基准测试。

使用YAP类似但更简单,因为time/1是一个内置的元谓语,而不像SWI-Prolog中的元元谓语。