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

Avoid returning / passing around a huge ParsedDeclaration type #16954

Merged
merged 6 commits into from May 20, 2017
Merged

Conversation

@SimonSapin
Copy link
Member

SimonSapin commented May 19, 2017

This enum type used to contain the result of parsing one CSS source declaration (name: value;) and expanding shorthands. Enum types are as big as the biggest of their variant (plus discriminant), which was quite big because some shorthands expand to many longhand properties. This type was returned through many functions and methods, wrapped and rewrapped in Result with different error types. This presumably caused significant memmove traffic.

Instead, we now allocate an ArrayVec on the stack and pass &mut references to it for various functions to push into it. This type is also very big, but we never move it.

We still use an intermediate data structure because we sometimes decide after shorthand expansion that a declaration is invalid after all and that we’re gonna drop it. Only later do we push to a PropertyDeclarationBlock, with an entire ArrayVec or nothing.

In future work we can try to avoid a large stack-allocated array, and instead writing directly to the heap allocation of the Vec inside PropertyDeclarationBlock. However this is tricky: we need to preserve this "all or nothing" aspect of parsing one source declaration, and at the same time we want to make it as little error-prone as possible for the various call sites. PropertyDeclarationBlock curently does property deduplication incrementally: as each PropertyDeclaration is pushed, we check if an existing declaration of the same property exists and if so overwrite it. To get rid of the stack allocated array we’d need to somehow deduplicate separately after pushing multiple PropertyDeclaration.


This change is Reviewable

@SimonSapin
Copy link
Member Author

SimonSapin commented May 19, 2017

CC @bholley

@bors-servo try p=10

@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

Trying commit 839b315 with merge ab45bf7...

bors-servo added a commit that referenced this pull request May 19, 2017
Avoid returning / passing around a huge ParsedDeclaration type

This enum type used to contain the result of parsing one CSS source declaration (`name: value;`) and expanding shorthands. Enum types are as big as the biggest of their variant (plus discriminant), which was quite big because some shorthands expand to many longhand properties. This type was returned through many functions and methods, wrapped and rewrapped in `Result` with different error types. This presumably caused significant `memmove` traffic.

Instead, we now allocate an `ArrayVec` on the stack and pass `&mut` references to it for various functions to push into it. This type is also very big, but we never move it.

We still use an intermediate data structure because we sometimes decide after shorthand expansion that a declaration is invalid after all and that we’re gonna drop it. Only later do we push to a `PropertyDeclarationBlock`, with an entire `ArrayVec` or nothing.

In future work we can try to avoid a large stack-allocated array, and instead writing directly to the heap allocation of the `Vec` inside `PropertyDeclarationBlock`. However this is tricky: we need to preserve this "all or nothing" aspect of parsing one source declaration, and at the same time we want to make it as little error-prone as possible for the various call sites. `PropertyDeclarationBlock` curently does property deduplication incrementally: as each `PropertyDeclaration` is pushed, we check if an existing declaration of the same property exists and if so overwrite it. To get rid of the stack allocated array we’d need to somehow deduplicate separately after pushing multiple `PropertyDeclaration`.
@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

💔 Test failed - windows-msvc-dev

@SimonSapin
Copy link
Member Author

SimonSapin commented May 19, 2017

@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

Trying commit 661ddad with merge 6d918ac...

bors-servo added a commit that referenced this pull request May 19, 2017
Avoid returning / passing around a huge ParsedDeclaration type

This enum type used to contain the result of parsing one CSS source declaration (`name: value;`) and expanding shorthands. Enum types are as big as the biggest of their variant (plus discriminant), which was quite big because some shorthands expand to many longhand properties. This type was returned through many functions and methods, wrapped and rewrapped in `Result` with different error types. This presumably caused significant `memmove` traffic.

Instead, we now allocate an `ArrayVec` on the stack and pass `&mut` references to it for various functions to push into it. This type is also very big, but we never move it.

We still use an intermediate data structure because we sometimes decide after shorthand expansion that a declaration is invalid after all and that we’re gonna drop it. Only later do we push to a `PropertyDeclarationBlock`, with an entire `ArrayVec` or nothing.

