Будет ли Rayon избегать порождения темы для небольшого объема работы? - PullRequest
0 голосов
/ 09 февраля 2019

Я думал об использовании функции параллельного итератора Rayon, но меня беспокоит производительность для перебора небольших коллекций.

Параллелизм иногда может привести к замедлению работы небольших коллекций.Повторение двух элементов происходит медленнее, если я выполняю необходимую подготовку к многопоточности, чем если бы я использовал однопоточную версию.Если у меня будет 40 миллионов элементов, параллелизм даст мне линейное улучшение производительности.

Я читаю о ParallelIterator::weight (0.6.0) , но я не понимаю, стоит ли мне оптимизироватьтакие угловые чехлы для небольших коллекций или если Rayon умный и обрабатывает все под крышкой.

if collection_is_small() {
    // Run single threaded version... 
} else {
    // Use parallel iterator.
}

ParallelIterator::weight обработанного элемента равен 1. См. соответствующую документацию дляхорошее определение, но обработка одного элемента стоит дешево.

Google отправил меня на старую страницу документации.Weight устарел, а удален начиная с версии 0.8.0.

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

Weight API устарел в пользу контроль длины разделения .По умолчанию Rayon будет делиться на каждый элемент , эффективно делая все вычисления параллельными, это поведение можно настроить с помощью with_min_len .

Устанавливает минимальную длину итераторовжелательно обрабатывать в каждой теме.Rayon не будет делиться меньше, чем эта длина, но, конечно, итератор может быть уже меньше.

Производители, такие как zip и interleave, будут использовать большее из двух минимумов.Цепные итераторы и итераторы внутри flat_map могут использовать собственную минимальную длину.

extern crate rayon; // 1.0.3
use rayon::prelude::*;
use std::thread;

fn main() {
    println!("Main thread: {:?}", thread::current().id());
    let ids: Vec<_> = (0..4)
        .into_par_iter()
        .with_min_len(4)
        .map(|_| thread::current().id())
        .collect();
    println!("Iterations: {:?}", ids);
}

Вывод:

Main thread: ThreadId(0)
Iterations: [ThreadId(0), ThreadId(0), ThreadId(0), ThreadId(0)]

Детская площадка (спасибо @shepmaster закод)

0 голосов
/ 09 февраля 2019

Эмпирически видно, что такое поведение не гарантируется:

use rayon::prelude::*; // 1.0.3

use std::thread;

fn main() {
    let ids: Vec<_> = (0..2)
        .into_par_iter()
        .map(|_| thread::current().id())
        .collect();
    println!("{:?}", ids);
}

Различные прогоны программы показывают:

[ThreadId(1), ThreadId(2)]
[ThreadId(1), ThreadId(1)]
[ThreadId(2), ThreadId(1)]
[ThreadId(2), ThreadId(2)]

При этом вы должны выполнить свой собственный сравнительный анализ.По умолчанию Rayon создает глобальный пул потоков и использует кражу работы, чтобы сбалансировать работу между потоками.Пул потоков - это единовременная стоимость установки на процесс, а кража работы помогает гарантировать, что работа пересекает границы потоков только при необходимости.Вот почему есть выходы выше, где оба используют один и тот же поток.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...