Lesson learned: avoid more than ~10 Species.AuxParm or Ecoregion.Auxparm in your extension to keep a reasonable performance.

48 views
Skip to first unread message

amgde...@gmail.com

unread,
Nov 12, 2015, 7:32:34 AM11/12/15
to LANDIS-II Developers
I have been struggling to get a reasonable performance time for 'my' extension PnET-Succession for years but I never seemed to find the real bottleneck. 

In the past, any considerable improvement in model performance usually indicated some major bug, like all the tree cohorts were deleted or some functions were simply not being called.

Even though in theory it is still possible that I am overlooking something like that, it seems that this time, all the computations are fine the performance of PnET-Succession is at least 5 times as fast. 

So I am confident enough to share this with the community. What I did is the following:

PnET-Succession adds ~40 species parameters and ~5 ecoregion parameters to landis species parameters such as longevity or shade tolerance or ecoregion parameters such as name or mapcode.

They were implemented as 

static public Landis.Library.Parameters.Species.AuxParm<T> parameter
static public Landis.Library.Parameters.Ecoregion.AuxParm<T> parameter

I figured out that something was wrong with them when I applied a stopwatch to check the time consumption to retrieve one of these parameters. When I did something like:

=========================================
Stopwatch sw = new Stopwatch()
sw.start();

for(int i=0;i<10000;i++)
{
   double t = PnETparameter[species];
}
sw.stop();
Console.WriteLine(elapsedtime);
=========================================
and compare this with 
=========================================
Stopwatch sw = new Stopwatch()
sw.start();

for(int i=0;i<10000;i++)
{
   double t = species.LandisParameter;
}
sw.stop();
Console.WriteLine(elapsedtime);
=========================================
I found that the latter is about 2/3 times faster. 

So instead of using the species.auxparm, I created an interface ISpeciesPNET

=========================================
 public interface ISpeciesPNET : Landis.Core.ISpecies 
{
    ...parameter1
    ...parameter2
    ...etc
}
=========================================
a class SpeciesPnET 
=========================================
public class SpeciesPnET : ISpeciesPNET
{
    public static Dictionary<ISpecies, ISpeciesPNET> AllSpecies;

    private static Landis.Library.Parameters.Species.AuxParm<float> parameter1;


    private ISpecies _species;    // the 'original' landis species
    private _parameter1
    private _parameter2;

   public Parameter1
   {
        get{ return _parameter1}
   }

   public LandisParameter1
   {
       get{ return _species.parameter1}
    }

   public void Initialize() 
   {
        
       parameter1 = ....(initialize)// Initialize static species.auxparm

      AllSpecies = new Dictionary<ISpecies, ISpeciesPNET>();
      
       foreach (ISpecies spc in PlugIn.ModelCore.Species)
       {
               AllSpecies.Add(spc, new SpeciesPnET(spc));
        }


        
    }

     public SpeciesPnET(ISpecies species) // Constructor of PnETSpecies
     {
           this._parameter1  =parameter1[species]
     }
    
}
=========================================================
This allowed me to change all the looping over the species in the code 

=========================================================
foreach(ISpecies spc in Plugin.ModelCore.species)
{

}
=========================================================

to 

=========================================================
foreach(IPnETSpecies spc in PnETSpecies.AllSpecies.Values)
{

}
=========================================================

and all ISpecies to ISpeciesPnET

To my surprise, my code became was much more than 2/3 times faster, but more in the range 5 to 10 times. 

I haven't figured out why this AuxParm is a problem. I remember that at some point I tried to replace all my auxparm with static dictionaries (i.e. Dictionary<species, float> PnETparameter = something) rather than auxparm and it didn't help. So my understanding is that nothing is wrong about the coding of AuxParm in the model core, but it just takes more time to retrieve a value from an array of values than a class member from a class that is already there.

I don't know if this is a similarly big deal for extensions like century, bfolds, that may use fewer parameters, but it sure is worth considering if you are working on an extension that uses species or ecoregion parameters!

Arjan




Reply all
Reply to author
Forward
0 new messages