gpt4 book ai didi

swift - Swift 是否优化结构的链式创建和复制?

转载 作者:搜寻专家 更新时间:2023-11-01 05:54:49 25 4
gpt4 key购买 nike

如果我创建一个具有类似...的函数的结构

struct SomeStruct {
var name: String? = nil
var number: Int = 0
var date: Date? = nil
//... many other properties

func setting<Value>(_ keyPath: WritableKeyPath<SomeStruct, Value>, to value: Value) -> SomeStruct {
var copy = self
copy[keyPath: keyPath] = value
return copy
}
}

Swift 是否对做类似...的事情进行了优化

let myStruct = SomeStruct()
.setting(\.name, to: "Fogmeister")
.setting(\.number, to: 42)
.setting(\.date, to: yesterday)
.setting(\.otherProperty, to: value)
...etc
...etc

因为 setting 函数会创建一个副本并在每次您实质上一遍又一遍地创建新的 Struct 时更改该副本,然后丢弃其中一个以外的所有结构。

执行此操作时是否需要考虑任何开销注意事项,或者 Swift 是否会在编译时优化掉所有这些未使用的值?

最佳答案

不,这没有优化。它将为每次调用制作一个新副本。很难想象优化器将如何解决这个问题以避免复制,但编写优化器的向导以前曾愚弄过我。但与大多数优化器问题一样,我们不必猜测它的作用。我们可以看看。

我稍微重写了这段代码以摆脱可选(这只会使事情稍微复杂化而不会改变问题)。

struct SomeStruct {
var name: String = ""
var number: Int = 0
var date: String = ""

func setting<Value>(_ keyPath: WritableKeyPath<SomeStruct, Value>, to value: Value) -> SomeStruct {
var copy = self
copy[keyPath: keyPath] = value
return copy
}
}

let myStruct = SomeStruct()
.setting(\.name, to: "Fogmeister")
.setting(\.number, to: 42)
.setting(\.date, to: "yesterday")

然后通过优化将其编译为 SIL:

swiftc -O -emit-sil x.swift

设置方法变成这样:

