为什么在 Swift 中每次调用 Decimal 类型的 formatted() 方法都会越来越慢?

Mac*_*Mac 3 swift

我需要将整数分成 3 位数字,并研究formatted()Decimal类型的方法。对随机整数每次
调用该formatted()类型的方法100,000 次会逐渐降低性能。 我想知道为什么会发生这种情况。Decimal

import Foundation

/// https://stackoverflow.com/a/56381954
func calculateTime(block : (() -> Void)) {
  let start = DispatchTime.now()
  block()
  let end = DispatchTime.now()
  let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
  let timeInterval = Double(nanoTime) / 1_000_000_000
  print("Time: \(timeInterval) seconds")
}

calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
Run Code Online (Sandbox Code Playgroud)
Time: 0.9492465 seconds
Time: 3.29213125 seconds
Time: 7.988363667 seconds
Time: 15.165178292 seconds
Time: 17.305036583 seconds
Time: 25.0114935 seconds
Time: 35.746310417 seconds
Time: 47.024551125 seconds
Run Code Online (Sandbox Code Playgroud)

Cou*_*per 6

虽然马特和马丁 R 是正确的,但他们实际上并没有指出罪魁祸首。当您使用“内存对象”调试器时,在函数执行时,您将看到这个非常有趣的图表:

在此输入图像描述

因此,该函数formatted()会导致大量分配,主要是 类型的对象NSAutoLocale

这暗示了进一步的问题:由于评估区域设置需要访问实际读取区域设置的文件,因此您还可能会遇到性能非常慢的情况。

因此,如果您也想加快速度,则应该显式使用预配置的区域设置对象,该对象分配一次且仅分配一次,然后将其用作参数来格式化字符串。

编辑:

为了证明我的假设,我创建了一个等效的陈述并将其与您的原始陈述进行了比较:

你原来的代码:

calculateTime { for _ in 0...100_000 { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } }
Run Code Online (Sandbox Code Playgroud)

时间:9.674371748秒

使用单个语言环境的等效代码:

let locale = Locale()
calculateTime { for _ in 0...100_000 { _ = NSDecimalNumber(
    value: Int.random(in: 0...Int.max))
    .description(withLocale: locale)
} }
Run Code Online (Sandbox Code Playgroud)

时间:0.210493037秒

并且无需任何进一步的修改,重复调用改进的语句,您将得到:

Time: 0.224133942 seconds
Time: 0.238930039 seconds
Time: 0.214735965 seconds
Time: 0.220390686 seconds
Time: 0.212360066 seconds
Time: 0.207630215 seconds
Time: 0.205125154 seconds
Run Code Online (Sandbox Code Playgroud)

...


mat*_*att 5

这是一个 Heisenbug:您自己通过测试工具使内存紧张而导致速度变慢。将您的测试线更改为:

calculateTime { for _ in 0...100_000 { autoreleasepool { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } } }
calculateTime { for _ in 0...100_000 { autoreleasepool { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } } }
calculateTime { for _ in 0...100_000 { autoreleasepool { _ = Decimal(Int.random(in: 0...Int.max)).formatted() } } }
// ... and so on
Run Code Online (Sandbox Code Playgroud)