POV-Ray : Newsgroups : povray.general : Function performance Server Time
26 Nov 2024 05:41:23 EST (-0500)
  Function performance (Message 1 to 9 of 9)  
From: David Given
Subject: Function performance
Date: 21 Jul 2011 19:14:39
Message: <4e28b2df$1@news.povray.org>
As part of my Insanely Complicated Tree function, I need to clip some
(x, y, z) coordinates to a sphere of radius r. (I'm going to use it as
an isosurface modifier; I want the effect to value according to phi and
theta but be constant for all r.)

The obvious way to do this is to normalise the vector of (x, y, z) and
then multiply by r, which more or less becomes:

f_normal_x(x, y, z, r) = r * x / f_r(x, y, z)
f_normal_y(x, y, z, r) = r * y / f_r(x, y, z)
f_normal_z(x, y, z, r) = r * z / f_r(x, y, z)

(Possible typos/thinkos owing to posting past midnight...)

Now, I'm going to be using this in a single expression:

result(x, y, z, r) = f_bozo(f_normal_x(x, y, z, r), f_normal_y(x, y, z,
r), f_normal_z(x, y, z, r))

My question is: is Povray intelligent enough to calculate f_r(x, y, z)
once, or will it do it three times? If it's going to do it three times,
are there any clean strategies for optimising the function (or should I
just not worry about it)?

My worry is that calculating common values and then passing them in
makes writing modular functions very hard --- the logic gets spread
across all functions rather than being focused in specific areas. e.g.:

result2(x, y, z, r) = f_bozo(x*r, y*r, z*r)
result1(x, y, z, r, R) = result2(x/R, y/R, z/R, r)
result(x, y, z, r) = result1(x, y, z, r, f_r(x, y, z))

...which is much less easy to read.

-- 
┌─── dg@cowlark.com ─────
http://www.cowlark.com ─────
│
│ "I have a mind like a steel trap. It's rusty and full of dead mice."
│ --- Anonymous, on rasfc


Post a reply to this message

From: Thorsten Froehlich
Subject: Re: Function performance
Date: 22 Jul 2011 09:10:11
Message: <4e2976b3@news.povray.org>
On 22.07.11 01:14, David Given wrote:
> As part of my Insanely Complicated Tree function, I need to clip some
> (x, y, z) coordinates to a sphere of radius r. (I'm going to use it as
> an isosurface modifier; I want the effect to value according to phi and
> theta but be constant for all r.)
<snip>
> ...which is much less easy to read.

If you show the POV-Ray SDL, then I can tell you what will happen and how to 
optimise it. From your post it is not clear if you are talking about macros 
or functions as you say function, but use a syntax closer to a declare.

	Thorsten


Post a reply to this message

From: David Given
Subject: Re: Function performance
Date: 22 Jul 2011 15:54:02
Message: <4e29d55a$1@news.povray.org>
On 22/07/11 14:10, Thorsten Froehlich wrote:
[...]
> If you show the POV-Ray SDL, then I can tell you what will happen and
> how to optimise it. From your post it is not clear if you are talking
> about macros or functions as you say function, but use a syntax closer
> to a declare.

