3.3.6: Better management of vector literals

Hello there,

Here are some (uninteresting) news about the current development of the 3.3.6 version of G’MIC.
These last days I’ve been focusing on trying to optimize the math parser, more precisely, finding a way to better manage “vector literals”, i.e. vectors or strings that have constant values and that can be theoretically defined only once (such as [ 1,2,3 ] or ['hello folks!']) when evaluating a mat expression.

I like the definitiion given in this page :
“A vector literal is a constant expression for which the value is interpreted as a vector type.”

The problem:

In G’MIC, the simplest example of a vector literal encountered in a math expression would be:

foo : 
  tic
  10000,10000,1,3,">[ 255,128,0 ]"
  toc rm

(i.e. the math expression is actually the vector literal itself).

This example simply creates a 10.000x10.000 color image where all pixels have the RGB color 255,128,0. Here, [ 255,128,0 ] is a vector literal, as all its component do not depend on any variable, so it can be (theoretically) computed once.

So far, the G’MIC math evaluator did not make a difference between “vectors” and “vector literals”. When a vector-valued object was encountered in a math expression, it was initialized once, but filled each time it was encountered in a sub expression.
So where it appeared inside a loop or in a fill expression, the vector was filled every time.
In my example above, the vector [ 255,128,0 ] was re-filled with its values, for each new image pixel traversed by fill.

What I’ve done:

In G’MIC 3.3.6, I’ve introduced a kind of “vector literal” detector in the math parser: it determines if a vector-valued object appearing in a math expression has constant values. In that case, the vector is initialized in a begin() block, and then evaluated only once when evaluating the whole math expression.

Let me show the effect of this, with the timing obtained for the simple example above (I’ve disabled multi-threading to have more representative timings:

With version 3.3.5:

$ gmic foo
[gmic]./ Start G'MIC interpreter (v.3.3.5).
[gmic]./foo/ Elapsed time: 1.002 s.
[gmic]./ End G'MIC interpreter.

With version 3.3.6_pre:

$ ./gmic foo
[gmic]./ Start G'MIC interpreter (v.3.3.6).
[gmic]./foo/ Elapsed time: 0.483 s.
[gmic]./ End G'MIC interpreter.

That’s a x2 gain on the evaluation of the math expression. Not bad!

Well, that’s it. In practice, you’ll probably won’t notice any difference, but it’s still a slight improvement .

4 Likes

Here’s a question, if a vector appears on begin, but gets modified after begin, the math evaluator will not treat it as const?

To be modified afterwards, your vector must be assigned to a variable. And vector-valued variables are hopefully not considered as vector literals.

Got it. However, what if some one wants that vector variable to be a const after evaluation akin to const scalar? Would it be a good idea to revisit const vector_variable = [ number_a...]? I have a few thing that might benefit.

The keyword const is used to define a variable whose value can be computed at the compiling stage. Right now, const only applies to scalar variables.
The main advantages of having a const variable defined is often to enable optimizations.
For instance,

foo : 
  eval "
    const var = round(sin(2*pi*$1));  # Can be computed at compile time. Is actually 0 if $1 is an integer
    if (var,
      .. do something here ..
    );
  "

Here, if you call foo with an integer argument, then const var = 0 is estimated at compile time, and the if (var, ... ) is in fact never evaluated.

Another use of const variables is when they are arguments to functions that requires a const argument. Typically, when you want to define a vector with a specific length:

eval "
  const size = round(abs(30*sin(2*pi/3)));
  V = vector(#size);
"

It would be theoretically doable to define const for vector-valued variables, but there is a trade-off to consider between the cost of implementing this feature (considering the current state of the math expression evaluator, that would be quite hard, believe me), and the benefit we can get from it.

My impression is that there wouldn’t be much benefit of having const vector-valued variables, as:

  • There are no functions that require the passing of const vector-valued arguments yet.
  • There are far fewer cases where knowing a vector is const can actually help the compiler to optimize math expressions. The main case was actually when vector literals were used, which is now managed correctly (and implicitely).