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

Optimize by introducing stages #33

Merged
merged 2 commits into from
Jun 5, 2017
Merged

Optimize by introducing stages #33

merged 2 commits into from
Jun 5, 2017

Conversation

torkleyy
Copy link
Member

@torkleyy torkleyy commented Jun 4, 2017

This optimization is big! 🎉 Benchmark running time decreased by 74%.

Fixes #6

@torkleyy torkleyy requested a review from kvark June 4, 2017 07:09
src/lib.rs Outdated
@@ -58,18 +58,18 @@ extern crate fnv;
extern crate mopa;
#[cfg(not(target_os = "emscripten"))]
extern crate pulse;
extern crate rayon;
#[cfg(not(target_os = "emscripten"))]
extern crate rayon_core;
Copy link
Contributor

Choose a reason for hiding this comment

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

rayon_core probably not needed anymore?

Copy link
Member Author

Choose a reason for hiding this comment

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

Given that rayon_core is stable and rayon is not, we can just use rayon_core where possible I guess.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well rayon itself uses rayon_core, and reexports all parts of it, so using this parts from rayon_core is all the same.

{
let id = self.id_gen.next();
let mut reads: Vec<_> = unsafe { T::SystemData::reads(0) };
let writes = unsafe { T::SystemData::writes(0) };
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are these calls unsafe? Yes if returned ids are invalid it will cause a undefined behavior, but i doesn't mean that it must be marked as unsafe.

Copy link
Member Author

@torkleyy torkleyy Jun 4, 2017

Choose a reason for hiding this comment

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

You're right, this should be changed. My initial version of shred didn't check borrow rules in release mode, but it does now..let's remove the unsafe.

Copy link
Member Author

Choose a reason for hiding this comment

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

It won't be undefined behaviour. If it was, unsafe would be just right.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually data race is undefined behavior.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it is, but we don't have any data races, it panics.

This optimization is big! 🎉
Benchmark running time decreased by 74%
Copy link
Member

@WaDelma WaDelma left a comment

Choose a reason for hiding this comment

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

Nice to see so good speed up!

}

/// Adds a new system with a given name and a list of dependencies.
/// Please not that the dependency should be added before
Copy link
Member

Choose a reason for hiding this comment

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

s/not/note

reads.sort();
reads.dedup();

let mut reads = SmallVec::from_vec(reads);
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason that SystemData::reads and SystemData::writes don't return SmallVecs?

Copy link
Member Author

Choose a reason for hiding this comment

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

Two reasons:

  • Didn't want to make it a public dependency
  • The system data may read from a resource several times. Therefore, the stack storage would often be too small at first, but not necessary anymore later. That's why I do the conversion after removing duplicates

I didn't yet check if it makes sense to use SmallVec for reads / writes at all. It's only used for building the stages, after all.

{
let id = self.id_gen.next();
let mut reads: Vec<_> = T::SystemData::reads(0);
let writes = T::SystemData::writes(0);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't these 0s be id instead? Or am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for reviewing, @WaDelma!

My code is correct, however it's not obvious why I did it like that, because I never wrote my design decision down.

First of all, in case you don't know, system data most likely consists of other structs implementing SystemData, so for example:

#[derive(SystemData)]
struct Data<'a> {
    a: ReadStorage<'a, A>,
    b: WriteStorage<'a, B>,
}

Now, I'd like resources to have additional ids (in Specs these were called component ids). So fetch / reads / writes should be parameterized over this resource id. However, the system data directly associated with a system, doesn't need such an id. It just gets passed 0 for that reason.

Shouldn't these 0s be id instead?

The id you're seeing here is the system id, which is totally unrelated to the resource id.

