Improving OpScript Performance
==============================

.. contents::

Understanding the Lua Garbage Collector
---------------------------------------
In Lua, memory can be allocated on either the stack or the heap.
Memory allocated on the heap is reclaimed through Lua's garbage collector.
When the garbage collector is running, your Lua code cannot make forward
progress. Therefore, reducing the frequency with which the garbage collector
must run or the amount of garbage it must collect will help improve the
performance of your OpScripts.

The following Lua constructs, while not bad, result in a new object being
created that must be subsequently cleaned up during garbage collection. It's
worth paying particular attention if these constructs are used within loops.

``string_one..string_two`` - String concatenation or any string creation
function could potentially result in the creation of a new object. As Lua
strings are unique, Lua will first check to determine if the string has
been created elsewhere.

``{ ... }`` - Each time a table constructor is executed, a new table is
created.

``function() ... end`` - Executing a function statement creates a closure. If
this is executed within a loop of n iterations it will result in the creation
of *n* closures.

Summary
~~~~~~~
Understanding which constructs create heap-based objects in Lua and being
careful of their use in loops can help improve performance. If necessary,
consider the use of object pools to reuse objects. For more information, see
http://lua-users.org/wiki/OptimisingGarbageCollection.

Avoiding ".." in loops
----------------------
As a concrete example of `Understanding the Lua Garbage Collector`_, in this
recommendation we explore the use of the string concatenation operator ``..``,
commonly used in OpScripts due to its convenient shorthand notation.

As an example, consider the following code used to build an L-System
description:

   .. code-block:: lua

    local result = ""
    for i = 1, #inputStr do
      local char = inputStr:sub(i,i)
      local ruleStr = rulesTable[char]
      if ruleStr then
        result = result..ruleStr
      else
        result = result..char
      end
    end
    return result

``result`` is appended to in a tight loop. Whilst convenient, this results in a
large number of string allocations and poor performance.

The preceding code can be re-written to handle all concatenation only once the
loop has completed:

    .. code-block:: lua

     local buf = {}
     for i = 1, #inputStr do
       local char = inputStr:sub(i,i)
       local ruleStr = rulesTable[char]
       if ruleStr then
         buf[#buf+1] = ruleStr
       else
         buf[#buf+1] = char
       end
     end
     return table.concat(buf)

Running the above example as part of a large Katana project file reduced the
scene processing time of this function by approximately 2.5x, as shown in the
following table:

==================  ======
String Method       Time
==================  ======
``..`` Operator     7.011s
``Table.concat()``  2.681s
==================  ======

Summary
~~~~~~~
When optimizing OpScripts, first identifying the use of string creation and
concatenation patterns, particularly within tight loops, can help improve
performance.