POV-Ray : Newsgroups : povray.general : A couple parser performance issues/optimizations. : Re: A couple parser performance issues/optimizations. Server Time
2 May 2024 21:14:38 EDT (-0400)
  Re: A couple parser performance issues/optimizations.  
From: William F Pokorny
Date: 8 Oct 2017 08:22:26
Message: <59da1882@news.povray.org>
On 10/06/2017 01:18 PM, William F Pokorny wrote:
> Details and sample code are part of my comment today to the github issue:
> 
> https://github.com/POV-Ray/povray/issues/313
> 
> Bill P.

Christoph asked me to limit my comments on github to those specific to 
the feature proposal in 313 so moving the bulk of the text posted there 
below so it is not lost and can still be referred to on github.

--- Moved text below ---

I've borrowed code Bill Walker (Bald Eagle) posted in the "Isosurface 
and f_spiral - bug or feature?"  thread started by Ronkar:

http://news.povray.org/povray.newusers/thread/%3Cweb.59d6bdad808cf4025cafe28e0%40news.povray.org%3E/

to demonstrate a couple of parser performance issues/user-optimizations. 
Both of which can be positively affected by how we code.  One of which, 
perhaps, the parser code itself can better handle.

First, writing macros partly or wholly as functions can quite often 
provide a significant parser speed up.  In the code below 10.78sec to 
3.31sec, or -69%, for Bill's code with further optimization still possible.

I think about our include files - CRGB2H in colors.inc for example - and 
I wonder how much parser performance improvement we'd get by re-coding 
to functions.  Or perhaps adding function equivalents to the current 
macros as the functional equivalents are less easy to understand on 
quick viewing.  I have rattling around in my head someone (Trevor 
Quayle?)  already coded many color space 'functions' as functions.  I 
wasn't able to quickly find the work just now and with the pigment { 
user_defined { } } feature of 3.8 maybe the approach would today be 
different...

In using functions we use our VM which already pre-calculates everything 
it can.  In other words in using functions we take the parsing / 
re-parsing / re-calculating which happens with macros and turn it into 
VM 'compiled' code which can be called.

Second, comments in inner loops are costly. I believe, but did not 
prove, the comments in Bill's Spiral macro are some part of why the 
functional version is faster in the code below.  To demonstrate the 
comment effect more plainly, the code below has a block of comments 
which, on copy/move into the inner-most for loop, slow the parsing by 
20+% and 60+% depending on whether using the original Spiral macro or 
the FN0to1Val function set up by a once called macro.

Users can move comments outside the loops/macros and help themselves, or 
pre-process SDL code to remove all comments themselves. However,  here I 
have to think the parser itself can do better.  FYI - Just shortening 
the block of comments to leave only the leading '//' characters speeds 
the parser up considerably.  Might the parser bail on comment lines 
earlier and/or on first read create a simpler representation for 
comments while maintaining the line counts?  I've done no digging in the 
parser code yet myself with these ideas in mind.

-----------------------------
Code below run and timed on Ubuntu linux with the command:

/usr/bin/time povray the.pov


//--- Equivalent coding using macro defining function
//    for faster parsing. Demo slow comments. WFP Oct 6, 2017
#version 3.7;
//------------------------------------------
// SDL for spiral distance field
// Bill Walker - October 2017
//------------------------------------------
global_settings {assumed_gamma 1}

#include "colors.inc"
#include "consts.inc"

light_source { <-1,8,2> color White}

camera {
  orthographic
  location <0, 0, -100>
  look_at  <0, 0,   0>
  right x*image_width
  up y*image_height
}

plane {z, 1 pigment {checker Gray20, White} scale 10}
light_source {<0, 10, -500> color White*0.5}

//----- This all macro method OK, but about 225% slower than
//      than using a single macro call to create a function.
#macro Spiral (X, Y)
  #declare a=1;
  #declare b=0.5;
  // calculate the target radius and theta
   #local R = sqrt (X*X +Y*Y);

    // early exit if the point requested is the origin itself
  // to avoid taking the logarithm of zero in the next step
  #if (R = 0)
   0
  #else
   #local T = atan2 (Y, X);
   // calculate the floating point approximation for n
   #local n = (log(R/a)/b - T)/(tau);

   // find the two possible radii for the closest point
   #local upper_r = a * pow(e, b * (T + tau*ceil(n)));
   #local lower_r = a * pow(e, b * (T + tau*floor(n)));

   // return the minimum distance to the target point
   min (abs(upper_r - R), abs(R - lower_r))
  #end
#end

// - Yes this a fairly straight map and it could be further optimized.
#macro DefFN0to1Val(A,B)
     #declare FNR  = function (x,y,z,X,Y) { sqrt(X*X+Y*Y) }
     #declare FNT  = function (x,y,z,X,Y) { atan2(Y,X) }
     #declare FNn  = function (x,y,z,X,Y) {
         (log(FNR(x,y,z,X,Y)/A)/B - FNT(x,y,z,X,Y))/tau
     }
     #declare FNuR = function (x,y,z,X,Y) {
         A * pow(e,B * (FNT(x,y,z,X,Y) + tau*ceil(FNn(x,y,z,X,Y))))
     }
     #declare FNlR = function (x,y,z,X,Y) {
         A * pow(e,B * (FNT(x,y,z,X,Y) + tau*floor(FNn(x,y,z,X,Y))))
     }
     #declare FNspiral = function (x,y,z,X,Y) {
         min(abs(FNuR(x,y,z,X,Y)-FNR(x,y,z,X,Y)),
             abs(FNR(x,y,z,X,Y)-FNlR(x,y,z,X,Y))
         )
     }
     #declare FN0to1Val = function (x,y,z,X,Y) {
         min(1,FNspiral(x,y,z,X,Y)/255)
     }
#end
DefFN0to1Val(1.0,0.5)

// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
// Adding this comment to most nested for loop 10 times is >20% slower.
#for (i, -image_width/2, image_width/2)
  #for (j, -image_height/2, image_height/2)
    box { -0.5,0.5 translate <i,j,0>
          pigment {color rgb FN0to1Val(0,0,0,i,j)}
        }
  #end
#end

// Options are NOT commented / un-commented in the nested for loops
// above because longish comments there cause a parser slow down.
//
//---  (slower) 10.80user 0.15system 0:11.50elapsed
// box { -0.5,0.5 translate <i,j,0>
//       pigment {color rgb min(1, Spiral(i,j)/255)}
//     }
//---  (faster) 3.41user 0.17system 0:04.13elapsed
// box { -0.5,0.5 translate <i,j,0>
//       pigment {color rgb FN0to1Val(0,0,0,i,j)}
//     }

#error "Stop after parse for time comparison"


Post a reply to this message

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