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-12308] Storing Swift Array is slower than copying it manually to a new buffer #54738

Open
dmcyk opened this issue Mar 3, 2020 · 3 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. performance standard library Area: Standard library umbrella

Comments

@dmcyk
Copy link
Contributor

dmcyk commented Mar 3, 2020

Previous ID SR-12308
Radar rdar://problem/60001989
Original Reporter @dmcyk
Type Bug
Environment

Xcode 11.4 Beta 2

Additional Detail from JIRA
Votes 0
Component/s Standard Library
Labels Bug, Performance
Assignee None
Priority Medium

md5: 7f2f36721c6e803dbbe482dc6bbad031

Issue Description:

I run into a case where storing a Swift Array seems to be slower than copying it over into an unsafe buffer.

In Instruments from those two snippets, the unsafe variant is around 200 ms faster on my machines (766 vs 556 ms). On top of that difference I have to actually copy array contents to an unsafe buffer. If there was a way to take ownership of the underlying array buffer I could avoid all those copies.

@inline(never)
func highLevel() {
  var collected: [[Entry]] = []
  var stack: [State] = []

  for i in 0 ..< 100_000 {
    var state = State(payload: .init(start: i, end: i + 1))
    for j in 0 ..< 100 {
      state.children.append(.init(payload: j ..< j + 1))
    }

    stack.append(state)
  }

  for i in 0 ..< stack.count {
    collected.append(stack[i].children)
    stack[i].children = []
  }

  let flatten = collected.flatMap { $0 }

  print(flatten.count)
}


@inline(never)
func unsafe() {
  var collected: [UnsafeMutableBufferPointer<Entry>] = []
  var stack: [State] = []

  for i in 0 ..< 100_000 {
    var state = State(payload: .init(start: i, end: i + 1))
    for j in 0 ..< 100 {
      state.children.append(.init(payload: j ..< j + 1))
    }

    stack.append(state)
  }

  var collectedCounter = 0
  for i in 0 ..< stack.count {
    stack[i].children.withUnsafeBufferPointer { ptr in
      let buffer = UnsafeMutableBufferPointer<Entry>.allocate(capacity: ptr.count)
      buffer.baseAddress!.initialize(from: ptr.baseAddress!, count: ptr.count)
      collectedCounter += ptr.count
      collected.append(buffer)
    }
    stack[i].children = []
  }

  let flatten = [Entry](unsafeUninitializedCapacity: collectedCounter) { (buffer: inout UnsafeMutableBufferPointer<Entry>, initialised: inout Int) in
    initialised = 0
    for pPtr in collected {
      buffer.baseAddress!.advanced(by: initialised).moveInitialize(from: pPtr.baseAddress!, count: pPtr.count)
      initialised += pPtr.count
      pPtr.deallocate()
    }

    precondition(initialised == collectedCounter)
  }

  print(flatten.count)
}


highLevel()
unsafe()
@dmcyk
Copy link
Contributor Author

dmcyk commented Mar 3, 2020

/cc @atrick

@atrick
Copy link
Member

atrick commented Mar 3, 2020

@swift-cicreate

@atrick
Copy link
Member

atrick commented Mar 3, 2020

Thanks for the bug report. I see no obvious reason that the normal Array-based implementation should be slower.

@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. performance standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

2 participants