26 msdn magazine Data Points
cylinders. It’s the CylinderCalculator.UpdateCylinders method that’s
called to start the PSI calculation for a set of cylinders. It iterates
through each cylinder in the set and performs the appropriate cal-
culations. Note that one of the methods, GetAreaForCalculation,
is dependent on the cylinder type because I calculate the area of
the cylinder using the appropriate formula.
Data Focus and Faster Processing with F#
Finally, I discovered that F#, thanks to its natural inclination for
manipulating data, provides a much better solution than evaluating
one formula at a time.
At the introductory session on F# given by Reese, I explained
this problem, which had been nagging at me for so many years,
and asked if a functional language could be used to solve it in a
more satisfying way. She confi rmed that I could apply my complete
calculation logic on a full set and let F# derive the PSIs for many
cylinders in parallel. I could get the client-side functionality and a
performance boost at the same time.
Th e key for me was to realize I could use F# to solve a particular
problem in much the same way I use a stored procedure—it’s
simply another tool in my tool belt. It doesn’t require giving up
my investment in C#. Perhaps there are some who are just the
reverse—writing the bulk of their apps in F# and using C# to attack
particular problems. In any case, using the C# CylinderCalculator
as a guide, Reese created a small F# project that did the task and I
was able to replace a call to my calculator with a call to hers in my
tests, as shown in Figure 3.
If, like me, you’re new to F#, you might look only at the amount of
code and see no point in choosing this route over C#. Upon closer
examination, however, you may appreciate the terseness of the
language, the ability to defi ne the formulas more elegantly and, in
the end, the simple way I can apply the calculatePsi function Reese
defi ned to the array of cylinders I passed to the method.
Th e terseness is thanks to the fact that F# is better designed to
perform math functions than C# is, so defi ning those functions
is more effi cient. But besides the geek appeal of the language, I
was interested in performance. When I increased the number of
cylinders per set in my tests, initially I did not see a performance
improvement over C#. Reese explained that the test environment
is more expensive when using F#. So I then tested the performance
in a console app using the Stopwatch to report the time passed.
Th e app built up a list of 50,000 cylinders, started the Stopwatch,
passed the cylinders to either the C# or F# calculator to update the
PSI value for each cylinder, then stopped the Stopwatch when the
calculations were complete.
In most of the cases, the C# process took about three times
longer than the F# process, although about 20 percent of the
time C# would beat F# by a small margin. I can’t account for the
oddity, but it’s possible there’s more I need to understand to per-
form truer profi ling.
Keeping an Eye Out for Logic That Begs
for a Functional Language
So while I have to work at my F# skills, my new understanding is
going to apply nicely to apps I already have in production as well
as future apps. In my production apps, I can look at business logic
I’ve relegated to the database and consider if an app would benefi t
from an F# replacement. With new apps, I now have a keener
eye for spotting functionality I can code more effi ciently with F#,
performing data manipulation, leveraging strongly typed units of
measurement and gaining performance. And there’s always the fun
of learning a new language and fi nding just the right scenarios for
which it was built! Q
JULIE LERMAN is a Microsoft MVP, .NET mentor and consultant who lives in the
hills of Vermont. You can fi nd her presenting on data access and other Microsoft
.NET topics at user groups and conferences around the world. She blogs at
thedatafarm.com/blog and is the author of “Programming Entity Framework”
(2010) as well as a Code First edition (2011) and a DbContext edition (2012), all
from O’Reilly Media. Follow her on Twitter at twitter.com/julielerman and see
her Pluralsight courses at juliel.me/PS-Videos.
THANKS to the following technical expert for reviewing this article:
Rachel Reese (Firefl y Logic)
module calcPsi =
let fourFourEightFormula WA WB = 3.14159*((WA+WB)/2.)/2.*((WA+WB)/2./2.)
let sixSixTwelveFormula WA WB = 3.14159*((WA+WB)/2.)/2.*((WA+WB)/2./2.)
let threeThreeSixFormula (WA:float) (WB:float) = WA*WB
let twoTwoTwoFormula WA WB = ((WA+WB)/2.)*((WA+WB)/2.)
// Ratio function
let ratioFormula height widthA widthB =
if (height > 0. && (widthA + widthB > 0.)) then
Some(Math.Round(height / ((widthA + widthB)/2.), 2))
else
None
// Tolerance function
let tolerance (ratioValue:float option) = match ratioValue with
| _ when (ratioValue.IsSome && ratioValue.Value > 1.94) -> 1.
| _ when (ratioValue.IsSome && ratioValue.Value < 1.) -> 1.
| _ -> 0.979
// Update the PSI, and return the original cylinder information.
let calculatePsi (cyl:CylinderMeasurement) =
let formula = match cyl.CylinderType with
| CylinderType.FourFourEightCylinder -> fourFourEightFormula
| CylinderType.SixSixTwelveCylinder -> sixSixTwelveFormula
| CylinderType.ThreeThreeSixCylinder -> threeThreeSixFormula
| CylinderType.TwoTwoTwoCylinder -> twoTwoTwoFormula
| _ -> failwith "Unknown cylinder"
let basearea = formula cyl.WidthA cyl.WidthB
let ratio = ratioFormula cyl.Height cylinder.WidthA cyl.WidthB
let tFactor = tolerance ratio
let PSI = Math.Round((float)cyl.LoadPounds/basearea/1000. * tFactor, 2)*1000.
cyl.Psi <- PSI
cyl
// Map evaluate to handle all given cylinders.
let getPsi (cylinders:CylinderMeasurement[])
= Array.Parallel.map calculatePsi cylinders
Figure 3 The F# PSI Calculator
I could get the client-side
functionality and a performance
boost at the same time.