Functions (I'm missing out the Povray boilerplate for simplicity because
it's too verbose).

-- 
┌─── dg@cowlark.com ─────
http://www.cowlark.com ─────
│
│ "I have a mind like a steel trap. It's rusty and full of dead mice."
│ --- Anonymous, on rasfc


Post a reply to this message

From: Thorsten Froehlich
Subject: Re: Function performance
Date: 22 Jul 2011 17:25:16
Message: <4e29eabc$1@news.povray.org>
On 22.07.11 21:54, David Given wrote:
> On 22/07/11 14:10, Thorsten Froehlich wrote:
> [...]
>> If you show the POV-Ray SDL, then I can tell you what will happen and
>> how to optimise it. From your post it is not clear if you are talking
>> about macros or functions as you say function, but use a syntax closer
>> to a declare.
>
> Functions (I'm missing out the Povray boilerplate for simplicity because
> it's too verbose).

Well, as said, without the SDL I can't help as that is what in the end 
determines how your functions are evaluated.

	Thorsten


Post a reply to this message

From: David Given
Subject: Re: Function performance
Date: 22 Jul 2011 17:51:50
Message: <4e29f0f6$1@news.povray.org>
On 22/07/11 22:25, Thorsten Froehlich wrote:
[...]
> Well, as said, without the SDL I can't help as that is what in the end
> determines how your functions are evaluated.

I'm not looking to optimise this specific bit of code. I'm looking for
general strategies in how to optimise code in general.

For example: I assume that Povray parses functions into an AST, and then
evaluates them by traversing the tree. Once the AST has been
constructed, does it run any of the standard compiler optimisation
algorithms on this such as common subexpression elimination or constant
propagation?

e.g. in my original example, when evaluating result(), f_r() is
referenced three times with the same parameters. Is Povray clever enough
to spot this and only evaluate it once?

-- 
┌─── dg@cowlark.com ─────
http://www.cowlark.com ─────
│
│ "I have a mind like a steel trap. It's rusty and full of dead mice."
│ --- Anonymous, on rasfc


Post a reply to this message

From: Thorsten Froehlich
Subject: Re: Function performance
Date: 23 Jul 2011 02:12:58
Message: <4e2a666a$1@news.povray.org>
On 22.07.11 23:51, David Given wrote:
> On 22/07/11 22:25, Thorsten Froehlich wrote:
> [...]
>> Well, as said, without the SDL I can't help as that is what in the end
>> determines how your functions are evaluated.
>
> I'm not looking to optimise this specific bit of code. I'm looking for
> general strategies in how to optimise code in general.
>
> For example: I assume that Povray parses functions into an AST, and then
> evaluates them by traversing the tree. Once the AST has been
> constructed, does it run any of the standard compiler optimisation
> algorithms on this such as common subexpression elimination or constant
> propagation?

Constant folding will always happen, as long as they are next to each other 
in the tree. This includes single-argument intrinsic functions. (sqrt, sin, 
cos, tan, abs, etc.). Further, there is inline expansion for pow, min and max.

There are no higher-level optimisations, as there is no detection (nor a 
complete list) of side effects some pattern functions do have (weel, had 
before 3.7). Further, the syntax tree is only kept as long as needed, which 
means as soon as the parser is done with a function.

Without knowing the specifics, most likely for your use the best way to 
eliminate common subexpressions is to use multiple functions and pass the 
subexpressions as arguments.

	Thorsten


Post a reply to this message

From: David Given
Subject: Re: Function performance
Date: 23 Jul 2011 07:12:12
Message: <4e2aac8c$1@news.povray.org>
On 23/07/11 07:12, Thorsten Froehlich wrote:
[...]
> There are no higher-level optimisations, as there is no detection (nor a
> complete list) of side effects some pattern functions do have (weel, had
> before 3.7). Further, the syntax tree is only kept as long as needed,
> which means as soon as the parser is done with a function.

Hmm. Thanks. What's actually used to evaluate the function, then? Is it
byte-compiled?

> Without knowing the specifics, most likely for your use the best way to
> eliminate common subexpressions is to use multiple functions and pass
> the subexpressions as arguments.

Yes, that's what I'm doing --- it's brittle and rather verbose. (A lot
of my functions take (x, y, z) coordinates, clip them to a sphere, and
actually do a texture calculation based the clipped value. It's a bit
painful.)

I don't suppose you know what happened to the old POVMan patch to allow
Renderman shader functions from Povray, do you? All the references I've
found appear to be dead...

-- 
┌─── dg@cowlark.com ─────
http://www.cowlark.com ─────
│
│ "I have a mind like a steel trap. It's rusty and full of dead mice."
│ --- Anonymous, on rasfc


Post a reply to this message

From: clipka
Subject: Re: Function performance
Date: 4 Aug 2011 18:00:35
Message: <4e3b1683$1@news.povray.org>
Am 23.07.2011 13:12, schrieb David Given:

> Hmm. Thanks. What's actually used to evaluate the function, then? Is it
> byte-compiled?

It does use a VM, yes.

>> Without knowing the specifics, most likely for your use the best way to
>> eliminate common subexpressions is to use multiple functions and pass
>> the subexpressions as arguments.
>
> Yes, that's what I'm doing --- it's brittle and rather verbose. (A lot
> of my functions take (x, y, z) coordinates, clip them to a sphere, and
> actually do a texture calculation based the clipped value. It's a bit
> painful.)

My favorite solution to such issues is to replace, e.g.,

   #declare F = function(a) { sin(a) + cos(a) + sin(a)*cos(a) }

with

   #declare F_ = function(sina,cosa) { sina + cosa + sina*cosa }
   #declare F  = function(a) { F_(sin(a),cos(a)) }

The idea is to create a helper function where all the multiply-used 
sub-expressions are substituted by parameters, and have a main function 
that computes these sub-expressions and passes them to that helper 
function. I think it's sufficiently easy to read if you use good names 
for the sub-expressions.

I usually use an added underscore for such functions, but you might also 
call them "F2", "F_sub", or whatever fits your taste.

For instance, for your example I might use

   #declare f_normal_x_ = function(x,r,fr) { r * x / fr }
   #declare f_normal_y_ = function(y,r,fr) { r * y / fr }
   #declare f_normal_z_ = function(z,r,fr) { r * z / fr }

   #declare result_ = function(x,y,z,r,fr) { f_bozo(
     f_normal_x_(x,r,fr), f_normal_y_(y,r,fr), f_normal_z_(z,r,fr)
   )}

   #declare result = function(x,y,z,r) { result_(x,y,z,r, f_r(x,y,z)) }

BTW, note that f_normal_x_, f_normal_y_ and f_normal_z_ are actually 
redundant; you could just as well use

   #declare f_normal_ = function(axis,r,fr) { r*axis / fr }
   #declare result_ = function(x,y,z,r,fr) { f_bozo(
     f_normal_(x,r,fr), f_normal_(y,r,fr), f_normal_(z,r,fr)
   )}
   #declare result = function(x,y,z,r) { result_(x,y,z,r, f_r(x,y,z)) }

However, depending on circumstances I might still use separate 
functions, to have a 1:1 mapping between original functions and helper 
functions which might improve legibility.


Not sure if that boils down to the same as your own attempt - I didn't 
try to understand how your set of functions works, as they're indeed not 
excessively easy to read ;-). In my experience, good naming of the 
sub-expression substitution parameters is essential for legibility.


Post a reply to this message

From: Alain
Subject: Re: Function performance
Date: 5 Aug 2011 14:14:10
Message: <4e3c32f2@news.povray.org>
Le 2011/08/04 18:00, clipka a écrit :

>
> My favorite solution to such issues is to replace, e.g.,
>
> #declare F = function(a) { sin(a) + cos(a) + sin(a)*cos(a) }
>
> with
>
> #declare F_ = function(sina,cosa) { sina + cosa + sina*cosa }
> #declare F = function(a) { F_(sin(a),cos(a)) }
>
> The idea is to create a helper function where all the multiply-used
> sub-expressions are substituted by parameters, and have a main function
> that computes these sub-expressions and passes them to that helper
> function. I think it's sufficiently easy to read if you use good names
> for the sub-expressions.
>

You are probably also geting some performance improvement that way: The 
sin() and cos() functions are now only evaluated once, then you pass the 
returned values to the helper function.



Alain


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.