我不了解所有Swift的机制,也不知道它如何处理变量。我总是更喜欢在进入for或while循环之前声明变量,而与语言无关,而不是一遍又一遍地在循环内声明它们。但是重新声明变量是否很糟糕?很大的迭代次数会影响性能吗? 特别是Swift 如何处理此行为?例子 :while i < 100 { let a = someFunc() i += 1}VSlet a: MyObjwhile i < 100 { a = someFunc() i += 1} 最佳答案 这不会影响性能,因此强烈建议使用版本1。即使会影响性能,您也需要在精确的代码上证明这一点,然后再考虑使用除版本1以外的任何其他选项。在处理优化编译器时,没有通用的性能答案。如果您未对代码进行深入探索,则执行任何不寻常的“性能”操作很可能会使情况变得更糟。正常情况是最优化的情况。(我知道我对此夸大了。绝对有办法查看代码并说“那将是非常低效的。”而且在Swift的一些古怪部分中,看起来不错的事情实际上是不好的,最明显的是使用+组合字符串,或使用pre-Swift4 reduce创建数组。但是在那些很重要的情况下,您会很快发现它,因为在它们很重要时它们确实很糟糕。)但是,我们不必对此进行任何猜测。我们可以问编译器。// inside.swiftimport Foundationfunc runme() { var i = 0 while i < 100 { let a = Int.random(in: 0...10) print(a) i += 1 }}// outside.swiftimport Foundationfunc runme() { var i = 0 var a: Int while i < 100 { a = Int.random(in: 0...10) print(a) i += 1 }}首先,请注意我将它们放在一个函数中。那很重要将它们放在顶层会使a在一种情况下成为全局变量,并且全局变量具有特殊处理,包括线程安全的初始化,这使“外部”情况比在更常规的用法中看起来更加昂贵和复杂。 (以这样的方式正确地测试微优化是非常非常困难的,您可以得出一般的“更快”的结论。有太多的因素。)第二个注意print。我们需要确保以副作用的方式使用a,否则优化程序可能会完全将其删除。 print非常好,即使它很复杂。您也可以使用结果来修改全局变量,但是编译器肯定可以更加积极地优化它,并且可以消除我们希望看到的内容。 (您真的真的必须在您关心的实际情况下测试这些东西。)现在,我们可以使用swiftc -O -emit-sil来了解Swift将如何处理这些问题。该-O至关重要。许多人试图在不打开优化器的情况下进行性能测试,而这些结果是毫无意义的。那么SIL是什么样的呢? (快速中级语言。这是将程序转换为机器代码的第一步。如果两个事物生成相同的SIL,则它们将生成相同的机器代码。)SIL有点长(8000行),因此我将对其进行一些调整。我在中的评论。这将有点乏味,因为探索这些东西非常挑剔。如果要跳过它,则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: %49bb1(%19 : $Builtin.Int64): // Preds: bb5 bb0 %20 = alloc_stack $SystemRandomNumberGenerator // users: %23, %30, %21 store %2 to %20 : $*SystemRandomNumberGenerator // id: %21 br bb2 // id: %22bb2: // 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: %26bb3: // Preds: bb2 br bb2 // id: %27bb4: // 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, Builtin.Int1), 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: %54bb5: // Preds: bb4 br bb1(%50 : $Builtin.Int64) // id: %55bb6: // Preds: bb4 %56 = tuple () // user: %57 return %56 : $() // id: %57} // end sil function '$S4main5runmeyyF'“外部”代码几乎相同。有什么不同?注意上面代码中的***标记了对debug_value的调用吗?因为a被定义为函数变量而不是块变量,所以在“外部”中缺少该功能。知道这两个都缺少什么吗? “a”的alloc_stack调用。这是一个整数;它可以放在寄存器中。无论是将其存储在寄存器还是堆栈中,都​​取决于较低级别的编译器。优化器发现“a”不会转义代码的这一部分,因此它包含了调试器的提示,但实际上并没有费力要求存储它,甚至不在堆栈上。它只需要获取Random的返回寄存器并将其移至print的参数寄存器即可。由LLVM及其优化器决定所有这一切。从所有这些中学到的教训是,它实际上对性能无关紧要。在可能很重要的晦涩情况下(例如a是全局的),版本1会更有效,我认为这与您的预期相反。关于swift - 在Swift中,在循环中声明变量有多糟糕,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53147092/
10-12 03:45