所以我一直在试验这样的代码:
[<Struct>]
type Component = {
Num : int
}
with
static member New = {
Num = 0
}
let init n =
seq {
for _ in 0..n do
yield Component.New
}
|> Seq.toArray
let test (c : Component []) =
c
|> Array.map ( fun c -> { c with Num = c.Num + 1} )
[<EntryPoint>]
let main argv =
let sw = Stopwatch()
let c = init 10000000
sw.Start ()
let c' = test c
sw.Stop ()
printfn "%A" sw.ElapsedMilliseconds
0
我在运行这个测试时得到了这些基准测试。
Reference type and list: 1450
Reference type and array: 850
Value type and list: 700
Value type and array: 80
数组会比列表更快这一事实显然在预期范围内,我也预期值类型会更快,但不会快那么多。然而,我想知道,是否存在不希望记录为值类型的情况?它似乎总是更可取?
对于这样的小结构,尤其是在数组中保存时,没有理由因为性能原因而不将其作为结构。即使结构大于16字节(这是一般的指导原则(,在这种情况下,它通常也会产生更快的性能:
- 分配到阵列中
- 该数组的直接处理
- 不再处理该数组
如果后续的几个数组处理例程也能产生更好的性能,我不会感到惊讶,因为将一堆值类型分配到数组中意味着它通常都会加载到CPU缓存行中,并且处理速度快得离谱。
然而,当你没有一直使用值类型(数组、跨度(,或者如果你改变了做事的方式,事情会变得更有趣:
- 基于阵列的操作会创建新的阵列,最终数据的连续重新创建会压倒数据的实际处理
- 有时您使用F#列表或序列或其他引用类型,其中数据不会全部加载到CPU缓存行中
- 有时,您会混合和匹配值类型和引用类型,将内容存储在值类型中的额外复制会对性能产生负面影响
简而言之,这种特殊的场景非常适合使用值类型——包含的数据都只是一个基元值类型,它的实例存储在一个数组中,每次都会重新创建相同的结构。因此,如果性能非常重要,那么应该在这种情况下使用值类型。
将代码更改为使用列表,而是预先分配数据,而不是重新创建结构,只需在基准测试下返回一个单一值的列表,如下所示:
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Running
module ReferenceType =
type Component = {
Num0 : int
}
with
static member New = {
Num0 = 0
}
let init n =
seq {
for _ in 0..n do
yield Component.New
}
|> Seq.toList
let test cs =
cs
|> List.map (fun c -> c.Num0 + 1)
module ValueType =
[<Struct>]
type Component = {
Num0 : int
}
with
static member New = {
Num0 = 0
}
let init n =
seq {
for _ in 0..n do
yield Component.New
}
|> Seq.toList
let test cs =
cs
|> List.map (fun c -> c.Num0 + 1)
[<MemoryDiagnoser>]
type ReferenceVsValueType() =
let refs = ReferenceType.init 10_000
let vals = ValueType.init 10_000
[<Benchmark(Baseline=true)>]
member _.BuiltIn() = ReferenceType.test refs
[<Benchmark>]
member _.ValueType() = ValueType.test vals
[<EntryPoint>]
let main argv =
BenchmarkRunner.Run<ReferenceVsValueType>() |> ignore
0 // return an integer exit code
你会得到非常非常接近的结果:
方法 | 平均值 | 错误 | tdDev>Gen 1 | 第2代 | 已分配 |
---|---|---|---|---|---|
内置 | 90.99μs | - | 312.53 KB | ||
值类型 | 87.13μs | 1.712μs | - | 312.53KB |