Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Aug 17 23:32
    cfallin opened #968
  • Aug 16 01:22
    Byron opened #967
  • Aug 12 20:16
    cuviper labeled #964
  • Aug 11 18:33
    cuviper synchronize #948
  • Aug 11 18:27
    bors[bot] closed #966
  • Aug 11 18:27

    bors[bot] on master

    Document an example with std::t… Merge #966 966: Document an ex… (compare)

  • Aug 11 18:19

    bors[bot] on staging.tmp

    (compare)

  • Aug 11 18:19

    bors[bot] on staging

    Document an example with std::t… Merge #966 966: Document an ex… (compare)

  • Aug 11 18:19

    bors[bot] on staging.tmp

    Document an example with std::t… [ci skip][skip ci][skip netlify… (compare)

  • Aug 11 18:19

    bors[bot] on staging.tmp

    [ci skip][skip ci][skip netlify] (compare)

  • Aug 11 18:17
    cuviper synchronize #948
  • Aug 11 18:14
    cuviper edited #948
  • Aug 11 18:14
    cuviper opened #966
  • Aug 10 07:29
    garysdevil edited #965
  • Aug 10 07:29
    garysdevil edited #965
  • Aug 10 07:28
    garysdevil edited #965
  • Aug 10 07:27
    garysdevil edited #965
  • Aug 10 07:23
    garysdevil opened #965
  • Aug 04 07:57
    benarmstead closed #880
  • Jul 21 07:11
    akhvoshch closed #961
Josh Stone
@jistone:fedora.im
[m]
@forbisc: there's no concept of fairness. the threads use work stealing when they are idle, taking tasks from any other thread's queue, with no visibility into their "origin" like scopes.
task stealing happens at the front of the queue, essentially FIFO, but it's random which thread is stolen from.
broadly speaking, rayon is opportunistic, trying to maximize execution throughput. I think tokio would be a better fit if you're concerned about fairness or latency.
forbisc
@forbisc
Thank you! Makes sense. Rayon does not make any guarantees about which thread will be stolen from (if multiple threads have tasks waiting).
zr40
@zr40:zr40.nl
[m]
I'm iterating over a list of files that each contain a number of items that I'd want to process in parallel. Right now I'm basically doing files.into_par_iter().filter_map(get_items_from_file).flat_map_iter(process_item).collect(). However this way get_items_from_file can only return all the items in a file at once. What would be a way to provide these items to the iterator as soon as they're available?
Josh Stone
@jistone:fedora.im
[m]
zr40: it sounds like get_items_from_file would do well in a flat_map_iter
zr40
@zr40:zr40.nl
[m]
how would that let get_items_from_file return items before it’s done?
Josh Stone
@jistone:fedora.im
[m]
I was thinking as a regular Iterator, but I can't guess more without knowing what that function is doing
zr40
@zr40:zr40.nl
[m]
the function takes one file and returns the N items that are present in the file
Josh Stone
@jistone:fedora.im
[m]
well sure, but can you do that incrementally in Iterator::next?
zr40
@zr40:zr40.nl
[m]
ah, you meant that I could implement an Iterator. Sure I can do that. get_items_from_file is built like a state machine so that shouldn't be too hard to adapt
enjoinedmot
@enjoinedmot:matrix.org
[m]
Hello people! I recently inherited a project with rayon in its dependencies, and I made a patch that changed .into_par_iter() to .into_iter() after finding the proeject wouldn't compile due to a trait bound error.
If anyone with specific knowledge has a moment, could you let me know if this should change at all the result of the logic that was changed? To me it looks like it should only be a performance hit, but I wanted to make sure.
WGH
@WGH:torlan.ru
[m]
So you basically removed parallelism here, no?
9 replies
enjoinedmot
@enjoinedmot:matrix.org
[m]
It does compile and seems to work after the change to .into_iter() Also, this might be out of scope, but actually just satisfying the trait bound and restoring the logic to what it was would be great, but I didn't get success right away. If anyone has general or specific advice I would be very grateful.
It seemed to me like that was the case, but I just jumped into these crates so I thought I might ask an expert. 😄
Dirkjan Ochtman
@djc
you should probably explain more about the trait bound issue so you can just keep it going in parallel :)
enjoinedmot
@enjoinedmot:matrix.org
[m]
I can find the trait bound error...
here's the trait bound error. 48 | pub struct Iter<T> { | ------------------ doesn't satisfy rayon::range::Iter<u32>: Iterator | = note: the following trait bounds were not satisfied: rayon::range::Iter<u32>: Iterator which is required by &mut rayon::range::Iter<u32>: Iterator
The project is complex, so there was some amount of backporting and updating in uneven ways before I came to it. At some point the logic that used .into_par_iter() didn't compile anymore.
Dirkjan Ochtman
@djc
that must have been due to some particular change in the code base?
enjoinedmot
@enjoinedmot:matrix.org
[m]
but it's safe to say that .into_par_iter() is not deterministic..?
the very next line is .step_by(c as usize) - where c is a variable that's set a long way up the stack.
So I would think .step_by() would consume the result of the previous line. Which would be reached in a non-deterministic way, but would in fact always result in the same return.
but I am not an expert so I thought I would ask. 😅
WGH
@WGH:torlan.ru
[m]
usually rayon is used in such way it wouldn't matter
enjoinedmot
@enjoinedmot:matrix.org
[m]
It's better to know I should be concerned with such a change rather than just assuming it's okay though. 😄 thank you @djc and WGH for responding.
I appreciate your crate! I might be spending more time with it soon... 😄
WGH
@WGH:torlan.ru
[m]
I mean it's usually wrapping a bit of logic that's trivially parallelizable, and the results are aggregated in a way it's easy to reason about
What method is called on the resulting iterator, for instance?
enjoinedmot
@enjoinedmot:matrix.org
[m]
.step_by()
WGH
@WGH:torlan.ru
[m]
And then?
enjoinedmot
@enjoinedmot:matrix.org
[m]
.ennumerate()
WGH
@WGH:torlan.ru
[m]
Those are unsubstantial
enjoinedmot
@enjoinedmot:matrix.org
[m]
I think my core question is - will .step_by() ever see anything different by changing to .into_iter() rather than into_par_iter()? If it does, then I need to get into the trait bound issues.
I might need to anyway, but... 😅
If this is a 'fix' that only slows performance, that might mean I could wait to return to this issue
WGH
@WGH:torlan.ru
[m]
It won't, but this is the wrong question, because things down the line might change
But this whole question looks silly: I can imagine a code that breaks from parallelization because it assumes particular determinism
But other way around, i.e. breaking code that never assumed anything by removing nondeterminism... sounds weird
1 reply
enjoinedmot
@enjoinedmot:matrix.org
[m]
thank you WGH
HackerFoo
@hackerfoo:sandbox.hackerfoo.com
[m]
Rayon tests fail on my machine. I'm not sure if it's the new compiler or M1 Mac specific: rayon-rs/rayon#956
Ritchie Vink
@ritchie46

