【问题标题】:Long compile time when using a function (Swift 4)使用函数时编译时间长(Swift 4)
【发布时间】:2017-08-16 14:17:45
【问题描述】:

以下代码可在合理时间内自行编译EXAMPLE

public struct LazyProduct6Sequence <
  T1: LazySequenceProtocol, T2: LazySequenceProtocol, T3: LazySequenceProtocol,
  T4: LazySequenceProtocol, T5: LazySequenceProtocol, T6: LazySequenceProtocol
>: LazySequenceProtocol {
  public typealias Element = (T1.Element, T2.Element, T3.Element, T4.Element, T5.Element, T6.Element)
  public typealias Iterator = AnyIterator<Element>

  private let iterator: Iterator

  internal init (
    _ sequence1: T1, _ sequence2: T2, _ sequence3: T3,
    _ sequence4: T4, _ sequence5: T5, _ sequence6: T6
  ) {
    self.iterator = AnyIterator(
      sequence1.flatMap { element1 in
        sequence2.flatMap { element2 in
          sequence3.flatMap { element3 in
            sequence4.flatMap { element4 in
              sequence5.flatMap { element5 in
                sequence6.map { element6 in
                  (element1, element2, element3, element4, element5, element6)
                }
              }
            }
          }
        }
      }.makeIterator()
    )
  }

  public func makeIterator () -> Iterator {
    return self.iterator
  }
}

public func product <
  T1: LazySequenceProtocol, T2: LazySequenceProtocol, T3: LazySequenceProtocol,
  T4: LazySequenceProtocol, T5: LazySequenceProtocol, T6: LazySequenceProtocol
> (
  _ sequence1: T1, _ sequence2: T2, _ sequence3: T3,
  _ sequence4: T4, _ sequence5: T5, _ sequence6: T6
) -> LazyProduct6Sequence<T1, T2, T3, T4, T5, T6> {
  return LazyProduct6Sequence(sequence1, sequence2, sequence3, sequence4, sequence5, sequence6)
}

但是,在编译使用它的代码时,大约需要 40 秒才能编译(或根本拒绝编译)EXAMPLE

_ = product([1, 2].lazy, [3, 4].lazy, [5, 6].lazy, [7, 8].lazy, [9, 10].lazy, [11, 12].lazy)

为什么编译需要这么长的时间?

【问题讨论】:

  • 长编译时间通常与类型推断的复杂性有关。尝试为每个闭包注释闭包参数 { (element1: T1.Element) in 的类型。
  • @AllenHumphreys:肯定与类型推断有关,是的。为每个闭包添加显式返回类型并没有太大帮助,因为编译时间几乎保持不变。但是,当我将显式类型添加到使用该函数 (example) 的赋值中时,编译时间几乎是瞬时的。
  • 这是一个非常痛苦的陈述,但我不确定在提供的示例中还有哪些改进空间。不幸的是,类型推断总是导致 Swift 编译时间问题的原因。我不确定其他具有强类型和推理的语言如何,但我想它是相似的?
  • @AllenHumphreys:我仍在尝试找出编写惰性函数的最佳方法,因此可能有更好的方法没有这个问题。但我认为这一定是某种错误。我会提交错误报告。

标签: swift functional-programming lazy-evaluation type-inference swift4


【解决方案1】:

据我所知,这与 Swift 的类型推断系统有关。为每个闭包添加显式返回类型并没有太大帮助,因为编译时间几乎保持不变。但是,当我将显式类型添加到使用函数EXAMPLE的赋值中时,编译几乎是即时的:

let sequence: LazyProduct6Sequence<
  LazyRandomAccessCollection<[Int]>,
  LazyRandomAccessCollection<[Int]>,
  LazyRandomAccessCollection<[Int]>,
  LazyRandomAccessCollection<[Int]>,
  LazyRandomAccessCollection<[Int]>,
  LazyRandomAccessCollection<[Int]>
> = product(
  [1, 2].lazy,
  [3, 4].lazy,
  [5, 6].lazy,
  [7, 8].lazy,
  [9, 10].lazy,
  [11, 12].lazy
)
let iterator = sequence.makeIterator()

while let element = iterator.next() {
  print(element)
}

显然这并不理想而且很丑。

我不知道为什么会这样,因为只有一个函数与表达式匹配。

值得一提的是,对于较低的 arities,编译时间会减少。如果我重载product 也需要热切的Sequences,那么编译将冻结我的机器。

编辑:从那以后我发现这些类型的Sequences 默认情况下应该是惰性的。使用与 Swift 的zip 相同的命名约定重新编写我的函数,上面的示例变为:

public struct Product6Iterator <Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence,
    Sequence5: Sequence, Sequence6: Sequence>: IteratorProtocol {
  public typealias Element = (Sequence1.Element, Sequence2.Element, Sequence3.Element, Sequence4.Element,
    Sequence5.Element, Sequence6.Element)

  private let iterator: AnyIterator<Element>

  internal init (_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4,
      _ sequence5: Sequence5, _ sequence6: Sequence6) {
    self.iterator = AnyIterator(
      sequence1.lazy.flatMap { element1 in
        sequence2.lazy.flatMap { element2 in
          sequence3.lazy.flatMap { element3 in
            sequence4.lazy.flatMap { element4 in
              sequence5.lazy.flatMap { element5 in
                sequence6.lazy.map { element6 in
                  (element1, element2, element3, element4, element5, element6)
                }
              }
            }
          }
        }
      }.makeIterator()
    )
  }

  public mutating func next () -> Element? {
    return self.iterator.next()
  }
}

public struct Product6Sequence <Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence,
    Sequence5: Sequence, Sequence6: Sequence>: Sequence {
  public typealias Iterator = Product6Iterator<Sequence1, Sequence2, Sequence3, Sequence4, Sequence5, Sequence6>
  public typealias Element = Iterator.Element

  private let sequence1: Sequence1
  private let sequence2: Sequence2
  private let sequence3: Sequence3
  private let sequence4: Sequence4
  private let sequence5: Sequence5
  private let sequence6: Sequence6

  internal init (_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4,
      _ sequence5: Sequence5, _ sequence6: Sequence6) {
    self.sequence1 = sequence1
    self.sequence2 = sequence2
    self.sequence3 = sequence3
    self.sequence4 = sequence4
    self.sequence5 = sequence5
    self.sequence6 = sequence6
  }

  public func makeIterator () -> Iterator {
    return Iterator(sequence1, sequence2, sequence3, sequence4, sequence5, sequence6)
  }
}

public func product <Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence,
    Sequence5: Sequence, Sequence6: Sequence> (_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3,
    _ sequence4: Sequence4, _ sequence5: Sequence5, _ sequence6: Sequence6) -> Product6Sequence<Sequence1, Sequence2,
    Sequence3, Sequence4, Sequence5, Sequence6> {
  return sequence1.product(sequence2, sequence3, sequence4, sequence5, sequence6)
}

public extension Sequence {
  func product <Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence, Sequence5: Sequence,
      Sequence6: Sequence> (_ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4,
      _ sequence5: Sequence5, _ sequence6: Sequence6) -> Product6Sequence<Self, Sequence2, Sequence3, Sequence4,
      Sequence5, Sequence6> {
    return Product6Sequence(self, sequence2, sequence3, sequence4, sequence5, sequence6)
  }
}

这个以及其他类似的 Sequence 扩展可以在我创建的名为 SequenceExtensions 的库中找到。

【讨论】:

    猜你喜欢
    • 2017-05-25
    • 1970-01-01
    • 2018-07-18
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多