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

Remove data structure specialization for .zip() iterator #36490

Merged
merged 1 commit into from
Sep 17, 2016

Conversation

bluss
Copy link
Member

@bluss bluss commented Sep 15, 2016

Go back on half the specialization, the part that changed the Zip
struct's fields themselves depending on the types of the iterators.

Previous PR: #33090

This means that the Zip iterator will always carry two usize fields,
which are sometimes unused. If a whole for loop using a .zip() iterator is
inlined, these are simply removed and have no effect.

The same improvement for Zip of for example slice iterators remain, and
they still optimize well. However, like when the specialization of zip
was merged, the compiler is still very sensistive to the exact context.

For example this code only autovectorizes if the function is used, not
if the code in zip_sum_i32 is inserted inline where it was called:

fn zip_sum_i32(xs: &[i32], ys: &[i32]) -> i32 {
    let mut s = 0;
    for (&x, &y) in xs.iter().zip(ys) {
        s += x * y;
    }
    s
}

fn zipdot_i32_default_zip(b: &mut test::Bencher)
{
    let xs = vec![1; 1024];
    let ys = vec![1; 1024];

    b.iter(|| {
        zip_sum_i32(&xs, &ys)
    })
}

Include a test that checks that Zip<T, U> is covariant w.r.t. T and U.

Fixes #35727

@rust-highfive
Copy link
Collaborator

r? @alexcrichton

(rust_highfive has picked a reviewer for you, use r? to override)

@@ -643,7 +643,8 @@ impl<A, B> FusedIterator for Chain<A, B>
pub struct Zip<A, B> {
a: A,
b: B,
spec: <(A, B) as ZipImplData>::Data,
index: usize,
Copy link
Member

Choose a reason for hiding this comment

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

could you add a comment here pointing out that these fields are only used for specialized instantiations of A and B (and point to #35727 in the comment for further explanation)

Copy link
Member Author

Choose a reason for hiding this comment

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

Done with an amend. Thanks

@pnkfelix
Copy link
Member

(hmm, these new github UI changes run risk of bypassing bors. good thing I don't have authority to merge...)

Go back on half the specialization, the part that changed the Zip
struct's fields themselves depending on the types of the iterators.

This means that the Zip iterator will always carry two usize fields,
which are unused. If a whole for loop using a .zip() iterator is
inlined, these are simply removed and have no effect.

The same improvement for Zip of for example slice iterators remain, and
they still optimize well. However, like when the specialization of zip
was merged, the compiler is still very sensistive to the exact context.

For example this code only autovectorizes if the function is used, not
if the code in zip_sum_i32 is inserted inline it was called:

```
fn zip_sum_i32(xs: &[i32], ys: &[i32]) -> i32 {
    let mut s = 0;
    for (&x, &y) in xs.iter().zip(ys) {
        s += x * y;
    }
    s
}

fn zipdot_i32_default_zip(b: &mut test::Bencher)
{
    let xs = vec![1; 1024];
    let ys = vec![1; 1024];

    b.iter(|| {
        zip_sum_i32(&xs, &ys)
    })
}
```

Include a test that checks that Zip<T, U> is covariant w.r.t. T and U.
@bluss bluss force-pushed the zip-slightly-despecialized-edition branch from 07d4213 to af1a3ff Compare September 15, 2016 11:00
@@ -0,0 +1,17 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
Copy link
Member

@nagisa nagisa Sep 15, 2016

Choose a reason for hiding this comment

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

NIT: Name for this test is weirdly general for what it tests. Linked list iterator covariance is tested in libcollections itself for example. Perhaps we could test for zip covariance in libcoretest too?

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's a place to put more iterator tests, for now just this one.

Copy link
Member

Choose a reason for hiding this comment

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

@bluss my experience suggests that people rarely reuse/update tests and instead just add new ones. The only exception I can think of is when some area belongs to one person and they do all the work in it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it has happened before in the various run-pass/sync-send-*.rs files

@nagisa
Copy link
Member

nagisa commented Sep 15, 2016

@pnkfelix the only thing that actually merges anything is clicking the “merge pull request” button AFAICT.

@bluss
Copy link
Member Author

bluss commented Sep 15, 2016

We need to get to grips with the iterator optimization stumbling blocks later. What I understand it's still due to spurious null checks being inserted.

The iterator element is Option<(&T, &T)>, so a null in the first half is equal to None and the end of the iteration. It seems even when the expression is explicitly Some(a, b), the null check is even inserted there, which is nonsensical from a Rust perspective -- Some(a, b) is never None.

@bluss
Copy link
Member Author

bluss commented Sep 15, 2016

Another sidenote, it's really true that iterators of non-references don't have that problem. a.iter().cloned().zip(b.iter().cloned()) has no problem if a, b are slices -- and if the specialization trait is added on Clone, oops.

@brson brson added the beta-nominated Nominated for backporting to the compiler in the beta channel. label Sep 16, 2016
@brson
Copy link
Contributor

brson commented Sep 16, 2016

Nominated since it's a regression fix. Might be better to get it out faster.

@alexcrichton
Copy link
Member

@bors: r+ af1a3ff

@alexcrichton alexcrichton added the beta-accepted Accepted for backporting to the compiler in the beta channel. label Sep 16, 2016
Manishearth added a commit to Manishearth/rust that referenced this pull request Sep 17, 2016
…ition, r=alexcrichton

Remove data structure specialization for .zip() iterator

Go back on half the specialization, the part that changed the Zip
struct's fields themselves depending on the types of the iterators.

Previous PR: rust-lang#33090

This means that the Zip iterator will always carry two usize fields,
which are sometimes unused. If a whole for loop using a .zip() iterator is
inlined, these are simply removed and have no effect.

The same improvement for Zip of for example slice iterators remain, and
they still optimize well. However, like when the specialization of zip
was merged, the compiler is still very sensistive to the exact context.

For example this code only autovectorizes if the function is used, not
if the code in zip_sum_i32 is inserted inline where it was called:

```rust
fn zip_sum_i32(xs: &[i32], ys: &[i32]) -> i32 {
    let mut s = 0;
    for (&x, &y) in xs.iter().zip(ys) {
        s += x * y;
    }
    s
}

fn zipdot_i32_default_zip(b: &mut test::Bencher)
{
    let xs = vec![1; 1024];
    let ys = vec![1; 1024];

    b.iter(|| {
        zip_sum_i32(&xs, &ys)
    })
}
```

Include a test that checks that `Zip<T, U>` is covariant w.r.t. T and U.

Fixes rust-lang#35727
@bors
Copy link
Contributor

bors commented Sep 17, 2016

⌛ Testing commit af1a3ff with merge fb62f4d...

bors added a commit that referenced this pull request Sep 17, 2016
…excrichton

Remove data structure specialization for .zip() iterator

Go back on half the specialization, the part that changed the Zip
struct's fields themselves depending on the types of the iterators.

Previous PR: #33090

This means that the Zip iterator will always carry two usize fields,
which are sometimes unused. If a whole for loop using a .zip() iterator is
inlined, these are simply removed and have no effect.

The same improvement for Zip of for example slice iterators remain, and
they still optimize well. However, like when the specialization of zip
was merged, the compiler is still very sensistive to the exact context.

For example this code only autovectorizes if the function is used, not
if the code in zip_sum_i32 is inserted inline where it was called:

```rust
fn zip_sum_i32(xs: &[i32], ys: &[i32]) -> i32 {
    let mut s = 0;
    for (&x, &y) in xs.iter().zip(ys) {
        s += x * y;
    }
    s
}

fn zipdot_i32_default_zip(b: &mut test::Bencher)
{
    let xs = vec![1; 1024];
    let ys = vec![1; 1024];

    b.iter(|| {
        zip_sum_i32(&xs, &ys)
    })
}
```

Include a test that checks that `Zip<T, U>` is covariant w.r.t. T and U.

Fixes #35727
@bors bors merged commit af1a3ff into rust-lang:master Sep 17, 2016
@bluss bluss deleted the zip-slightly-despecialized-edition branch September 19, 2016 12:40
@brson brson mentioned this pull request Sep 19, 2016
@brson brson removed the beta-nominated Nominated for backporting to the compiler in the beta channel. label Sep 20, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beta-accepted Accepted for backporting to the compiler in the beta channel.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants