Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-7365] 'cycled(_:)' performance regression in Swift 4.1 #49913

Open
moiseev mannequin opened this issue Apr 5, 2018 · 2 comments
Open

[SR-7365] 'cycled(_:)' performance regression in Swift 4.1 #49913

moiseev mannequin opened this issue Apr 5, 2018 · 2 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself performance regression standard library Area: Standard library umbrella swift 4.1

Comments

@moiseev
Copy link
Mannequin

moiseev mannequin commented Apr 5, 2018

Previous ID SR-7365
Radar rdar://problem/39239628
Original Reporter @moiseev
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Compiler, Standard Library
Labels Bug, 4.1Regression, Performance
Assignee None
Priority Medium

md5: f9eb66bac7c507368f34aa9fa5476249

Issue Description:

See this forum post for the details: https://forums.swift.org/t/starter-pitch-introducing-a-cycled-method-to-sequence/11254/28

This snippet of code:

import XCTest

extension Collection {

  public func cycledCustom() -> CycleSequence<Self> {
    return CycleSequence(self)
  }

  public func cycledRepeat() -> FlattenSequence<Repeated<Self>> {
    return repeatElement(self, count: self.isEmpty ? 0 : .max).joined()
  }

  public func cycledUnfoldFirst() -> FlattenSequence<UnfoldFirstSequence<Self>> {
    return sequence(first: self, next: { $0.isEmpty ? nil : $0 }).joined()
  }

  public func cycledUnfoldState() -> UnfoldSequence<Element, Iterator> {
    return sequence(state: makeIterator(), next: { iterator in
      if let nextElement = iterator.next() {
        return nextElement
      }
      iterator = self.makeIterator() // NOTE: captures `self`.
      return iterator.next()
    })
  }
}

public struct CycleSequence<C: Collection> : Sequence, IteratorProtocol {

  internal let _collection: C
  internal var _iterator: C.Iterator

  internal init(_ collection: C) {
    _collection = collection
    _iterator = collection.makeIterator()
  }

  public mutating func next() -> C.Element? {
    if let nextElement = _iterator.next() {
      return nextElement
    }
    _iterator = _collection.makeIterator();
    return _iterator.next()
  }
}

class CycleBenchmark: XCTestCase {

  let _prefix = 1_000_000
  let _source = 0 ... 999

  func testCustom() {
    self.measure {
      var count = 0
      for _ in _source.cycledCustom().prefix(_prefix) {
        count += 1
      }
      XCTAssertEqual(count, _prefix)
    }
  }

  func testRepeat() {
    self.measure {
      var count = 0
      for _ in _source.cycledRepeat().prefix(_prefix) {
        count += 1
      }
      XCTAssertEqual(count, _prefix)
    }
  }

  func testUnfoldFirst() {
    self.measure {
      var count = 0
      for _ in _source.cycledUnfoldFirst().prefix(_prefix) {
        count += 1
      }
      XCTAssertEqual(count, _prefix)
    }
  }

  func testUnfoldState() {
    self.measure {
      var count = 0
      for _ in _source.cycledUnfoldState().prefix(_prefix) {
        count += 1
      }
      XCTAssertEqual(count, _prefix)
    }
  }
}

Gives different performance compiled with different versions of a compiler:

Average times (seconds):

Swift 4.0.3 Swift 4.1 Swift 4.2
(Xcode 9.2) (Xcode 9.3) (Xcode 9.3)
testCustom() 0.227 0.587 0.417
testRepeat() 0.320 0.685 0.501
testUnfoldFirst() 0.318 0.677 0.510
testUnfoldState() 0.273 0.632 0.471
@benrimmington
Copy link
Collaborator

Re-tested using Xcode 9.3 (9E145) from the App Store, with the following toolchains:

-Onone with let _prefix = 1_000_000:

Swift 4.0.3 Swift 4.1 Swift 4.2
testCustom() 0.220 0.585 0.504
testRepeat() 0.316 0.687 0.621
testUnfoldFirst() 0.310 0.682 0.628
testUnfoldState() 0.272 0.629 0.556

-O with let _prefix = 100_000_000:

Swift 4.0.3 Swift 4.1 Swift 4.2
testCustom() 0.160 0.222 0.222
testRepeat() 0.222 0.222 0.222
testUnfoldFirst() 0.225 0.603 0.572
testUnfoldState() 1.995 2.014 2.047
  • The -O results are for 100x more iterations of the for _ in loop, to give readable average times.

  • The -O results for testUnfoldState() are 10x slower than expected (i.e. compared to other tests).

  • The -Onone results for the latest Swift 4.2 snapshot are a bit slower than the March 17 snapshot.

  • The repeated 0.222 results are genuine (i.e. not copy-paste errors).

@belkadan
Copy link
Contributor

belkadan commented Apr 6, 2018

Needs investigation, I'm sure.

@swift-ci create

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself performance regression standard library Area: Standard library umbrella swift 4.1
Projects
None yet
Development

No branches or pull requests

3 participants