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

[Caching] Adds changes to Environment and EnvironmentKey needed for cross-render caching #382

Merged
merged 4 commits into from
Nov 11, 2022

Conversation

kdaker
Copy link
Collaborator

@kdaker kdaker commented Nov 3, 2022

This PR adds updates to Environment and EnvironmentKey that needed for cross-render caching.

Summary of changes:

  • Makes Environment equatable and adds equivalency to EnvironmentKey
  • Adds default equivalency implementations to Environment and EnvironmentKeys
  • Adds ability to get a Subset of keys from Environment and compare an Environment with a Subset
  • We can now subscribe to Environment reads through subscribeToReads()

@kdaker kdaker changed the base branch from main to feature/caching November 3, 2022 20:09
@kdaker kdaker changed the title Adds changes to Environment and EnvironmentKey needed for cross-render caching [Caching] Adds changes to Environment and EnvironmentKey needed for cross-render caching Nov 3, 2022
@kdaker kdaker changed the title [Caching] Adds changes to Environment and EnvironmentKey needed for cross-render caching [WIP][DNR][Caching] Adds changes to Environment and EnvironmentKey needed for cross-render caching Nov 3, 2022
@kdaker kdaker force-pushed the kareem/add-environment-equatability branch from 741de67 to 71d1175 Compare November 3, 2022 22:56
@kdaker kdaker changed the title [WIP][DNR][Caching] Adds changes to Environment and EnvironmentKey needed for cross-render caching [Caching] Adds changes to Environment and EnvironmentKey needed for cross-render caching Nov 3, 2022
@kdaker kdaker force-pushed the kareem/add-environment-equatability branch 2 times, most recently from 35641f0 to 4c97689 Compare November 3, 2022 23:30
@kdaker kdaker marked this pull request as ready for review November 3, 2022 23:32
@kdaker kdaker requested a review from a team as a code owner November 3, 2022 23:32
…r caching

* Adds equatability to Environment and EnvironmentKey
* Adds ability to subscribe to environment reads
@kdaker kdaker force-pushed the kareem/add-environment-equatability branch from 43c95c5 to c3480c2 Compare November 4, 2022 00:29
}
}

struct StorageKey: Hashable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we able to just use any EnvronmentKey.Type once we bump to Xcode 14.1?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think so, since .Types aren't Hashable. We can likely heavily simplify this type though!

private var values: [ObjectIdentifier: Any] = [:]
/// When enabled, notify any subscribers when a value is read
/// from the environment
var readNotificationsEnabled: Bool = true
Copy link
Collaborator

Choose a reason for hiding this comment

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

When would you want to disable this? Could we just rely on the presence of subscriptions instead?

Copy link
Collaborator

Choose a reason for hiding this comment

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

We turn this off when adapting the environment so erroneous reads aren't fired; which is coming in a later PR: https://github.com/square/Blueprint/pull/367/files#diff-07c71c5dcdc5da4c2ddf51b560695e562af396d4afafe3908b35656a0907459aR568

Copy link
Collaborator

Choose a reason for hiding this comment

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

Update: Since this a relatively small optimization; we just removed this in a later PR.

}

extension EnvironmentKey {
static func areValuesEqual(_ lhs: Any?, _ rhs: Any?) -> Bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit/thought:

Suggested change
static func areValuesEqual(_ lhs: Any?, _ rhs: Any?) -> Bool {
@_disfavoredOverload
static func isEquivalent(_ lhs: Any?, _ rhs: Any?) -> Bool {

Copy link
Collaborator

Choose a reason for hiding this comment

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

We think it's important to include the word Value in here, since the type signature is just two Anys, which could be construed to be the keys. Perhaps areValuesEquivalent?

Comment on lines 41 to 44
enum LayoutPass: Equatable {
case measurement
case layout
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can make this change later if we decide it's worth it, but if we wanted to promote these changes to MarketEnvironment it would be nice if this was an extendible "scope" instead of being tied to measure and layout.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Had the same thought; it should be easy enough to do this when we extract the environment, but for now I think this is sufficient.

@@ -41,4 +50,8 @@ struct ClosureURLHandler: URLHandler {
func onTap(url: URL) {
onTap(url)
}

func isEquivalent(to other: URLHandler) -> Bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I change the logic for the onTap between updates, will this handler still be re-applied to the backing view (even though it's considered equal)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes it will get re-applied. The equality check is only used to cache the ElementState however the viewDescription will always be called regardless of the element's cache state.


/// Equivalency check on the `Value`s of `EnvironmentKey`s. This should return false if the
/// difference in values affects the measurement or layout of consuming elements.
static func isEquivalent(_ lhs: Value, _ rhs: Value) -> Bool
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thought: Should we name these functions so that they describe that they're comparing only for layout/measurement purposes and not for strict content equability? isEquivalentForLayout(...)? I suppose not as I can't think of a good name...

Copy link
Collaborator

@n8chur n8chur Nov 7, 2022

Choose a reason for hiding this comment

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

I suppose this question might be more relevant in the next PR (for the ComparableElement function)

@kyleve kyleve added the caching label Nov 8, 2022
…-environment-equatability

* origin/feature/caching:
  Add debugDescription tests
  ElementIdentifier test updates per code review
  Add caching for ElementIdentifier
Copy link
Collaborator

@n8chur n8chur left a comment

Choose a reason for hiding this comment

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

Approving merge to feature branch to help make iterating on further work easier, but I'd like to do a full pass at the feature branch before it lands!


/// Gets or sets an environment value by its key.
public subscript<Key>(key: Key.Type) -> Key.Value where Key: EnvironmentKey {
public subscript<KeyType: EnvironmentKey>(key: KeyType.Type) -> KeyType.Value {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: prefer Key over KeyType (Key.Type is the Type)

Comment on lines 61 to 62
private var measurementDidRead: OnDidRead?
private var layoutDidRead: [OnDidRead] = []
Copy link
Collaborator

Choose a reason for hiding this comment

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

Feeling pretty dumb not being able to keep the whole picture in my head, but I'll just ask the stupid questions anyway:

Why do we use an array of read observations for measurement and not layout? I naively expected the opposite because measurement can happen many times while layout should happen once per pass...

Copy link
Collaborator

Choose a reason for hiding this comment

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

A very good question!

We differentiate these because measurements are "flat", eg we only care about the dependencies read during the direct element's measurement – because every layer of the tree that can caches its own measurements. However for layouts, we cache entire trees of layout – including our children – so we want to record and track the environment dependencies of our children as well, and re-perform the layouts if those values change as well.

Does that make sense? I feel like I should probably describe this both better in the code docs here, and in some explanatory tests

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does that make sense?

I think so!

I feel like I should probably describe this both better in the code docs here, and in some explanatory tests

  • 1

If we used an array to describe the measurement subscriptions (just to simplify the implementation a bit within environment and perhaps prepare the shape better for supporting "scopes" rather than enum LayoutPass) would we need to manually clear them out for some reason, or would there just end up being only 1 subscription of that scope?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can simplify this to a single array!

Copy link
Collaborator

@kyleve kyleve Nov 11, 2022

Choose a reason for hiding this comment

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

Thinking forward through this more, we might actually want to simplify measurement to be append as well. In theory, once we allow measurements and layouts to read the environment, downstream elements could pull a value from the environment that would affect their size, which when the parent measures its children, would affect its final size.

@kyleve kyleve merged commit 973484c into feature/caching Nov 11, 2022
@kyleve kyleve deleted the kareem/add-environment-equatability branch November 11, 2022 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants