大佬教程收集整理的这篇文章主要介绍了在Swift中,在循环中声明变量有多糟糕,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
我总是更喜欢在输入for或while循环之前声明变量,而不是语言,而不是反复在循环内声明它们.
但是重新声明变量是不是很糟糕?它会以非常大的迭代次数影响性能吗?具体的Swift如何处理这种行为?
例如:
while i < 100 { let a = someFunc() i += 1 }
VS
let a: MyObj while i < 100 { a = someFunc() i += 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,请注明来意。