In future work we can try to avoid a large stack-allocated array, and instead writing directly to the heap allocation of the `Vec` inside `PropertyDeclarationBlock`. However this is tricky: we need to preserve this "all or nothing" aspect of parsing one source declaration, and at the same time we want to make it as little error-prone as possible for the various call sites. `PropertyDeclarationBlock` curently does property deduplication incrementally: as each `PropertyDeclaration` is pushed, we check if an existing declaration of the same property exists and if so overwrite it. To get rid of the stack allocated array we’d need to somehow deduplicate separately after pushing multiple `PropertyDeclaration`.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16954)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

💔 Test failed - windows-msvc-dev

@emilio
emilio approved these changes May 19, 2017
Copy link
Member

emilio left a comment

Looks great. r=me with nits


// Step 6
match result {
Ok(()) => {},

This comment has been minimized.

@emilio

emilio May 19, 2017

Member

nit: This can use if let now.

Also, where did step 7 go?

This comment has been minimized.

@SimonSapin

SimonSapin May 19, 2017

Author Member

The spec renumbered (removed an earlier step), and I forgot to renumber steps 8-9 here to 7-8. Done.

@@ -1504,6 +1297,180 @@ impl PropertyDeclaration {
PropertyDeclaration::Custom(..) => false,
}
}

/// The `in_keyframe_block` parameter controls this:

This comment has been minimized.

@emilio

emilio May 19, 2017

Member

I don't see an in_keyframe_block parameter anymore, though I think it's preexisting, mind to update it?

This comment has been minimized.

@SimonSapin

SimonSapin May 19, 2017

Author Member

It had been renamed context. Fixed.

}

const MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL: usize =
${max(len(s.sub_properties) for s in data.shorthands if s.name != "all")};

This comment has been minimized.

@emilio

emilio May 19, 2017

Member

As you noticed on irc, you can use shorthands_except_all here.

This comment has been minimized.

@SimonSapin

SimonSapin May 19, 2017

Author Member

Done.


fn push(&mut self, declaration: PropertyDeclaration) {
let over_capacity = self.declarations.push(declaration).is_some();
assert!(!over_capacity);

This comment has been minimized.

@emilio

emilio May 19, 2017

Member

debug_assert!?

This comment has been minimized.

@SimonSapin

SimonSapin May 19, 2017

Author Member

Done.


/// A stack-allocated vector of `PropertyDeclaration`
/// large enough to parse one CSS `key: value` declaration.
/// (Shorthands expand to multiple `PropertyDeclaration`s.)

This comment has been minimized.

@emilio

emilio May 19, 2017

Member

It'd be nice to elaborate on how it's used here, for example, saying that it's used as a scratch buffer for each key: value in a CSS declaration block.

This comment has been minimized.

@SimonSapin

SimonSapin May 19, 2017

Author Member

As discussed on IRC I’m not sure what to add since this already mentions parsing one key: value.

SimonSapin added 3 commits May 19, 2017
This enum type used to contain the result of parsing
one CSS source declaration (`name: value;`) and expanding shorthands.
Enum types are as big as the biggest of their variant (plus discriminant),
which was quite big because some shorthands
expand to many longhand properties.
This type was returned through many functions and methods,
wrapped and rewrapped in `Result` with different error types.
This presumably caused significant `memmove` traffic.

Instead, we now allocate an `ArrayVec` on the stack
and pass `&mut` references to it for various functions to push into it.
This type is also very big, but we never move it.

We still use an intermediate data structure because we sometimes decide
after shorthand expansion that a declaration is invalid after all
and that we’re gonna drop it.
Only later do we push to a `PropertyDeclarationBlock`,
with an entire `ArrayVec` or nothing.

In future work we can try to avoid a large stack-allocated array,
and instead writing directly to the heap allocation
of the `Vec` inside `PropertyDeclarationBlock`.
However this is tricky:
we need to preserve this "all or nothing" aspect
of parsing one source declaration,
and at the same time we want to make it as little error-prone as possible
for the various call sites.
`PropertyDeclarationBlock` curently does property deduplication
incrementally: as each `PropertyDeclaration` is pushed,
we check if an existing declaration of the same property exists
and if so overwrite it.
To get rid of the stack allocated array we’d need to somehow
deduplicate separately after pushing multiple `PropertyDeclaration`.
@SimonSapin SimonSapin force-pushed the arrayvec branch from 661ddad to 341fc54 May 19, 2017
@SimonSapin
Copy link
Member Author

SimonSapin commented May 19, 2017

@bors-servo r=emilio

@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

📌 Commit 341fc54 has been approved by emilio

@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

Testing commit 341fc54 with merge 6f7b1d8...

bors-servo added a commit that referenced this pull request May 19, 2017
Avoid returning / passing around a huge ParsedDeclaration type

This enum type used to contain the result of parsing one CSS source declaration (`name: value;`) and expanding shorthands. Enum types are as big as the biggest of their variant (plus discriminant), which was quite big because some shorthands expand to many longhand properties. This type was returned through many functions and methods, wrapped and rewrapped in `Result` with different error types. This presumably caused significant `memmove` traffic.

Instead, we now allocate an `ArrayVec` on the stack and pass `&mut` references to it for various functions to push into it. This type is also very big, but we never move it.

We still use an intermediate data structure because we sometimes decide after shorthand expansion that a declaration is invalid after all and that we’re gonna drop it. Only later do we push to a `PropertyDeclarationBlock`, with an entire `ArrayVec` or nothing.

In future work we can try to avoid a large stack-allocated array, and instead writing directly to the heap allocation of the `Vec` inside `PropertyDeclarationBlock`. However this is tricky: we need to preserve this "all or nothing" aspect of parsing one source declaration, and at the same time we want to make it as little error-prone as possible for the various call sites. `PropertyDeclarationBlock` curently does property deduplication incrementally: as each `PropertyDeclaration` is pushed, we check if an existing declaration of the same property exists and if so overwrite it. To get rid of the stack allocated array we’d need to somehow deduplicate separately after pushing multiple `PropertyDeclaration`.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16954)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

