【问题标题】:Duff's device in SwiftDuff 在 Swift 中的设备
【发布时间】:2016-01-25 15:34:57
【问题描述】:

我们知道Duff's device 使用交错的结构切换和循环,例如:

send(to, from, count)
register short *to, *from;
register count;
{
    register n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
            } while (--n > 0);
    }
}

现在,在 Swift 2.1 中,switch-case control flows 并没有像我们在 Swift 文档中看到的那样隐含失败:

没有隐式失败

与 C 和 Objective-C 中的 switch 语句相比,switch Swift 中的语句不会落在每个 case 的底部,并且 默认进入下一个。相反,整个 switch 语句 一旦第一个匹配的 switch case 完成它的执行 完成,不需要显式的 break 语句。这使得 switch 语句比 C 语言更安全、更容易使用,并且避免了 错误地执行了多个 switch case。

现在,鉴于在 Swift 中有一个 fallthrough 子句显式地具有 fallthrough 副作用:

穿越

Swift 中的 Switch 语句不会落在每个 case 的底部 并进入下一个。相反,整个 switch 语句完成 它在第一个匹配案例完成后立即执行。经过 相反,C 要求您在 每个开关盒的末端,以防止跌落。避免违约 fallthrough 意味着 Swift switch 语句更加简洁 并且比它们在 C 中的对应物可预测,因此它们避免了 错误地执行了多个 switch case。

这很像:

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}
print(description)
// prints "The number 5 is a prime number, and also an integer."

考虑到 Wikipedia 提醒我们,这些设备来自问题

A straightforward code to copy items from an array to a memory-mapped output register might look like this:
do {                          /* count > 0 assumed */
    *to = *from++;            /* "to" pointer is NOT incremented, see explanation below */
} while(--count > 0);

Duff 的设备在 Swift 中的具体实现是什么

这只是一个语言和编码问题,并不打算应用于真正的 Swift 应用程序。

【问题讨论】:

  • 呃...这是 1983 年的优化。像高速缓存和分支预测这样的东西几乎没有发明,更不用说高度优化的 memcpy 实现了。为什么你会根据 1980 年代的算法优化代码?为什么你认为这些东西在今天仍然适用?
  • @Lundin:我没有看到有人谈论任何“优化”。我认为这个问题非常清楚,它与优化甚至任何现实生活中的编码场景无关。这是一个关于可用语言结构灵活性的抽象问题。
  • Duff 的 Device 依赖于 C 的 switch 语句的两个特性: (1) 默认情况下执行通过 casedefault 标签; (2) switchcasedefault 标签可以出现在嵌套控制结构中(if/elsewhiledo/whilefor)。即使您可以使用fallthrough 指令在 Swift 中执行 (1),我假设它不支持 (2)。
  • @Lundin:它复制到硬件寄存器这一事实使其与 memcpy 截然不同。除了循环展开(这是 Duff 的设备的全部内容)之外,另一个常见的 memcpy 优化是一次移动更大的数据单元,如果您的目标是 1 字节硬件寄存器,您就无法做到这一点。至于除法,它是一个整数除法和一个循环外的模数。由于分母是 2 的幂,大多数编译器会将它们转换为位移位和掩码——这在微控制器上不是问题。达夫的设备今天完美无缺。
  • 当然,Swift 不会允许编译这种代码。事实上,Duff's device 本质上是一个不应该编译的疏忽(意外)。在某些时候,有人发现它可以用于循环展开优化,因此最终保留了语言中的漏洞。今天,这几乎无关紧要,因为编译器要先进得多,并且所需的优化也非常不同,因为架构在多核、缓存、管道等方面发生了很大变化。同样在现代语言中,你并不像实际的 CPU 指令那样接近.

标签: c swift switch-statement fall-through duffs-device


【解决方案1】:

Duffs 设备不仅仅是优化。如果您查看https://research.swtch.com/duff,它是关于使用这种机制实现协同例程的讨论(见第 8 段 Duff 先生的评论)。

如果你尝试编写一个没有这个能力的可移植协程包。您最终会组装或重写 jmpbuf 条目 [两者都不是可移植的]。

像 go 和 swift 这样的现代语言比 C 语言有更多限制性的内存模型,所以这种机制(我想)会导致各种跟踪问题。即使是 clang,gcc 中类似 lambda 的块结构,最终也会与线程本地存储交织在一起,除非您坚持使用琐碎的应用程序,否则可能会造成各种破坏。

【讨论】:

    【解决方案2】:

    您尽可能用最高级别的代码表达您的意图,并相信 Swift 编译器会为您优化它,而不是尝试自己优化它。 Swift 是一门高级语言。您不会在高级语言中进行低级循环展开。

    尤其是在 Swift 中,您无需担心复制数组(Duff 设备的原始应用程序),因为 Swift 会假装在您分配数组时使用“写入时复制”来复制数组。这意味着只要您只是读取它们,它将对两个变量使用相同的数组,但是一旦您修改其中一个,它将在后台创建一个副本。

    例如,来自https://developer.apple.com/documentation/swift/array 修改数组的副本

    Each array has an independent value that includes the values of all
    of its elements. For simple types such as integers and other structures,
    this means that when you change a value in one array, the value of that
    element does not change in any copies of the array. For example:
    
    var numbers = [1, 2, 3, 4, 5]
    var numbersCopy = numbers
    numbers[0] = 100
    print(numbers)
    // Prints "[100, 2, 3, 4, 5]"
    print(numbersCopy)
    // Prints "[1, 2, 3, 4, 5]"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 2016-08-29
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多