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

Optimise FlatDictionary init for scenarios where no values are stored and llow inlining access #456

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Joannis
Copy link
Contributor

@Joannis Joannis commented May 19, 2024

This provides specialized access to the type, and virtually eliminates its presence from routes where a flat dictionary is not used. This affects the route parameters primarily, but helps in particular because CoreRequestContext always starts out with an empty Parameters instance.

…. Allow inlining access, providing specialized access
@Joannis Joannis requested a review from adam-fowler May 19, 2024 20:36
@adam-fowler
Copy link
Member

Empty arrays don't allocate any memory, so I'm not sure this is going to be a saving, as you have added a branch whenever accessing the parameters now

@Joannis
Copy link
Contributor Author

Joannis commented May 20, 2024

I think my measurements are macOS-specific then. Swift arrays do seem to have a different (default) implementation from the ContiguousArray one

Copy link
Member

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

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

Looks good, some minor change requests

@@ -26,104 +26,154 @@ public struct FlatDictionary<Key: Hashable, Value>: Collection, ExpressibleByDic
public typealias Element = (key: Key, value: Value)
public typealias Index = Array<Element>.Index

@usableFromInline
struct Storage {
@usableFromInline internal /* private */ var elements: [Element]
Copy link
Member

Choose a reason for hiding this comment

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

Can we remove the commented out /* private */ if we require everything to be internal

/// Access element at specific position
public subscript(_ index: Index) -> Element { return self.elements[index] }
@inlinable
public subscript(_ index: Index) -> Element { return self.storage!.elements[index] }
Copy link
Member

Choose a reason for hiding this comment

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

! isn't ideal here, perhaps we should add a precondition that storage exist and provide an out of range message or something similar, given the index will be out of range

/// Returns the index immediately after the given index
public func index(after index: Index) -> Index { self.elements.index(after: index) }
@inlinable
public func index(after index: Index) -> Index { index + 1 }
Copy link
Member

Choose a reason for hiding this comment

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

Any reason you changed this? I know index+1 is correct in most cases, but what is the expected result when index == endIndex?

} else {
return nil
}
}
set {
let hashKey = Self.hashKey(key)
if let index = hashKeys.firstIndex(of: hashKey) {
if var storage, let index = storage.hashKeys.firstIndex(of: hashKey) {
Copy link
Member

Choose a reason for hiding this comment

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

The ordering of the if statements here is slightly confusing. Currently you test for the existence of storage twice when the key doesn't exist. Perhaps it should be

if let storage {
    // do stuff when storage already exists
} else {
    // create storage if needed
}

}

// FlatDictionary is Sendable when Key and Value are Sendable
extension FlatDictionary.Storage: Sendable where Key: Sendable, Value: Sendable {}
Copy link
Member

Choose a reason for hiding this comment

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

I added this in to get rid of a Sendable warning

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.

None yet

2 participants