Swift   发布时间:2022-03-31  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了在Swift中,在循环中声明变量有多糟糕大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

我不知道所有 Swift机制,以及它如何处理变量. 我总是更喜欢在输入for或while循环之前声明变量,而不是语言,而不是反复在循环内声明它们. 但是重新声明变量是不是很糟糕?它会以非常大的迭代次数影响性能吗?具体的Swift如何处理这种行为? 例如: while i < 100 { let a = someFunc() i += 1 } VS let a: MyObj while i
我不知道所有 Swift机制,以及它如何处理变量.

我总是更喜欢在输入for或while循环之前声明变量,而不是语言,而不是反复在循环内声明它们.

但是重新声明变量是不是很糟糕?它会以非常大的迭代次数影响性能吗?具体的Swift如何处理这种行为?

例如:

while i < 100 {
  let a = someFunc()
  i += 1
}

VS

let a: MyObj
while i < 100 {
 a = someFunc()
 i += 1
}
这不会影响性能,版本1是首选.即使它会影响性能,您也需要在虑除了版本1之外的任何其他选项之前在您的精确代码上进行演示.在处理优化编译器时,没有通用的性能答案.做一些与你的代码没有深入探讨的“性能”异常,很可能会让事情变得更糟.正常情况是最优化的情况.

(我知道我夸大了这一点.肯定有办法查看代码并说“这将是非常低效的.”并且有一些古怪的Swift部分,其中看起来不错的东西实际上是坏的,最值得注意的是使用组合字符串,或者使用pre-Swift4 reduce来创建一个数组.但是在那些重要的情况下,你会很快发现它,因为当它们重要时它们真的很糟糕.)

但我们不必猜测任何这一点.我们可以问一下编译器.

// inside.swift
import Foundation

func runme() {
    var i = 0
    while i < 100 {
      let a = Int.random(in: 0...10)
      print(a)
      i += 1
    }
}


// outside.swift
import Foundation

func runme() {
    var i = 0
    var a: Int
    while i < 100 {
      a = Int.random(in: 0...10)
      print(a)
      i += 1
    }
}

首先,请注意我将这些放在一个函数中.这很重要.将它们放在顶层会使一个全局变为一种情况,并且全局变量具有特殊处理,包括线程安全初始化,这使得“外部”情况看起来比在更常规的情况下更昂贵和复杂. (以这种方式正确地测试微优化是非常非常困难的,你可以得出一般的“这是更快”的结论.有很多因素.)

第二次注意打印.我们需要确保以副作用方式使用a,否则优化器可能会完全删除它.打印非常好,即使它很复杂.您也可以使用结果来修改全局,但编译器肯定可以更积极地进行优化,并可能消除我们想要看到的内容. (你真的必须在你关心的实际案例上测试这些东西.)

现在我们可以使用swiftc -O -emit-sil看看Swift将如何处理这些问题.那个-O很关键.很多人试图在不打开优化器的情况下进行性能测试,这些结果都是无意义的.

SIL看起来是什么样的? (Swift中间语言.这是将程序转换为机器代码的第一个重要步骤.如果两个@L_874_22@相同的SIL,它们将@L_874_22@相同的机器代码.)

SIL有点长(8000行),所以我要修剪一下.我在<>中的@L_607_25@.这将变得有点乏味,因为探索这些东西是非常挑剔的.如果你想跳过它,TL-DR是:这两段代码没有区别.不是“一个无关紧要的小差异”.字面上(除了调试器的提示),没有区别.

// runme()
sil hidden @$S4main5runmeyyF : $@convention(thin) () -> () {
bb0:
  ... <define a bunch of variables and function calls> ...

<compute the random number and put it in %29>
// %19                                            // user: %49
bb1(%19 : $BuilTin.Int64):                        // Preds: bb5 bb0
  %20 = alloc_stack $SystemRandomnumberGenerator  // users: %23,%30,%21
  store %2 to %20 : $*SystemRandomnumberGenerator // id: %21
  br bb2                                          // id: %22

bb2:                                              // Preds: bb3 bb1
  %23 = apply %6<SystemRandomnumberGenerator>(%20,%5) : $@convention(method) <τ_0_0 where τ_0_0 : RandomnumberGenerator> (@inout τ_0_0,@thin UInt.TypE) -> UInt // user: %24
  %24 = struct_extract %23 : $UInt,#UInt._value  // users: %28,%25
  %25 = builTin "cmp_ult_Int64"(%24 : $BuilTin.Int64,%4 : $BuilTin.Int64) : $BuilTin.Int1 // user: %26
  cond_br %25,bb3,bb4                           // id: %26

bb3:                                              // Preds: bb2
  br bb2                                          // id: %27

bb4:                                              // Preds: bb2
  %28 = builTin "urem_Int64"(%24 : $BuilTin.Int64,%3 : $BuilTin.Int64) : $BuilTin.Int64 // user: %29
  %29 = struct $Int (%28 : $BuilTin.Int64)        // users: %42,%31
  dealloc_stack %20 : $*SystemRandomnumberGenerator // id: %30


< *** Note that %29 is called "a" *** >

  debug_value %29 : $Int,let,name "a"           // id: %31

... < The print call. This is a lot more code than you think it is...> ...

< Add one to i and check for overflow >

  %49 = builTin "sadd_with_overflow_Int64"(%19 : $BuilTin.Int64,%8 : $BuilTin.Int64,%13 : $BuilTin.Int1) : $(BuilTin.Int64,BuilTin.Int1) // users: %51,%50
  %50 = tuple_extract %49 : $(BuilTin.Int64,BuilTin.Int1),0 // users: %55,%53
  %51 = tuple_extract %49 : $(BuilTin.Int64,1 // user: %52
  cond_fail %51 : $BuilTin.Int1                   // id: %52


< Loop if i < 100 >
  %53 = builTin "cmp_slt_Int64"(%50 : $BuilTin.Int64,%1 : $BuilTin.Int64) : $BuilTin.Int1 // user: %54
  cond_br %53,bb5,bb6                           // id: %54

bb5:                                              // Preds: bb4
  br bb1(%50 : $BuilTin.Int64)                    // id: %55

bb6:                                              // Preds: bb4
  %56 = tuple ()                                  // user: %57
  return %56 : $()                                // id: %57
} // end sil function '$S4main5runmeyyF'

“外部”代码几乎相同.有什么不同?注意上面代码中的***标记调用debug_value的位置?这在“外部”中缺失,因为a被定义为函数变量而不是块变量.

知道这两个中缺少什么?对“a”的alloc_stack调用.这是一个整数;它可以放在一个寄存器中.它取决于低级编译器是存储在寄存器还是堆栈中.优化器看到“a”不会逃避代码的这个区域,因此它包含调试器的提示,但它实际上不打算为它请求存储,甚至不在堆栈上.它可以只取Random的返回寄存器并将其移动到参数寄存器进行打印.由LLVM及其优化器来决定所有这些.

所有这一切的教训是,它对性能来说无关紧要.在可能很重要的模糊情况下(例如当a是全局的)时,版本1将更有效,我认为这与您期望的相反.

大佬总结

以上是大佬教程为你收集整理的在Swift中,在循环中声明变量有多糟糕全部内容,希望文章能够帮你解决在Swift中,在循环中声明变量有多糟糕所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。