Параллельное перемешивание в C # 4? - PullRequest
0 голосов
/ 29 августа 2010

Как отмечено в этом вопросе: Рандомизировать список вы можете реализовать метод случайного выбора в списке; как один из ответов упоминает:

using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
    RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
    int n = list.Count;
    while (n > 1)
    {
        byte[] box = new byte[1];
        do provider.GetBytes(box);
        while (!(box[0] < n * (Byte.MaxValue / n)));
        int k = (box[0] % n);
        n--;
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
    }
}

Кто-нибудь знает, возможно ли "Parallel-ize" это использовать некоторые из новых функций в C # 4?

Просто любопытство.

Спасибо всем,

-R.

Ответы [ 3 ]

1 голос
/ 29 августа 2010

Ну, вы могли бы довольно легко распараллелить код, который генерирует случайные числа - который может стать узким местом при использовании криптографически безопасного генератора случайных чисел.Затем вы могли бы использовать эту последовательность случайных чисел (которая должна была бы сохранить свой порядок) в пределах одного потока, выполняющего перестановки.

Одна проблема, которая только что возникла у меня, заключается в том, что RNGCryptoServiceProvider isn 'Потокобезопасен (и не является System.Random).Вам понадобится столько генераторов случайных чисел, сколько потоков, чтобы это работало.В основном это становится немного уродливым: (

1 голос
/ 30 августа 2010

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

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

static RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();

private static byte[] GetBytes() {
        byte[] box = new byte[1];
        do provider.GetBytes(box);
        while (!(box[0] < n * (Byte.MaxValue / n)));
        return box;
}
private static IList<T> InnerLoop(int n, IList<T> list) {
        var box = GetBytes(n);
        int k = (box[0] % n);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
        return list;
}

public static void Shuffle<T>(this IList<T> list)
{
    int n = list.Count;
    while (n > 1)
    {
        list = InnerLoop(n, list);
        n--;
    }
}

Это грубая идея о том, как разделить вашу функцию, так что вы можете заменить функцию GetBytes, но вам может потребоваться внести некоторые другие изменения, чтобы протестировать ее.

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

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

Но если вы хотите сделать несколько списков, это будет легко, возможно, вы захотите посмотреть PLINQ для этого.

UPDATE

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

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

UPDATE:

Основываясь на ответе на мой комментарий, вы можете посмотреть на различные функции в параллельной библиотеке, но эта страница может помочь. http://msdn.microsoft.com/en-us/library/dd537609.aspx.

Вы можете создать Func версию своей функции и передать ее в качестве параметра. Существует несколько способов работы с этой библиотекой для параллельного выполнения этой функции, поскольку у вас уже нет глобальных переменных, просто потеряйте оператор static.

Вы захотите получать числа, когда добавляете больше потоков, хотя, чтобы увидеть, где вы начнете видеть снижение производительности, так как вы не увидите улучшения 1: 1, поэтому, если вы добавите 2 потока, это выиграет ' Это происходит вдвое быстрее, поэтому просто протестируйте и посмотрите, где возникает проблема с наличием большего количества потоков. Поскольку ваша функция связана с процессором, вы можете захотеть иметь только один поток на ядро, что является грубой отправной точкой.

1 голос
/ 29 августа 2010

Любая тасовка на месте не очень подходит для распараллеливания. Тем более, что для перемешивания требуется случайный компонент (вне диапазона), поэтому нет способа локализовать части проблемы.

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