// SomeStruct.setting<A>(_:to:)
sil hidden @$S1x10SomeStructV7setting_2toACs15WritableKeyPathCyACxG_xtlF : $@convention(method) <Value> (@guaranteed WritableKeyPath<SomeStruct, Value>, @in_guaranteed Value, @guaranteed SomeStruct) -> @owned SomeStruct {
// %0 // users: %26, %17, %18, %3
// %1 // users: %11, %4
// %2 // users: %8, %7, %9, %5
bb0(%0 : $WritableKeyPath<SomeStruct, Value>, %1 : $*Value, %2 : $SomeStruct):
debug_value %0 : $WritableKeyPath<SomeStruct, Value>, let, name "keyPath", argno 1 // id: %3
debug_value_addr %1 : $*Value, let, name "value", argno 2 // id: %4
debug_value %2 : $SomeStruct, let, name "self", argno 3 // id: %5
%6 = alloc_stack $SomeStruct, var, name "copy" // users: %12, %28, %9, %29
%7 = struct_extract %2 : $SomeStruct, #SomeStruct.name // user: %15
%8 = struct_extract %2 : $SomeStruct, #SomeStruct.date // user: %16
store %2 to %6 : $*SomeStruct // id: %9
%10 = alloc_stack $Value // users: %27, %24, %11
copy_addr %1 to [initialization] %10 : $*Value // id: %11
%12 = address_to_pointer %6 : $*SomeStruct to $Builtin.RawPointer // user: %13
%13 = struct $UnsafeMutablePointer<SomeStruct> (%12 : $Builtin.RawPointer) // user: %18
// function_ref _projectKeyPathWritable<A, B>(root:keyPath:)
%14 = function_ref @$Ss23_projectKeyPathWritable4root03keyC0Spyq_G_yXlSgtSpyxG_s0dbC0Cyxq_Gtr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (UnsafeMutablePointer<τ_0_0>, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> (UnsafeMutablePointer<τ_0_1>, @owned Optional<AnyObject>) // user: %18
retain_value %7 : $String // id: %15
retain_value %8 : $String // id: %16
strong_retain %0 : $WritableKeyPath<SomeStruct, Value> // id: %17
%18 = apply %14<SomeStruct, Value>(%13, %0) : $@convention(thin) <τ_0_0, τ_0_1> (UnsafeMutablePointer<τ_0_0>, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> (UnsafeMutablePointer<τ_0_1>, @owned Optional<AnyObject>) // users: %25, %19, %20
%19 = tuple_extract %18 : $(UnsafeMutablePointer<Value>, Optional<AnyObject>), 0 // user: %21
%20 = tuple_extract %18 : $(UnsafeMutablePointer<Value>, Optional<AnyObject>), 1 // user: %23
%21 = struct_extract %19 : $UnsafeMutablePointer<Value>, #UnsafeMutablePointer._rawValue // user: %22
%22 = pointer_to_address %21 : $Builtin.RawPointer to [strict] $*Value // user: %23
%23 = mark_dependence %22 : $*Value on %20 : $Optional<AnyObject> // user: %24
copy_addr [take] %10 to %23 : $*Value // id: %24
release_value %18 : $(UnsafeMutablePointer<Value>, Optional<AnyObject>) // id: %25
strong_release %0 : $WritableKeyPath<SomeStruct, Value> // id: %26
dealloc_stack %10 : $*Value // id: %27
%28 = load %6 : $*SomeStruct // user: %30
dealloc_stack %6 : $*SomeStruct // id: %29
return %28 : $SomeStruct // id: %30
} // end sil function '$S1x10SomeStructV7setting_2toACs15WritableKeyPathCyACxG_xtlF'

特别感兴趣的是这个部分:

%6 = alloc_stack $SomeStruct, var, name "copy"  // users: %12, %28, %9, %29
%7 = struct_extract %2 : $SomeStruct, #SomeStruct.name // user: %15
%8 = struct_extract %2 : $SomeStruct, #SomeStruct.date // user: %16
store %2 to %6 : $*SomeStruct // id: %9

正如预期的那样,每次调用 setting 时都会创建一个新副本。

IMO,Swift 中更好的方法是这样的:

let myStruct: SomeStruct = { 
var s = SomeStruct()
s.name = "Fogmeister"
s.number = 42
s.date = "yesterday"
return s
}()

这优化了以下内容(加上我的注释):

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):

# allocate some storage for myStruct as a global
alloc_global @$S1x8myStructAA04SomeB0Vvp // id: %2
%3 = global_addr @$S1x8myStructAA04SomeB0Vvp : $*SomeStruct // user: %23

# Construct the tagged string value for "Fogmeister"
%4 = integer_literal $Builtin.Int64, 8391166415069474630 // user: %9
%5 = integer_literal $Builtin.Int64, -1585267068834385307 // user: %6
%6 = struct $UInt (%5 : $Builtin.Int64) // user: %7
%7 = value_to_bridge_object %6 : $UInt // user: %8
%8 = struct $_StringObject (%7 : $Builtin.BridgeObject) // user: %10
%9 = struct $UInt (%4 : $Builtin.Int64) // user: %10
%10 = struct $_StringGuts (%8 : $_StringObject, %9 : $UInt) // user: %11
%11 = struct $String (%10 : $_StringGuts) // user: %22

# Construct the 42
%12 = integer_literal $Builtin.Int64, 42 // user: %13
%13 = struct $Int (%12 : $Builtin.Int64) // user: %22

# Construct the tagged string for "yesterday"
%14 = integer_literal $Builtin.Int64, -1657324662872342407 // user: %15
%15 = struct $UInt (%14 : $Builtin.Int64) // user: %16
%16 = value_to_bridge_object %15 : $UInt // user: %18
%17 = integer_literal $Builtin.Int64, 7017859899421058425 // user: %19
%18 = struct $_StringObject (%16 : $Builtin.BridgeObject) // user: %20
%19 = struct $UInt (%17 : $Builtin.Int64) // user: %20
%20 = struct $_StringGuts (%18 : $_StringObject, %19 : $UInt) // user: %21
%21 = struct $String (%20 : $_StringGuts) // user: %22

# init SomeStruct and store it in our global
%22 = struct $SomeStruct (%11 : $String, %13 : $Int, %21 : $String) // user: %23
store %22 to %3 : $*SomeStruct // id: %23

# Return 0 (cause it's main)
%24 = integer_literal $Builtin.Int32, 0 // user: %25
%25 = struct $Int32 (%24 : $Builtin.Int32) // user: %26
return %25 : $Int32 // id: %26
} // end sil function 'main'

您会在这里注意到闭包执行已完全优化。编译器能够将“Fogmeister”和“yesterday”缩减为它们的标记字符串值,并将整个 block 缩减为单个 init 调用(在 %22),因为它注意到我正在设置所有值。太棒了。

关于swift - Swift 是否优化结构的链式创建和复制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53267232/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com