Hi all.. We use rayon extensively in polars and can have multiple levels of par_iter.

Now my understanding is that if the outer par_iter has sufficient groups and the inner par_iter also has many groups we can get stackoverflow due to work stealing.

I believe this can be resolved by setting with_min_len and that is a solution I want to investigate. But before I do so I have a question of another possible path.

Is it possible to retrieve the depth of par_iter calls? . Something like this. This shows two levels of nesting, but we can actually have more.


// this would be level 1
fn inner_work(seq) {
    if par_level == 0 {
        seq.par_iter().some_work()
    } else {
       seq.iter().some_work()
    }
}

// this would be level 0
fn outer_work(seq) {
   seq.par_iter().map(inner_work).collect()
}
wagnerf42
@wagnerf42
hi
we could actually write an adaptor giving you access to that info but i don't think any of the actual adaptors will allow you to retrieve it
outside of manually splitting with rayon::iter::split
Ritchie Vink
@ritchie46
That would be great. Would that be in the scope of the project? It would make it much easier for use to predict if some parallelizing job might sefgault due to SO or not.
Josh Stone
@jistone:fedora.im
[m]
could you pass your own par_level parameter through inner_work/some_work()?
I guess it depends whether you just want par_iter nesting, or the full depth of work-stealing nesting -- but we don't really track that
Ritchie Vink
@ritchie46

could you pass your own par_level parameter through inner_work/some_work()?

In this trivial example we can. But in polars we can have aribitrary nesting as expressions may call expressions. It is very hard to track in there are so many operation that may par_iter and may be very deep.

I guess it depends whether you just want par_iter nesting, or the full depth of work-stealing nesting -- but we don't really track

I am only interested in the par_iter nesting. Then we could apply a rule of thumb that the outer level has parallelization priority and can make the inner levels 1..n sequential.