F#奇妙游(7):也来整点测评

性能测试

作为一个不事生产和创造但是热爱学习和思考的编程爱好者,最喜欢看和最喜欢做的通常都是比来比去、拉拉扯扯,所以性能测试是必不可少的。

你看那些云玩家、云车神、云计算爱好者,孜孜不倦的都到处看评测报告,动手能力强的都在写评测报告。这就是我的观点:不能让人做决策,也要让人感觉自己在做决策。因为智人实在是太喜欢做决策,人均国师。

让人评测、替人决策是第一生产力。

F#作为.NET语言,当然也有高大上的性能测试工具,BenchmarkDotNet,这个工具是.NET Core官方推荐的性能测试工具,它的特点是简单易用,而且支持多种运行时,包括.NET Core、.NET Framework、Mono、CoreRT、NativeAOT等等。

帮工具吹牛我这里就不重复了,使用的步骤在这里

F#底层大冒险

这一篇不小心涉及到了.NET的底层编程实践,感觉是没学会走就要飞奔。但是评测最重要。

.NET在机器码之上精心设计了一层抽象,叫做IL,Intermediate Language,中间语言。这个中间语言是一种类似于汇编的语言,但是比汇编更高级,比如支持面向对象、支持泛型等等。这个中间语言是跨平台的,也就是说,不管你是在Windows、Linux、MacOS上编译,都会编译成这个中间语言。

但是.NET与JVM平台不同的是,它还提供了大量的直接在底层干活的手段。比如说Span<T>,这个类型就是直接在底层干活的,它的底层实现是一个指针,指向一段连续的内存。这个类型在.NET Core 2.1中引入,是为了提高内存操作的效率,因为.NET Core 2.1中引入了内存池,这个内存池是为了减少GC的压力,但是使用内存池的时候,就需要直接操作内存,而不是通过GC来操作内存。

那么我们就来评测下Span<T>的性能。

代码

这个评测代码是一个console的app。

dotnet new console -lang F# -o SpanBenchmark

然后添加依赖:

dotnet add package BenchmarkDotNet

然后就可以写代码了:

open System
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Jobs
open BenchmarkDotNet.Running

module Parsing =
    let getNumbers (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 getNumbers2 (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))
        first, second 
        
[<SimpleJob(RuntimeMoniker.Net70)>]
[<SimpleJob(RuntimeMoniker.Net80)>]
[<SimpleJob(RuntimeMoniker.NativeAot70)>]
[<SimpleJob(RuntimeMoniker.NativeAot80)>]
type ParsingBench() =
    let str = "123, 456"
    let delim = ','
    [<Benchmark>]
    member _.GetNumbers() = Parsing.getNumbers str delim
    [<Benchmark>]
    member _.GetNumbers2() = Parsing.getNumbers2 str delim
    
[<EntryPoint>]
let main argv =
    let summary = BenchmarkRunner.Run<ParsingBench> ()
    0   

然后运行:

dotnet run -c Release

就可以看到结果。

Results

BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3086/22H2/2022Update)
12th Gen Intel Core i7-12700H, 1 CPU, 20 logical and 14 physical cores
.NET SDK=8.0.100-preview.5.23303.2
  [Host]        : .NET 7.0.8 (7.0.823.31807), X64 RyuJIT AVX2 DEBUG
  .NET 7.0      : .NET 7.0.8 (7.0.823.31807), X64 RyuJIT AVX2
  .NET 8.0      : .NET 8.0.0 (8.0.23.28008), X64 RyuJIT AVX2
  NativeAOT 7.0 : .NET 7.0.5-servicing.23174.5, X64 NativeAOT AVX2
  NativeAOT 8.0 : .NET 8.0.0-preview.5.23280.8, X64 NativeAOT AVX2
MethodJobRuntimeMeanErrorStdDev
GetNumbers.NET 7.0.NET 7.029.08 ns0.250 ns0.195 ns
GetNumbers2.NET 7.0.NET 7.019.03 ns0.044 ns0.039 ns
GetNumbers.NET 8.0.NET 8.023.10 ns0.067 ns0.056 ns
GetNumbers2.NET 8.0.NET 8.014.86 ns0.069 ns0.064 ns
GetNumbersNativeAOT 7.0NativeAOT 7.026.90 ns0.065 ns0.061 ns
GetNumbers2NativeAOT 7.0NativeAOT 7.018.42 ns0.085 ns0.079 ns
GetNumbersNativeAOT 8.0NativeAOT 8.026.99 ns0.061 ns0.057 ns
GetNumbers2NativeAOT 8.0NativeAOT 8.018.91 ns0.154 ns0.144 ns

这个结果还是挺有趣的。

  1. Span<T>确实很快,快大概三分之一
  2. .NET 8.0比.NET 7.0快了大概一丢丢
  3. .NET 8.0里面,AOT居然更慢

总结

  1. 这就是.NET的魅力,你可以选择性能,也可以选择开发效率(并不是)
  2. F#背靠着大的平台,还是可能有点实际用途的,这也是Clojure、Scala等语言比古早的Lisp、Haskell等语言更有可能被工业界采用的原因
  3. 这就是.NET的魅力,你可以选择性能,也可以选择开发效率(并不是)
  4. F#背靠着大的平台,还是可能有点实际用途的,这也是Clojure、Scala等语言比古早的Lisp、Haskell等语言更有可能被工业界采用的原因
  5. .NET 8.0的AOT肿么回事,果然是preview版本
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大福是小强

除非你钱多烧得慌……

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值