let dependencies: SystemDependencies = dep.iter()
.map(|x| {
*self.map
.get(x.to_owned())
Copy link
Member

Choose a reason for hiding this comment

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

Because how HashMap::get is defined &str can be used for HashMap<String, _>::get and to_owned is unnessary here.

@torkleyy
Copy link
Member Author

torkleyy commented Jun 4, 2017

I got another idea how to optimize this even more, hold on.

@torkleyy
Copy link
Member Author

torkleyy commented Jun 5, 2017

Yeah! I finally got it finished.

@torkleyy torkleyy force-pushed the opt branch 2 times, most recently from 457f86c to f8ae782 Compare June 5, 2017 16:55

(self.barrier..self.stages.len())
.map(|stage| {
let conflict = Self::find_conflict(&*self.ids,
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 this is not just self.find_conflict(stage, new_reads.clone(), new_writes.clone(), new_dep)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, easier to test.

.find(|&(stage, conflict)| match conflict {
Conflict::None => true,
Conflict::Single(group) => {
self.stages[stage].groups[group].len() < MAX_SYSTEMS_PER_GROUP - 1 &&
Copy link
Member

Choose a reason for hiding this comment

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

why MAX_SYSTEMS_PER_GROUP - 1 ?

Copy link
Member Author

Choose a reason for hiding this comment

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

The ArrayVec only supports up to MAX_SYSTEMS_PER_GROUP, so any additional system would just be discarded. So I check if there's enough space left.

.map(|(stage, conflict)| match conflict {
Conflict::None => InsertionTarget::Stage(stage),
Conflict::Single(group) => InsertionTarget::Group(stage, group),
_ => unreachable!(),
Copy link
Member

Choose a reason for hiding this comment

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

let's have Conflict::Multiple here explicitly instead of _

fn remove_ids(&self, stage: usize, new_dep: &mut SmallVec<[SystemId; 4]>) {
if !new_dep.is_empty() {
for id in self.ids[stage].iter().flat_map(|id_group| id_group) {
if let Some(index) = new_dep.iter().position(|x| *x == *id) {
Copy link
Member

Choose a reason for hiding this comment

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

is there SmallVec::retain()?

Copy link
Member Author

Choose a reason for hiding this comment

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

Wanted to use that, but no, unfortunately there isn't.

Add rayon and arrayvec dependencies

Removes rayon_core

Add RunningTime and System::running_time

This allows for further optimizations.

Do not rustfmt macro invocations in system module

Reexport RunningTime

Removed debug printlns

Remove typ annotation

Make match exhaustive
@torkleyy
Copy link
Member Author

torkleyy commented Jun 5, 2017

bors r+

Thanks @Object905 @WaDelma @kvark for reviewing!

bors bot added a commit that referenced this pull request Jun 5, 2017
33: Optimize by introducing stages r=torkleyy
This optimization is big! 🎉 Benchmark running time decreased by 74%.

Fixes #6
@bors
Copy link
Contributor

bors bot commented Jun 5, 2017

Build succeeded

@bors bors bot merged commit e9acf68 into amethyst:master Jun 5, 2017
@torkleyy torkleyy deleted the opt branch June 5, 2017 18:15
bors bot added a commit to amethyst/specs that referenced this pull request Jun 6, 2017
166: Update to shred 0.4 r=torkleyy
Now specs can make use of [a major optimization](amethyst/shred#33) in `shred` and [a bugfix](amethyst/shred#35) in `shred-derive`.

Also improves bench by specifying running time.
bors bot added a commit that referenced this pull request Apr 3, 2019
126: Update hashbrown requirement from 0.1.7 to 0.2.0 r=torkleyy a=dependabot[bot]

Updates the requirements on [hashbrown](https://github.com/Amanieu/hashbrown) to permit the latest version.
<details>
<summary>Changelog</summary>

*Sourced from [hashbrown's changelog](https://github.com/Amanieu/hashbrown/blob/master/CHANGELOG.md).*

> ## [v0.2.0] - 2019-03-31
> 
> ### Changed
> - The code has been updated to Rust 2018 edition. This means that the minimum
>   Rust version has been bumped to 1.31 (2018 edition).
> 
> ### Added
> - Added `insert_with_hasher` to the raw_entry API to allow `K: !(Hash + Eq)`. ([#54](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/54))
> - Added support for using hashbrown as the hash table implementation in libstd. ([#46](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/46))
> 
> ### Fixed
> - Fixed cargo build with minimal-versions. ([#45](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/45))
> - Fixed `#[may_dangle]` attributes to match the libstd `HashMap`. ([#46](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/46))
> - ZST keys and values are now handled properly. ([#46](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/46))
> 
> ## [v0.1.8] - 2019-01-14
> 
> ### Added
> - Rayon parallel iterator support ([#37](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/37))
> - `raw_entry` support ([#31](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/31))
> - `#[may_dangle]` on nightly ([#31](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/31))
> - `try_reserve` support ([#31](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/31))
> 
> ### Fixed
> - Fixed variance on `IterMut`. ([#31](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/31))
> 
> ## [v0.1.7] - 2018-12-05
> 
> ### Fixed
> - Fixed non-SSE version of convert_special_to_empty_and_full_to_deleted. ([#32](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/32))
> - Fixed overflow in rehash_in_place. ([#33](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/33))
> 
> ## [v0.1.6] - 2018-11-17
> 
> ### Fixed
> - Fixed compile error on nightly. ([#29](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/29))
> 
> ## [v0.1.5] - 2018-11-08
> 
> ### Fixed
> - Fixed subtraction overflow in generic::Group::match_byte. ([#28](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/28))
> 
> ## [v0.1.4] - 2018-11-04
> 
> ### Fixed
> - Fixed a bug in the `erase_no_drop` implementation. ([#26](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/26))
> 
> ## [v0.1.3] - 2018-11-01
> 
> ### Added
></tr></table> ... (truncated)
</details>
<details>
<summary>Commits</summary>

- [`af90bc9`](Amanieu/hashbrown@af90bc9) Version 0.2.0
- [`83ce3d5`](Amanieu/hashbrown@83ce3d5) Merge [#46](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/46)
- [`f9bcc42`](Amanieu/hashbrown@f9bcc42) Add #[may_dangle] to RawIntoIter
- [`b11f8cd`](Amanieu/hashbrown@b11f8cd) Upgrade to 2018 edition
- [`2bd15ed`](Amanieu/hashbrown@2bd15ed) Apply feedback from https://github-redirect.dependabot.com/rust-lang/rust/pull/56241
- [`91d3e4a`](Amanieu/hashbrown@91d3e4a) Merge [#55](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/55)
- [`49a9d6c`](Amanieu/hashbrown@49a9d6c) use --exclude-should-panic
- [`c36d49a`](Amanieu/hashbrown@c36d49a) install Miri via rustup
- [`332db4d`](Amanieu/hashbrown@332db4d) Prepare hashbrown for inclusion in the standard library
- [`edf79d0`](Amanieu/hashbrown@edf79d0) Merge [#54](https://github-redirect.dependabot.com/Amanieu/hashbrown/issues/54)
- Additional commits viewable in [compare view](Amanieu/hashbrown@v0.1.7...v0.2.0)
</details>
<br />

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot ignore this [patch|minor|major] version` will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language
- `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme

Additionally, you can set the following in your Dependabot [dashboard](https://app.dependabot.com):
- Update frequency (including time of day and day of week)
- Automerge options (never/patch/minor, and dev/runtime dependencies)
- Pull request limits (per update run and/or open at any time)
- Out-of-range updates (receive only lockfile updates, if desired)
- Security updates (receive only security updates, if desired)

Finally, you can contact us by mentioning @dependabot.

</details>

Co-authored-by: dependabot[bot] <[email protected]>
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.

4 participants