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

[stdlib][WIP perf experiment] Replace AnySequence with Array in default Sequence methods #9639

Closed
wants to merge 3 commits into from

Conversation

palimondo
Copy link
Contributor

This is an experiment to confirm that replacing AnySequence in default methods on Sequence with Array improves performance. This is a supporting argument to SE-NNNN Sequence Cleanup.

At the moment, I don't know if the replacement implementations work correctly, because the code as is does not compile. I seem to misunderstand something fundamental here and would appreciate getting educated. Please advise @dabrahams, @airspeedswift, @moiseev, @gottesmm, @atrick.

My original reasoning is that associatedtype SubSequence defined in protocol Sequence ensures same return type of following methods: dropFirst, dropLast, drop(while:), prefix, prefix(while:). Another requirement that can not be expressed in Swift type system at the moment (recursive protocol constraints) is that SubSequence conforms to Sequence.

For a lot of types in stdlib, the Sequence conformance is provided by the default implementation in extension Sequencein Sequence.swift. And I thought replacing AnySequence<Element> with [Element] — here I assume it's just syntactic sugar for Array<Element> — would just work. But stdlib with this change does not compile. According to the first error, my modified implementation (please see diff) fails to provide Sequence conformance for EnumeratedIterator:

/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Algorithm.swift:93:15: 
error: type 'EnumeratedIterator<Base>' does not conform to protocol 'Sequence'
public struct EnumeratedIterator<
              ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:1041:15: 
