在Swift中,是两次计算两个值的总和更快,还是一次计算并将结果存储在变量中更快?

在Swift中,是两次计算两个值的总和更快,还是一次计算并将结果存储在变量中更快?,swift,Swift,我正在创建一个高性能的应用程序,想知道哪种编写相同代码的方法在运行时运行得更快 备选案文1: let a = 1 + 2 self.doSomething(with: a) self.doSomethingElse(with: a) 备选案文2: self.doSomething(with: 1 + 2) self.doSomethingElse(with: 1 + 2) 如果其中任何一个选项速度更快,那么结构是否也是如此?e、 g let a = CGPoint(x: 1, y: 1) s

我正在创建一个高性能的应用程序,想知道哪种编写相同代码的方法在运行时运行得更快

备选案文1:

let a = 1 + 2
self.doSomething(with: a)
self.doSomethingElse(with: a)
备选案文2:

self.doSomething(with: 1 + 2)
self.doSomethingElse(with: 1 + 2)
如果其中任何一个选项速度更快,那么结构是否也是如此?e、 g

let a = CGPoint(x: 1, y: 1)
self.doSomething(with: a)
self.doSomethingElse(with: a)

编辑:添加了真实世界场景

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
      return
    }

    let currentPoint = touch.location(in: view)
    let lastPoint = touch.previousLocation(in: view)


    //////////

    let newPoint1 = CGPoint(x: lastPoint.x - currentPoint.x, y: lastPoint.y - currentPoint.y)
    let newPoint2 = CGPoint(x: lastPoint.y - currentPoint.y, y: lastPoint.x - currentPoint.x)

    // OR

    let newX = lastPoint.x - currentPoint.x
    let newY = lastPoint.y - currentPoint.y
    let newPoint11 = CGPoint(x: newX, y: newY)
    let newPoint22 = CGPoint(x: newY, y: newX)

    ///////

    print([newPoint1, newPoint2])
    print([newPoint11, newPoint22])
  }
override func touchesMoved(touch:Set,带有事件:UIEvent?){
保护让触摸=触摸。首先触摸{
返回
}
让currentPoint=触摸位置(在:视图中)
让lastPoint=touch.previousLocation(在:视图中)
//////////
设newPoint1=CGPoint(x:lastPoint.x-currentPoint.x,y:lastPoint.y-currentPoint.y)
设newPoint2=CGPoint(x:lastPoint.y-currentPoint.y,y:lastPoint.x-currentPoint.x)
//或
设newX=lastPoint.x-currentPoint.x
设newY=lastPoint.y-currentPoint.y
设newPoint11=CGPoint(x:newX,y:newY)
设newPoint22=CGPoint(x:newY,y:newX)
///////
打印([newPoint1,newPoint2])
打印([newPoint11,newPoint22])
}

一般来说,做更少的“工作”总是更快。在选项2中,你要做两次
1+2
,所以需要“更长的时间”。我说“更长的时间”,因为这取决于你在做什么,你是否会看到任何不同。至于使用变量
让a=1+2
,在空间和时间之间有一个折衷。选项1占用的时间较少,但占用的空间较多,而选项2占用的时间较多,但占用的空间较少


这不一定是Swift特有的。

它们在字面上是相同的。编译器将为您内联
a
。我们怎么知道?我们问编译器

编译器看到有一个文本值3的计算,它在编译时计算,并将其粘贴到编译器变量(%3)中。唯一的区别是,在第一种情况下,编译器发出一个
debug\u值
,以便调试器具有%3的本地名称。如果删除了调试信息,以后这将完全不相关。但除此之外,它实际上是相同的代码。没有分配内存或已注册内存来保存3。它在编译时已知,并在引用它的任何地方注入

执行代码中更清晰的操作。编译器将处理它

对于你的问题,你可以做同样的事情。您将看到优化器可以检测到这些也是常量。在SIL级别,它将生成两个CGfloat,并将它们组合到一个CGPoint中(它将重用该CGPoint,无论是否使用局部变量)。但是,如果您查看最终的汇编输出,它通常能够将整个结构简化为最终的逐字节表示(通常直接内联到
print
调用中)。例如,我创建了一个
CGPoint(x:999,y:888)
doSomething
的排放组件为:

    movabsq $7956005065853857651, %rsi
    movabsq $-1215907691987450521, %rdx
    callq   _$sSS5write2toyxz_ts16TextOutputStreamRzlF
由于我只调用了一次
doSomething
,编译器意识到它可以将字符串插值所需的精确值直接硬编码到汇编中(您也可以在
doSomethingElse
中找到这两个相同的“随机”数)。在编译步骤中进行字符串插值不够聪明,但它知道所有涉及的文本


写清楚。让优化器完成它的工作。然后,也只有到那时,你才能探索自己是否能做得更好。在没有仔细测试的情况下,不要猜测优化器。它通常(尽管并不总是)比你聪明。

使用
计算一次值。这会告诉Swift编译器相同的值被使用了两次,并允许Swift编译器/优化器生成更紧凑的代码。如果您知道值是相同的,请与编译器共享该信息,不要让优化器自己知道(因为它可能无法知道)

在您使用
1+2
的示例中,这两种情况下肯定会生成相同的代码,因为。编译器将在编译时执行
1+2
,生成的代码只会将
3
传递给每个函数调用

在第二个示例中,Swift编译器可能无法识别您已经生成了相同结构的两个版本,并且它可能会发出两次生成结构的代码。通过将该结构分配给常量
a
,Swift知道它可以将相同的
struct
传递给两个函数,并避免创建两次

一般规则:为编译器提供更多信息,使其能够进行更好的优化

增加的好处:使用
let
使您的代码更可读,更易于修改


在您的真实场景中

let newPoint1 = CGPoint(x: lastPoint.x - currentPoint.x, y: lastPoint.y - currentPoint.y)
let newPoint2 = CGPoint(x: lastPoint.y - currentPoint.y, y: lastPoint.x - currentPoint.x)

// OR

let newX = lastPoint.x - currentPoint.x
let newY = lastPoint.y - currentPoint.y
let newPoint11 = CGPoint(x: newX, y: newY)
let newPoint22 = CGPoint(x: newY, y: newX)
同样,编译器可能会生成相同的代码,因为编译器会检测并消除冗余表达式。但为什么要依赖这个呢?您知道这些值表示
newX
newY
,因此,通过首先计算这些值作为常量,您1)让编译器知道要计算此表达式一次,2)向您自己和您的读者记录代码的意图


除了向编译器/优化器提供额外提示外,第二个示例更清晰、更易于修改。总的来说,这是更好的代码。

我想让我困惑的是,分配变量需要一些时间,我不知道分配变量是否比计算两个值的总和快。@t0a0一般来说,分配变量所需的时间主要是实际计算变量的值。分配它确实需要一点时间,但对于大多数现代编程语言,如Swift,这是相当疏忽的。@t0a0如果你真的想自己检查一下,你可以只计算时间:在操场上对事情进行计时完全是不明智的
...
    func f() {
        doSomething(with: 1 + 2)
        doSomethingElse(with: 1 + 2)
    }
...

$ swiftc -O -emit-sil second.swift

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  %2 = integer_literal $Builtin.Int64, 3          // user: %3
  %3 = struct $Int (%2 : $Builtin.Int64)          // users: %7, %5
  // function_ref specialized X.doSomething(with:)
  %4 = function_ref @$s6second1XV11doSomething4withySi_tFTf4nd_n : $@convention(thin) (Int) -> () // user: %5
  %5 = apply %4(%3) : $@convention(thin) (Int) -> ()
  // function_ref specialized X.doSomethingElse(with:)
  %6 = function_ref @$s6second1XV15doSomethingElse4withySi_tFTf4nd_n : $@convention(thin) (Int) -> () // user: %7
  %7 = apply %6(%3) : $@convention(thin) (Int) -> ()
  %8 = integer_literal $Builtin.Int32, 0          // user: %9
  %9 = struct $Int32 (%8 : $Builtin.Int32)        // user: %10
  return %9 : $Int32                              // id: %10
} // end sil function 'main'
%2 = integer_literal $Builtin.Int64, 3          // user: %3
%3 = struct $Int (%2 : $Builtin.Int64)          // users: %8, %6, %4
    movabsq $7956005065853857651, %rsi
    movabsq $-1215907691987450521, %rdx
    callq   _$sSS5write2toyxz_ts16TextOutputStreamRzlF
let newPoint1 = CGPoint(x: lastPoint.x - currentPoint.x, y: lastPoint.y - currentPoint.y)
let newPoint2 = CGPoint(x: lastPoint.y - currentPoint.y, y: lastPoint.x - currentPoint.x)

// OR

let newX = lastPoint.x - currentPoint.x
let newY = lastPoint.y - currentPoint.y
let newPoint11 = CGPoint(x: newX, y: newY)
let newPoint22 = CGPoint(x: newY, y: newX)