💔 Test failed - mac-dev-unit

@SimonSapin
Copy link
Member Author

SimonSapin commented May 19, 2017

@bors-servo retry

@bors-servo
Copy link
Contributor

bors-servo commented May 19, 2017

Testing commit 341fc54 with merge 60682cf...

bors-servo added a commit that referenced this pull request May 19, 2017
Avoid returning / passing around a huge ParsedDeclaration type

This enum type used to contain the result of parsing one CSS source declaration (`name: value;`) and expanding shorthands. Enum types are as big as the biggest of their variant (plus discriminant), which was quite big because some shorthands expand to many longhand properties. This type was returned through many functions and methods, wrapped and rewrapped in `Result` with different error types. This presumably caused significant `memmove` traffic.

Instead, we now allocate an `ArrayVec` on the stack and pass `&mut` references to it for various functions to push into it. This type is also very big, but we never move it.

We still use an intermediate data structure because we sometimes decide after shorthand expansion that a declaration is invalid after all and that we’re gonna drop it. Only later do we push to a `PropertyDeclarationBlock`, with an entire `ArrayVec` or nothing.

In future work we can try to avoid a large stack-allocated array, and instead writing directly to the heap allocation of the `Vec` inside `PropertyDeclarationBlock`. However this is tricky: we need to preserve this "all or nothing" aspect of parsing one source declaration, and at the same time we want to make it as little error-prone as possible for the various call sites. `PropertyDeclarationBlock` curently does property deduplication incrementally: as each `PropertyDeclaration` is pushed, we check if an existing declaration of the same property exists and if so overwrite it. To get rid of the stack allocated array we’d need to somehow deduplicate separately after pushing multiple `PropertyDeclaration`.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16954)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented May 20, 2017

☀️ Test successful - android, arm32, arm64, linux-dev, linux-rel-css, linux-rel-wpt, mac-dev-unit, mac-rel-css, mac-rel-wpt1, mac-rel-wpt2, windows-msvc-dev
Approved by: emilio
Pushing 60682cf to master...

@bors-servo bors-servo merged commit 341fc54 into master May 20, 2017
3 of 4 checks passed
3 of 4 checks passed
dependency-ci Failed dependency checks
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
@emilio emilio deleted the arrayvec branch May 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants
You can’t perform that action at this time.