Performance¶
Logtalk is implemented as a trans-compiler to Prolog. When compiling predicates, it preserves in the generated Prolog code all cases of first-argument indexing and tail-recursion. In practice, this mean that if you know how to write efficient Prolog predicates, you already know how to write efficient Logtalk predicates.
The Logtalk compiler adds an hidden execution-context argument to all entity predicates. In the common case where a predicate makes no calls to the execution-context predicates and message-sending control constructs and is neither a meta-predicate nor a coinductive predicate, the execution-context argument is simply passed between goals. In this case, with most backend Prolog VMs, the cost of this extra argument is null or negligible. When the execution-context argument needs to be accessed (e.g. to fetch the value of self for a ::/1 call) there may be a small inherent overhead due to the implicit unifications.
Local predicate calls¶
Local calls to object (or category) predicates have zero overhead in terms of number of inferences, as expected, compared with local Prolog calls.
Calls to imported or inherited predicates¶
Assuming the optimize flag is turned on and a static predicate, ^^/1 calls have zero overhead in terms of number of inferences.
Calls to module predicates¶
Local calls from an object (or category) to a module predicate have zero overhead (assuming both the module and the predicate are bound at compile time).
Messages¶
Logtalk implements static binding and dynamic binding for message sending calls. For dynamic binding, a caching mechanism is used by the runtime. It’s useful to measure the performance overhead in number of inferences compared with plain Prolog and Prolog modules. The results for Logtalk 3.17.0 and later versions are:
- Static binding: 0
- Dynamic binding (object bound at compile time): 1
- Dynamic binding (object bound at runtime time): 2
Static binding is the common case with libraries and most application
code; it requires compiling code with the optimize
flag turned on. Dynamic binding numbers are after the first call (i.e.
after the generalization of the query is cached). All numbers with the
events flag set to deny
(setting this flag to
allow
adds an overhead of 5 inferences to the results above).
The dynamic binding caches assume the used backend Prolog compiler does indexing of dynamic predicates. This is a common feature of modern Prolog systems but the actual details vary from system to system and may have an impact on dynamic binding performance.
Note that messages to self (::/1 calls) always use dynamic binding as the object that receives the message is only know at runtime.
Inlining¶
When the optimize flag is turned on, the Logtalk
compiler performs inlining of predicate calls whenever possible. This
includes calls to built-in methods such as once/1,
ignore/1, phrase/2, and phrase/3
but also calls to Prolog predicates that are either built-in, foreign, or
defined in a module (including user
). Inlining notably allows wrapping
module or foreign predicates using an object without introducing any
overhead. In the specific case of the
execution-context predicates,
calls are inlined independently of the optimize
flag value.
Generated code simplification and optimizations¶
When the optimize flag is turned on, the Logtalk
compiler simplifies and optimizes generated clauses (including those
resulting from the compilation of grammar rules), by flattening conjunctions,
folding left unifications (e.g. generated as a by-product of the compilation
of grammar rules), and removing redundant calls to true/0
.
Size of the generated code¶
The size of the intermediate Prolog code generated by the compiler is proportional to the size of the source code. Assuming that the term-expansion mechanism is not used, each predicate clause in the source code is compiled into a single predicate clause. But the Logtalk compiler also generates internal tables for the defined entities, for the entity relations, and for the declared and defined predicates. These tables enable support for fundamental features such as inheritance and reflection. The size of these tables is proportional to the number of entities, entity relations, and predicate declarations and definitions. When the source_data is turned on (the default when developing an application), the generated code also includes additional data about the source code such as entity and predicates positions in a source file. This data enables advanced developer tool functionality but it is usually not required when deploying an application. Thus, turning this flag off is a common setting for minimizing an application footprint.
Other considerations¶
One aspect of performance, that affects both Logtalk and Prolog code, is the characteristics of the Prolog VM. The Logtalk distribution includes two examples, bench and benchmarks, to help evaluate performance with specific backend Prolog systems. A table with results for a subset of the supported systems is also available in the Logtalk website.