Difference between revisions of "Contrib equationReader/Efficiency"

From OpenFOAMWiki
Line 1: Line 1:
 
''How fast is '''equationReader'''?''
 
''How fast is '''equationReader'''?''
  
The bottom line is: I don't know yet, but am planning on testing it soonOnce the test is done, I'll put the results (bad or good) here.  I believe that '''equationReader''' will prove to be a little slower than a hard-coded solution, but not too slow to offset the benefits gained in flexibility.
+
Preliminary results are in, and they are a little disappointing.  '''equationReader''' seems to take between 10 to 20 times longer than a hard-coded solution, depending on the equation involved, and the type of evaluation.  I will upload results of my experiment when I have the time.  If you don't plan on using it within your main solver loop, this isn't a big deal, but if you do, the question is: do the benefits of flexibility outweigh the cost?
  
== Parsing and evaluating ==
+
Also, expect '''equationReader''' to get faster with future updates.  I used a ''function-pointer'' framework, and should have used a ''function object''... or maybe even defined an entire set of derived objects, one for each operation type, and inlined its <tt>evaluate</tt> function.
 +
 
 +
== Efficiency discussion ==
 +
 
 +
=== Parsing and evaluating ===
 
There is a difference between ''parsing'' and ''evaluating''.  When the equation is first read, it is a ''human-readable'' string expression.  '''equationReader''' translates the ''human-readable'' form into an ''operation list''.  This is ''parsing''.  To calculate the result, '''equationReader''' does a <tt>forAll(operations, i)</tt>.  This is ''evaluating''.
 
There is a difference between ''parsing'' and ''evaluating''.  When the equation is first read, it is a ''human-readable'' string expression.  '''equationReader''' translates the ''human-readable'' form into an ''operation list''.  This is ''parsing''.  To calculate the result, '''equationReader''' does a <tt>forAll(operations, i)</tt>.  This is ''evaluating''.
  
 
''Parsing'' happens only once, and is slow.  ''Evaluating'' happens at every cell index, at every timestep (or however you've used it), and it is fast.
 
''Parsing'' happens only once, and is slow.  ''Evaluating'' happens at every cell index, at every timestep (or however you've used it), and it is fast.
  
== Burn all conditionals ==
+
=== Burn all conditionals ===
 
Conditionals are slow.  In the framework of '''equationReader''', one <tt>if</tt> or <tt>switch</tt> slows a single equation operation by about 25%.  A word comparison - that slows things by over 1600%.
 
Conditionals are slow.  In the framework of '''equationReader''', one <tt>if</tt> or <tt>switch</tt> slows a single equation operation by about 25%.  A word comparison - that slows things by over 1600%.
  
 
'''equationReader''' has a design philosophy: put no conditionals in the way of an <tt>evaluate</tt> function.  To achieve this I implemented a ''function-pointer'' framework.
 
'''equationReader''' has a design philosophy: put no conditionals in the way of an <tt>evaluate</tt> function.  To achieve this I implemented a ''function-pointer'' framework.
  
== Function-pointers ==
+
=== Function-pointers ===
 
Apparently in C++ you can have pointers to functions.  The syntax looks a little odd:
 
Apparently in C++ you can have pointers to functions.  The syntax looks a little odd:
  
Line 28: Line 32:
 
Now, there's not a single conditional encountered during an ''evaluate'' function call.  Even all the <tt>debug</tt> switches have been converted to ''function-pointers''.
 
Now, there's not a single conditional encountered during an ''evaluate'' function call.  Even all the <tt>debug</tt> switches have been converted to ''function-pointers''.
  
== Function objects ==
+
=== Function objects ===
 
Apparently ''function-pointers'' are old-school, and the next best thing are ''function-objects'', or "''functors''".  ''Function-objects'' are better because they can be inlined, unlike ''function-pointers''.  Well, I didn't get the memo until it was too late for this version.  Maybe in another version.
 
Apparently ''function-pointers'' are old-school, and the next best thing are ''function-objects'', or "''functors''".  ''Function-objects'' are better because they can be inlined, unlike ''function-pointers''.  Well, I didn't get the memo until it was too late for this version.  Maybe in another version.

Revision as of 13:38, 16 September 2011

How fast is equationReader?

Preliminary results are in, and they are a little disappointing. equationReader seems to take between 10 to 20 times longer than a hard-coded solution, depending on the equation involved, and the type of evaluation. I will upload results of my experiment when I have the time. If you don't plan on using it within your main solver loop, this isn't a big deal, but if you do, the question is: do the benefits of flexibility outweigh the cost?

Also, expect equationReader to get faster with future updates. I used a function-pointer framework, and should have used a function object... or maybe even defined an entire set of derived objects, one for each operation type, and inlined its evaluate function.

1 Efficiency discussion

1.1 Parsing and evaluating

There is a difference between parsing and evaluating. When the equation is first read, it is a human-readable string expression. equationReader translates the human-readable form into an operation list. This is parsing. To calculate the result, equationReader does a forAll(operations, i). This is evaluating.

Parsing happens only once, and is slow. Evaluating happens at every cell index, at every timestep (or however you've used it), and it is fast.

1.2 Burn all conditionals

Conditionals are slow. In the framework of equationReader, one if or switch slows a single equation operation by about 25%. A word comparison - that slows things by over 1600%.

equationReader has a design philosophy: put no conditionals in the way of an evaluate function. To achieve this I implemented a function-pointer framework.

1.3 Function-pointers

Apparently in C++ you can have pointers to functions. The syntax looks a little odd:

       void (Foam::equationReader::*reportScalarOperationFunction_)
       (
           const label&,
           const label&
       ) const;

That's not a function - it's a pointer. You don't see these too often because C++'s virtual functions do the same thing - only you need a hierarchy of derived classes. In theory, virtual functions have slightly more overhead than function-pointers because each virtual function call requires a hash-table look-up, unlike the direct use of function-pointers.

Anyway, these function pointers can all be assigned during parsing, and called directly during evaluation. Essentially this moves all conditionals into the parser, and their result is permanently remembered. In short: these make equationReader faster.

Now, there's not a single conditional encountered during an evaluate function call. Even all the debug switches have been converted to function-pointers.

1.4 Function objects

Apparently function-pointers are old-school, and the next best thing are function-objects, or "functors". Function-objects are better because they can be inlined, unlike function-pointers. Well, I didn't get the memo until it was too late for this version. Maybe in another version.