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

[Swift] Provide access to underlying pointer for Scalars and NativeStructs #8481

Conversation

mustiikhalil
Copy link
Collaborator

@mustiikhalil mustiikhalil commented Jan 13, 2025

The following PR allows users to access a pointer to a vector of objects within the ByteBuffer without the need to copy or create an array until the users would want to.

  • We use UnsafeBufferPointer<T> which will always assume the type, and always provide access to a type of T.
  • Updates Sample code
  • Updates docs to remove old way of creating an object

depends on #8478

@github-actions github-actions bot added c++ codegen Involving generating code from schema documentation Documentation python swift labels Jan 13, 2025
@mustiikhalil mustiikhalil force-pushed the mmk/provide-access-underlying-pointer branch from 8ecad3f to 13150ef Compare January 13, 2025 22:04
@mustiikhalil mustiikhalil self-assigned this Jan 13, 2025
@mustiikhalil mustiikhalil force-pushed the mmk/provide-access-underlying-pointer branch from 13150ef to 5953d40 Compare January 13, 2025 22:33
…teBuffer

Adds a way to store external data and use them direclty within them
ByteBuffer instead of copying memory into the internal memory of the
ByteBuffer

Removed unowned from the initializer

Fixes a bug where array's of bytes are still being copied

Adds benchmark for initializing memory into a bytebuffer

Address a bug where the bytebuffer starts a memory leak

Replaces assumingMemoryBinding with bindMemory which is safer
@mustiikhalil mustiikhalil force-pushed the mmk/provide-access-underlying-pointer branch 2 times, most recently from 07fab73 to b8aca98 Compare January 14, 2025 01:28
Removed the use of Array to create a string

Updates docs

suppress compilation warnings by replacing var with let
@mustiikhalil mustiikhalil force-pushed the mmk/provide-access-underlying-pointer branch from b8aca98 to 7b92b51 Compare January 14, 2025 01:31
@@ -122,6 +197,7 @@ public struct MyGame_Sample_Monster: FlatBufferObject, Verifiable {
public var inventoryCount: Int32 { let o = _accessor.offset(VTOFFSET.inventory.v); return o == 0 ? 0 : _accessor.vector(count: o) }
public func inventory(at index: Int32) -> UInt8 { let o = _accessor.offset(VTOFFSET.inventory.v); return o == 0 ? 0 : _accessor.directRead(of: UInt8.self, offset: _accessor.vector(at: o) + index * 1) }
public var inventory: [UInt8] { return _accessor.getVector(at: VTOFFSET.inventory.v) ?? [] }
public var inventoryPointer: UnsafeBufferPointer<UInt8>? { return _accessor.getBufferPointer(at: VTOFFSET.inventory.v) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to have a withInventoryPointer style API which ensures the pointer is valid for the duration of the closure. Seems weird to be able to pass in a Data which requires that sort of API, but then evade it later.

Comment on lines +69 to +81
case .data(let data):
(memory, capacity) = data.withUnsafeBytes {
(UnsafeMutableRawPointer(mutating: $0.baseAddress!), $0.count)
}
case .bytes(let bytes):
(memory, capacity) = bytes.withUnsafeBytes {
(UnsafeMutableRawPointer(mutating: $0.baseAddress!), $0.count)
}
#endif
case .array(let array):
(memory, capacity) = array.withUnsafeBytes {
(UnsafeMutableRawPointer(mutating: $0.baseAddress!), $0.count)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wtholliday I think that's fine because we technically return the pointer from the withUnsafeBytes here. But its doable for sure to make it extra safe

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, calling withUnsafeBytes and returning the pointer is bad practice AFAIK.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then assigning it similar to what we had earlier is also wrong :/ cause their ways we are escaping the closure and returning the pointer.

// number 1
array.withUnsafeBytes {
   self.memory = UnsafeMutableRawPointer(mutating: $0.baseAddress!)
   self.capacity = $0.count
}

// number 2
(memory, capacity) = array.withUnsafeBytes {
   (UnsafeMutableRawPointer(mutating: $0.baseAddress!), $0.count)
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mzaks a bit of feedback is appreciated here, it seems that we are stuck.
1- We either copy the data
2- We slow down the library using closure, just to access swift internal data.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wtholliday this is the difference between both read double benchmarks, current implementation:

Reading Doubles
╒═══════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
 Metric                              p0      p25      p50      p75      p90      p99     p100  Samples 
╞═══════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
 Malloc (total) *                     0        0        0        0        0        0        0     1528 
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
 Memory (resident peak) (K)        8126     8471     8471     8471     8471     8471     8471     1528 
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
 Releases *                           0        0        0        0        0        0        0     1528 
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
 Time (total CPU) (ns) *              2        2        2        2        2        2        2     1528 
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
 Time (wall clock) (ns) *             2        2        2        2        2        2        3     1528 
╘═══════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

with the use of unsafeBytes(..)

Reading Doubles
╒═══════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                        │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *              │       0 │       0 │       0 │       0 │       0 │       0 │       0 │      59 │
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Memory (resident peak) (K)    │    8143 │    8462 │    8462 │    8471 │    8471 │    8471 │    8471 │      59 │
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases *                    │       3 │       3 │       3 │       3 │       3 │       3 │       3 │      59 │
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (ns) *       │      50 │      51 │      51 │      51 │      51 │      65 │      65 │      59 │
├───────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (ns) *      │      50 │      50 │      50 │      51 │      51 │      65 │      65 │      59 │
╘═══════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

@mustiikhalil mustiikhalil marked this pull request as draft January 14, 2025 08:17
@mustiikhalil mustiikhalil deleted the mmk/provide-access-underlying-pointer branch January 14, 2025 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ codegen Involving generating code from schema documentation Documentation python swift
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants