Page 30 - MSDN Magazine, September 2019
P. 30

Figure 2 Benchmarking a Parsing Routine with and without Span<'T>
The module called Parsing contains two functions that split a string by a given delimiter, returning a tuple representing each half of the string. However, one called getNumsFaster uses both Span<'T> and struct tuples to eliminate allocations. As you’ll see, the results are quite profound.
I’ll run the benchmark to produce the results:
dotnet run -c release
This will produce results that can be shared as markdown, HTML or other formats.
When building something like a Web service that has high traffic, performance matters!
I ran this benchmark on my laptop with the following hardware and runtime environment:
• BenchmarkDotNet v0.11.5
• macOS Mojave 10.14.5 (18F132) [Darwin 18.6.0]
• Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU,
8 logical and 4 physical cores
• .NET Core SDK=3.0.100-preview5-011568 (64-bit)
The results are shown in Figure 3.
Impressive, right? The getNumsFaster routine not only allocated 0 additional bytes, it also ran 33 percent faster!
If you’re still not convinced that this matters, imagine a scenario where you need to perform 100 transformations on that data, and it all had to happen on the hot path for a highly trafficked Web ser- vice. If that service saw requests on the order of millions per second, you’d be looking at a pretty severe performance problem if you’re allocating in each transformation (or several of them). However, if you use types like Span<'T> and struct tuples, all of those alloca- tions can often disappear. And, as the benchmark shows, it can also take significantly less wall-clock time to execute a given operation.
Wrapping up
As you can tell, there’s quite a lot you can do with F# on .NET Core! It’s very easy to get started, and easy to work your way into building Web applications, too. Moreover, the ability to use constructs like Span<'T> means that F# can be used for perfor- mance-sensitive work, as well.
F# is only getting better on .NET Core, and the community con- tinues to grow. We’d love it if you’d join the community in building some of the next great things for F# and .NET! n
PhilliP Carter is a member of the .NET team at Microsoft. He works on the F# language and tools, F# documentation, the C# compiler and .NET project
integration tooling for Visual Studio.
thanks to the following technical expert for reviewing this article:
Dustin Moris Gorski
open System
open BenchmarkDotNet.Attributes open BenchmarkDotNet.Running
module Parsing =
/// "123,456" --> (123, 456)
let getNums (str: string) (delim: char) =
let idx = str.IndexOf(delim)
let first = Int32.Parse(str.Substring(0, idx)) let second = Int32.Parse(str.Substring(idx + 1)) first, second
let getNumsFaster (str: string) (delim: char) = let sp = str.AsSpan()
let idx = sp.IndexOf(delim)
let first = Int32.Parse(sp.Slice(0, idx)) let second = Int32.Parse(sp.Slice(idx + 1)) struct(first, second)
[<MemoryDiagnoser>] type ParsingBench() = let str = "123,456"
let delim = ','
[<Benchmark(Baseline=true)>] member __.GetNums() =
Parsing.getNums str delim |> ignore
[<Benchmark>]
member __.GetNumsFaster() =
Parsing.getNumsSpan str delim |> ignore
[<EntryPoint>] let main _ =
let summary = BenchmarkRunner.Run<ParsingBench>() printfn "%A" summary
0 // Return an integer exit code
Go Faster
Now I’ll diverge from the practical application of F# to delve into some performance characteristics.
When building something like a Web service that has high traffic, performance matters! Specifically, avoiding needless allocations for the GC to clean up tends to be one of the most impactful things you can do for long-running Web server processes.
This is where types like Span<'T> start to shine when you’re using F# and .NET Core. A span is sort of like a window into a buffer of data that you can use to read and manipulate that data. Span<'T> impos- es a variety of restrictions on how you can use it so that the runtime can guarantee that various performance enhancements will apply.
I’lldemonstratethiswithasample(asseenin“AllAboutSpan: Exploring a new .NET Mainstay” by Steven Toub at msdn.com/magazine/ mt814808). I’ll use BenchmarkDotNet to measure the results.
First, I’ll create a console app on .NET Core:
dotnet new console -lang F# -o Benchmark && cd Benchmark dotnet add package benchmarkdotnet
Next, I’ll modify it to benchmark a routine that has a typical implementation and an implementation that uses Span<'T>, as shown in Figure 2.
Figure 3 Benchmark Results
Method
Mean
Error
StdDev
Ratio
Gen0
Gen1
Gen2
Allocated
GetNums
90.17 ns
0.6340 ns
0.5620 ns
1.00
0.5386
-
-
88B
GetNumsFaster
60.01 ns
0.2480 ns
0.2071 ns
0.67
-
-
-
-
22 msdn magazine
F#


































































































   28   29   30   31   32