   Using EffectHandles, quick analysis and utility code
Edited: Fixed EffectHandlerProxy Reset method, didn't work because of Hashtable synchronization issues._____________________________________I was just playing around with my cloud generator and I decided that I should be setting the effect parameters using EffectHandles instead of name strings. Everyone says these are more efficient, but there seem to be no solid figures on exactly how much more efficient. So, I thought my results might be interesting for other (M)DX developers. I also developed a little utility class to facilitate using EffectHandles, which you can find below.AnalysisWhen I started replacing the SetParameter calls to use handles, I noticed quite an impressive speed gain as I went along. After I replaced all 26 SetParameter calls, which are called each frame, my average framerate went up from 950 to 1100 fps. That's a 16% gain, by only switching from strings to handles!My EffectHandleProxy class below uses a Hashtable to look up handles for parameter names, so you can keep setting the parameters using the hassle-free, descriptive name strings, while taking advantage of the efficiency of the handles. I'm a bit puzzled why the DirectX team doesn't incorporate this into the Effect.SetParameter method directly. At first I thought the overhead of the Hashtable lookups might be just as inefficient as calling SetParameter using the name string, but the 16% fps gain already includes this overhead.EffectHandleProxy utility classThe EffectHandleProxy class allows you to easily use handles for setting effect parameters, with a minimum of recoding. You can create an instance of the EffectHandleProxy alongside an effect. When you create the Effect object during a device reset (or initialization), simply call the Reset(Effect) method on the proxy object. This will bind the proxy to the effect and it will recreate any handles that were used on the effect before. Note that you need one proxy per effect and that you can keep reusing this proxy for the same effect over a device reset, using the Reset(Effect) method.Actually setting the parameters is easy as well. You simply replace effect.SetParameter with proxyObject.SetParameter and that's that. The proxy supports all overloads of the Effect method (plus an extra one for structs) and it will automatically create a new EffectHandle for any parameter name that hasn't been used before.The code (or download it without layout quirks):[source lang=c#]using System;using System.Collections;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;using System.Runtime.InteropServices;namespace Clouds.Util{/// <summary>/// The EffectHandleProxy caches effecthandles to efficiently set /// effect parameters while using the name strings in the main app./// </summary>public class EffectHandleProxy{private Hashtable effectHandles = new Hashtable();private Effect effect;public EffectHandleProxy(){}public void Reset( Effect effect ){this.effect = effect;effectHandles.Clear();}public void SetValue( string name, BaseTexture val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, bool val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, bool[] val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, ColorValue val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, ColorValue[] val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, GraphicsStream val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, int val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, int[] val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, Matrix val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, Matrix[] val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, float val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, float[] val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, string val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, Vector4 val ){effect.SetValue( GetHandle( name ), val );}public void SetValue( string name, Vector4[] val ){effect.SetValue( GetHandle( name ), val );}public unsafe void SetValue( string name, void* val, int len ){effect.SetValue( GetHandle( name ), val, len );}public unsafe void SetStructure(string name, object objStr){int nSize = Marshal.SizeOf(objStr);IntPtr unmanagedPointer = Marshal.AllocHGlobal(nSize);Marshal.StructureToPtr(objStr, unmanagedPointer, true);effect.SetValue(GetHandle(name), unmanagedPointer.ToPointer(), nSize);Marshal.FreeHGlobal(unmanagedPointer);}private EffectHandle GetHandle( string name ){if (!effectHandles.ContainsKey(name)){effectHandles.Add( name, effect.GetParameter( null, name ) );}return (EffectHandle)effectHandles[name];}}}[Edited by - remigius on December 8, 2005 1:18:01 PM]
Quote:Original post by remigiusMy EffectHandleProxy class below uses a Hashtable to look up handles for parameter names, so you can keep setting the parameters using the hassle-free, descriptive name strings, while taking advantage of the efficiency of the handles. I'm a bit puzzled why the DirectX team doesn't incorporate this into the Effect.SetParameter method directly. At first I thought the overhead of the Hashtable lookups might be just as inefficient as calling SetParameter using the name string, but the 16% fps gain already includes this overhead.This is pretty nifty.  However, did you calculate what FPS you get without using a hashmap and just using the traditional handles?  I have heard that some implementations of hashmaps are pretty inefficient for accessing elements (if their size is say < 1000), so that could be a factor.I like it.  It is always kinda annoying prototyping shaders and having to go the whole way and make dedicated handle members, just so you can find its performance accurately.   ;  Good to see it was interesting enough for a sticky :)I'm using the Hashtable implementation from the .NET Runtime, so I assumed it was pretty optimized for all sizes of datasets. To be sure, I replaced 13 of the 26 SetParameter calls with versions that directly used instance variable EffectHandles. The improvement over the Hashtable approach was marginal, around 3 fps and even that may well fall within the fps variations caused by the random noise textures used in shader.Anyway, verifying this highlighted another benefit of the proxy class, as it's very easy to find out which effect handles are set at runtime, by simply checking the keys in the Hashtable. This can be used to generate variable declarations for the handles for example (see below) and it may be useful for debugging.Code which can be inserted in the class to generate handle declarations and assignments:[source lang=c#]public void PrintHandleDeclaration(){foreach( string name in effectHandles.Keys ){System.Diagnostics.Debug.WriteLine(string.Format("private EffectHandle {0}Handle;", name));}}public void PrintHandleAssignment(string effectName){foreach( string name in effectHandles.Keys ){System.Diagnostics.Debug.WriteLine( string.Format("{0}Handle = {1}.GetParameter(null, \"{2}\");",name, effectName, name));}};  Interesting!  I was just thinking about this today, as the Effect wrapper class in my engine just sets the effect parameters with strings.  I thought the same thing - that using a hashtable to look up parameters myself would just be moving the lookup from the ID3DXEffect interface to my own code, so I didn't think I'd gain any performance.  Well, looks like I was wrong, so I'm going to go do that now.   ;  Please do post your results, I'm interested to see if you get a similar performance gain when using normal (C++) DirectX.   ;  Odd.  I get no change in speed.Perhaps it could be how my effect system is implemented.  I'm already caching the parameter values using a hashtable, so the only time the parameters are actually set to the effect interface is once a frame.  So it's already pretty optimized that way.  However, when the parameters are set to the effect interface, it basically just loops through a regular array of handles - no hashtable used for actually retrieving the handles corresponding to the parameters.So it could be one of two things.  One, the Effect interface in MDX might not be implemented as efficiently as in UDX.  This is entirely possible, and it might just be a case of some safety or testing code left in the MDX Effect interface while it's under development.  The other thing that it could be is the number of parameters and how frequently they are set.  The effect I was using only had about 8 parameters.  This is something I wanted to ask you - for your 26 SetParameter calls, are they all called every frame?  Of course, it could be a third thing, which is that I messed up somewhere ;)I'll see if I can get a more parameter-happy effect to see if I can get some more conclusive results.   ;  Hmmm, strange indeed...The 26 parameters I use are all set once every frame and they're completely different parameters, spread over 3 effects. About 8 are texture setters, 6 for matrices, 4 Vector4's and that leaves 8 floats. I don't know if that makes any difference, but I thought I'd mention it for completeness.Quote:Of course, it could be a third thing, which is that I messed up somewhere ;)Door #4 might also be a possibility, that I messed up :) Anyway, in previous test runs, I my framerate was always 950 on average. For the test, I ran the app at least 10 times from VS.NET using debug -without DX debug runtimes- for both the string and EffectHandle implementation (since I couldn't believe it myself). When I run it now, with a full taskbar (ie. with too many programs open), it still averages to 1080 fps.Could the efficiency of the SetParameter method be influenced by the video card?   ;  I just tried an effect with 44 parameters, and made sure they were all being set to the effect every frame.  No speed difference between names and handles.As for your last statement, I kind of wonder.. what shader model are you using, anyway?  I've just got an SM2 card.   ;  I'm also just using sm2 on an X850PE. My last statement was just a hunch, since I can reproduce my results every time, with the same speed gain. So there's three possibilities; either my video card is very sensitive about EffectHandles, or the MDX implementation of the effects framework could use some optimization or a miracle has happened... with MDX, the last one seems unlikely ;)Anyway, I'll write some test projects tomorrow, focussing solely on setting the effect parameters. It might be that the rest of the rendering process of my cloud generator is somehow influencing the results... I didn't change anything in the rest of the code though, but I'll get to the bottom of this :)   ;  I've run some more tests and for MDX the performance gain is there and significant. I wrote a new test app that sets 10 matrices, 10 floats and 10 float4's on an effect. It doesn't do any rendering at all with this effect, only setting these effect parameters and calculating framerates. The average framerates are calculated over a 10 second period and for the comparison I took the average framerate in the 2nd period (from 10 to 20 secs), so the app initialization doesn't interfere. The test was conducted by having the EffectHandlerProxy simply return the name string from the GetHandle method, instead of a handle, automatically using the appropriate Effect.SetParameter overload. This was easiest to implement, but it also shouldn't have any influence on the test. In code:[source=c#]// normal proxy functionalityprivate EffectHandle GetHandle( string name ){if (!effectHandles.ContainsKey(name)){effectHandles.Add( name, effect.GetParameter( null, name ) );}return (EffectHandle)effectHandles[name];}// returning the name instead of the handle, for string name settingprivate string GetHandle( string name ){return name;}In this set up, the average framerate over 10 secs was 4548 fps using the handles, and 3928 fps using name strings. That's again a 16% gain, consistent with previous results. So my testing would suggest that handles give indeed a 16% better result than name strings, at least for Managed DirectX on an ATI Radeon X850PE.For more details, download the test project (~280KB ZIP)If someone can test this on other (nvidia) video cards, that might give us some more insight in whether or not the results are dependant on the video card.   