note: candidate has non-matching type '<Self> (Int) -> [Self.Element]' 
[with Iterator = EnumeratedIterator<Base>,
 SubSequence = [(offset: Int, element: Base.Element)]]
  public func dropFirst(_ n: Int) -> [Element] {
              ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:1073:15: 
note: candidate has non-matching type '<Self> (Int) -> [Self.Element]' 
[with Iterator = EnumeratedIterator<Base>,
 SubSequence = [(offset: Int, element: Base.Element)]]
  public func dropLast(_ n: Int) -> [Element] {
              ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:1123:15: 
note: candidate has non-matching type '<Self> (while: (Self.Element) throws -> Bool)
 throws -> [Self.Element]' 
[with Iterator = EnumeratedIterator<Base>,
 SubSequence = [(offset: Int, element: Base.Element)]]
  public func drop(
              ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:1160:15: 
note: candidate has non-matching type '<Self> (Int) -> [Self.Element]' 
[with Iterator = EnumeratedIterator<Base>,
 SubSequence = [(offset: Int, element: Base.Element)]]
  public func prefix(_ maxLength: Int) -> [Element] {
              ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:1199:15: 
note: candidate has non-matching type '<Self> (while: (Self.Element) throws -> Bool)
 throws -> [Self.Element]' 
[with Iterator = EnumeratedIterator<Base>,
 SubSequence = [(offset: Int, element: Base.Element)]]
  public func prefix(
              ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:451:8: 
note: protocol requires function 'dropFirst' with type '(Int) ->
 [(offset: Int, element: Base.Element)]'; do you want to add a stub?
  func dropFirst(_ n: Int) -> SubSequence
       ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:471:8: 
note: protocol requires function 'dropLast' with type '(Int) ->
 [(offset: Int, element: Base.Element)]'; do you want to add a stub?
  func dropLast(_ n: Int) -> SubSequence
       ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:481:8: 
note: protocol requires function 'drop(while:)' with type 
'(((offset: Int, element: Base.Element)) throws -> Bool)
 throws -> [(offset: Int, element: Base.Element)]'; do you want to add a stub?
  func drop(
       ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:501:8: 
note: protocol requires function 'prefix' with type
 '(Int) -> [(offset: Int, element: Base.Element)]'; do you want to add a stub?
  func prefix(_ maxLength: Int) -> SubSequence
       ^
/Users/mondo/Developer/swift-source/swift/stdlib/public/core/Sequence.swift:525:8: 
note: protocol requires function 'prefix(while:)' with type 
'(((offset: Int, element: Base.Element)) throws -> Bool)
 throws -> [(offset: Int, element: Base.Element)]'; do you want to add a stub?
  func prefix(
       ^

Am I missing something fundamental? Is this some kind of compiler bug? Is it related to the most recent changes in #8990?

@atrick
Copy link
Contributor

atrick commented May 16, 2017

I don't really have time to advise on this but I'll suggest that you build some small models of the feature you're trying to add, outside of the standard library, to understand how the language works before jumping into changing stdlib code. It looks like you're expecting Array to resolve overloads that require AnySequence, but those are unrelated types.

@palimondo
Copy link
Contributor Author

@dabrahams, @airspeedswift, @moiseev, @gottesmm Could you point me in the right direction here?
I don't see any place that specifies that the protocol Sequenceconformance that is provided by default method implementation in extension Sequence must be provided by AnySequence. Why doesn't it work when I replace it with Array? I thought that SubSequencegets inferred to be Array here, as there is no concrete type that would define the associated type to be anything else, as far as I can see.

@moiseev
Copy link
Contributor

moiseev commented May 22, 2017

@palimondo what if you try to specify the SubSequence typealias explicitly? You are right that the associated type 'value' gets inferred, but it is not necessarily the case that it's inferred solely based on the methods that you list. Perhaps there is even an ambiguity that you introduce with your change.

@palimondo
Copy link
Contributor Author

palimondo commented May 22, 2017

@moiseev How does one explicitly specify associated type in default implementation? Can I do typealias in an extension?!

@palimondo
Copy link
Contributor Author

Can I get a benchmark, please?

@moiseev
Copy link
Contributor

moiseev commented May 23, 2017

@palimondo You should be able to ask swift-ci to do that. Can you try: 'Please smoke benchmark'?

@moiseev
Copy link
Contributor

moiseev commented May 23, 2017

@swift-ci Please smoke benchmark

@swift-ci
Copy link
Contributor

Build comment file:

Build failed before running benchmark.


@palimondo
Copy link
Contributor Author

palimondo commented May 24, 2017

@moiseev AFAIK CI bot listening to you requires commit access which I don't have yet.
...and from the looks of it, rightly so. I can't tell my brackets apart. Array<Element> or [Element], but never Array[Element]! I shouldn't try to push changes at 11pm. I've force-pushed correction.

BTW where is the link to the failed build? // @shahmishal

@swift-ci Please smoke benchmark

@moiseev
Copy link
Contributor

moiseev commented May 24, 2017

Oh well. I was misled by:
_stdlib__wip_perf_experiment__replace_anysequence_with_array_in_default_sequence_methods_by_palimondo_ _pull_request__9639_ _apple_swift

@moiseev
Copy link
Contributor

moiseev commented May 24, 2017

@swift-ci Please smoke benchmark

@swift-ci
Copy link
Contributor

Build comment file:

Build failed before running benchmark.


@palimondo
Copy link
Contributor Author

@shahmishal Why aren't there any links to failing build here?

After searching around a lot on Jenkins I found the failing build. My changes are crashing the compiler... filed SR-4988.

@shahmishal
Copy link
Member

@swift-ci Please smoke benchmark

@shahmishal
Copy link
Member

From the build Log: Setting status of f579944fcdf129893dc59ac3154fd8c490496585 to FAILURE with url https://ci.swift.org/job/swift-PR-osx-smoke-perf/97/ and message: 'Build finished. '

Did you force push to this PR? If yes, it's known bug with GitHub PR testing.

@palimondo
Copy link
Contributor Author

@shahmishal Indeed I force pushed. There's no need to try to build again now, it crashes the compiler.

@swift-ci
Copy link
Contributor

Build comment file:

Build failed before running benchmark.


@palimondo
Copy link
Contributor Author

palimondo commented May 24, 2017

@dabrahams Could you please have a quick look over the changes in Sequence.swift here? This currently crashes compiler, but is this a change that would be acceptable and still in scope for Swift 4? I'm hoping this is source compatible, but affects ABI (I guess?).

@dabrahams
Copy link
Contributor

These changes would fail with infinite input sequences, wouldn't they?

@palimondo
Copy link
Contributor Author

Yes, it would fail with infinite input sequences, as is the case with all other non-lazy methods on Sequence.

At the moment this initial commit has only extracted the inner classes into their own files without any changes. Second part is to properly implement LazySequence conformance modeled after PrefixWhile.swift.gyb.

But extracting out the lazy implementations would allow the default methods to just always return [Element] instead of AnySequence. (Once we resolve that compiler crash...)

@palimondo
Copy link
Contributor Author

palimondo commented May 24, 2017

My point is that to handle infinite sequences you should explicitly use .lazy. The prefix and dropFirst are the only two exceptions on Sequence that are always lazy. Cleaning up the API to be consistent across all Sequence methods is my goal here. But first I wanted to get some performance data from the smaller change, until the SR-4988 stopped me...

@palimondo
Copy link
Contributor Author

#20221 has removed AnySequence as return type from sequence slicing operations. 🎉

@palimondo palimondo closed this Nov 28, 2018
@palimondo palimondo deleted the sequence-cleanup branch November 28, 2018 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants