|
|
YOUR FEEDBACK
|
TOP MICROSOFT .NET LINKS Performance
F# on a Virtual Super Computer
Understanding grid computing
By: Chad Albrecht
May. 10, 2006 10:00 AM
Digg This!
Page 2 of 2
« previous page
Listing 5 defines a module called pb and creates a prototype for the CalculatePiDigits function. To a C# developer this looks really strange! It's essentially saying CalculatePiDigits takes an integer as an input and returns a string. The strangeness will continue as we look at the meat of the calculation class. But before I got into full-fledged F# development, I wanted to make sure it would run on the grid. I added the code in Listing 6 to pb.fs as a test. From Listing 6, we see that we begin by declaring the implementation of module pb followed by opening System. Opening in F# is equivalent to the using statement in C#. Next we define a function CalculatePiDigits using the let syntax. CalculatePiDigits will explicitly take an integer, n, as an input argument and implicitly return a string. This is a key and unique feature of F#, the ability to mix explicit and implicit typing. To call the F# code contained in the pb module, I modified the Start method of the PiCalcGridThread class and included the Microsoft.FSharp namespace statement in Listing 7. After compiling the solution and running it, everything appeared to work but it showed Pi as 3 with no numbers after the decimal. A little digging into the Alchemi user guide and the log file revealed that my F# based plouffe_bellard.dll wasn't being sent to the executor. To overcome that I had to add the lines in Listing 8 to the startup code. Adding file dependencies to the GApplication manifest let Alchemi distribute these files with the tasks. As a result the F# code can run on remote machines. Alternatively you can specify the standalone compiler option to compile these into a single DLL. If you use this option the dependency code looks like Listing 9. After this was done, the job ran. Figure 4 shows the result of the first test run. I could now move on to rewriting the C# Plouffe-Bellard calculation class in F#. As a result of a lot of reading and instant messaging with optionsScalper, the first working F# Plouffe-Bellard module is in Listing 10. To compare the differences between C# and F# take a look at Listing 2 and Listing 10. As you can see the use of F# mutables helps in this endeavor immensely! Mutables in F# are modifiable, by reference, values that are implicitly dereferenced. Mutables use the local mutation assignment operator: id exp r. As you become a more experienced F# developer, you'll find yourself using mutables less. However, mutables are indispensable when doing direct C# to F# conversion as you'll see.
Going All In You'll see the similarities between Listing 4 and Listing 12. The differences include:
Constructors are implemented using the new keyword and can be defined as using the keyword to access the base class. The use of the base class is required to implement method overloading using the override keyword. Finally, I implemented the main code in F# in Listing 13. In this code segment, some of the issues I ran into with F# are:
An example would be type car is derived from type vehicle then car needs to be up cast to a vehicle and vehicle needs to be down cast to a car.
More Advanced F#
The hardest thing for me to adapt to has been the use of let-bound functions. While it reminds me of the use of macros in other languages, I'm still not used to seeing them inline with the rest of the code. You can see an example of this in Listing 14. The declaration of check in the is_prime function took me a while to understand.
Performance Comparison Without any optimization the F# implementation took 453 ms. C# finished up in 62 ms. Turning optimization on caused the C# results to drop to 46 ms. And, most shocking of all, the F# was dead even at 46 ms! Apparently, the F# compiler does an excellent job optimizing the code. Continuing on, I ran the entire C# and F# grid applications against one another. Without any optimization the C# code took 3.9 seconds to calculate 100 digits of pi while F# only took 2.8 seconds. Optimizing this application showed that F# would finish in 1.5 seconds while C# would not complete any faster than 2.6 seconds. (Table 1) A look at the IL showed the reason for the difference. From the beginning of Listing 10 you can see the use of the inline keyword, not available in C#, this lets F# squeeze out a little more performance. If you're not familiar with the concept of inline functions, let me explain. Each time a function is called arguments need to be pushed onto the stack or stored in registers. When the function is done results need to be pushed onto the stack, all this takes time. The cost of these calls can be avoided by moving the function into the body of the calling code. The C# compiler doesn't support the inlining, instead, it relies on the JIT to do this. Here's a simplified list of heuristics that the JIT uses to decide if a method should be inlined:
Conclusion Resources
Page 2 of 2 « previous page MICROSOFT .NET LATEST STORIES
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK BREAKING NEWS FROM THE WIRES
|
||||||||||||||||||||||||||||||||||