cypherkey/RaspberryPi.Net — pull request #10 by Captain-Coder (2013-01-08 15:43:49+00:00)
Title: Performance and OO-ness

Hi,

I was looking for a C# Pi GPIO implementation and came across this project, I needed far better performance on the GPIOMem and saw some room for improvements. So (for the first time ever in my life) I saw an opportunity to contribute back to a project (hope I did the Git part ok, have never used it before).

I managed about a 146x speedup on writes and 250.9x on reads. The GPIOFile has also improved from restructuring some OO and c# language aspects of the project, but since I didn't need it I decided to not optimize it. (You can keep the relevant files open instead of using File.ReadAllText and WriteAllText, they are FUBAR slow. But this would slightly change the semantics of what is happening and eliminate sharing of the GPIO pins among multiple processes at the same time) Most ppl needing performance will look for the Mem variant anyway.

Here is a short list of how/why I changed stuff if you want to know:
- Moved to .Net 2.0, no reason to use 4.0. Eliminates a bunch of dependencies.
- Corrected usings (no .Linq in 2.0)
- Corrected .ctor overloading and overriding. GPIO... ctor(x) now goes to GPIO... ctor(x,y,z), and then GPIO.ctor(x,y,z). Such that the super class may perform init in the 3 parameter .ctor regardless of what ctor was initially called, this unduplicates code. The GPIO base class 1 and 2 parameter .ctors are thus redundant and removed.
- Modeled everything to use GPIO instances, and not static methods. Much more OO-ish and much less C-ish. The static methods are now based on the instances vs. the other way around. (This touches upon personal style, this can be changed around if needed, but using instances is more natural imo)
- Corected use of the IDisposed interface (no need to repeat this on sub classes btw, but admittedly doesn't do harm either). Member access now throws an exception after the GPIO instance is disposed.
- Defining the GPIOPins enum as derived from uint allows it to be directly used in the dll imports, saves on the cast calls in code (doesn't make much run time difference though)
- Using bool directly in dllimport is no problem, at run time (any sane platform) will use native int for them. Eliminates a bunch of == 0? .. : .. expressions.
- Added factory method (CreatePin) that creates GPIOMem if possible and GPIOFile as fallback. This allows the static read and write methods to go to GPIO, and not have a static GPIOMem.Write() and separate static GPIOFile.Write()
- In debug builds, factory method falls back to GPIODebug if GPIOFile also fails
- Corrected DirectionEnum to read GPIODirection. To include Enum in the name of an enum is bad style imo, it serves no purpose (or atleast is not consistent with naming the other enum GPIOPins, that should then be PinsEnum).
- I also saw no reason GPIODirection and GPIOPins should be nested in GPIO (GPIO.GPIO... adds no readability, and makes you type and extra GPIO. every time), but this als is a bit of sylistic difference.
- Static are useless in GPIODebug, there are no overrides (in c#) for static methods, so a base class can never call these, and manually calling GPIODebug.CleanUp() serves little purpose.
- Finalizer behaviour can be generalized to GPIO class, makes sense since it is also the class declaring the IDisposabe.
- Static class initializer is better than using static _initialized variable. The runtime ensures thread safety and guarantees it runs exactly only once before using the class. Perfect place for bcm2835_init().
- Corrected dictionary accesses to read .ContainsKey() instead of .Keys.Contains(). This last form is the only v4.0 framwork feature used in the project. This statement acutally causes a new object to be created in the .Keys getter, and an extension method to run on that object. The dictionary provide a convenient (and v2.0 compatible) .ContainsKey() method.
- Added xml comments on all public items needing them in GPIO{,Mem,File} classes

Using a simple app that just repeats writes/read I got the following timings on the Pi.
Timings:
Original:
Mem IO:
Time for 200k writes: 21764.569ms
Time for 200k reads: 19576.452ms

File IO:
Time for 200k writes: 330967.659ms
Time for 200k reads: 366102.993ms

Modified:
Mem IO:
Time for 200k writes: 149.032ms
Time for 200k reads: 78.91ms

File IO:
Time for 200k writes: 285773.429ms
Time for 200k reads: 306495.585ms