Использование rand () и rand_r (): правильный ли это простой пример? - PullRequest
1 голос
/ 21 октября 2019

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

Ради того, чтобы указать на разницу и отношения между rand () и rand_r(), давайте решим:

Создайте случайное целое число N, затем извлеките N случайных чисел параллельно и вычислите их среднее значение.

Это мое предложение (проверка и бесплатнаяпропущено), целые маленькие числа:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <omp.h>

int main() {
        /* Initialize and extract an integer via rand() */
        srand(time(NULL));
        int N = rand() % 100;

        /* Storage array */ 
        int *extracted = malloc(sizeof(int) * N);

        /* Initialize N seeds for rand_r, which is completely
         * independent on rand and srand().
         * (QUESTION 1: is it right?)
         * Setting the first as time(NULL), and the others
         * via successive increasing is a good idea (? QUESTION 2)*/
        unsigned int *my_seeds = malloc(sizeof(unsigned int) * N);
        my_seeds[0] = time(NULL);
        for (int i = 1; i < N; ++i) {
                my_seeds[i] = my_seeds[i - 1] + 1;
        }

        /* The seeds for rand_r are ready:
         * extract N random numbers in parallel */
        #pragma omp parallel for
        for (int i = 0; i < N; ++i) {
                extracted[i] = rand_r(my_seeds + i) % 10;
        }

        /* Compute the average: must be done sequentially, QUESTION 3,
         * because of time-sincronization in reading/writing avg */
        double avg = 0;
        for (int i = 0; i < N; ++i) {
                avg += extracted[i];
        }
        avg /= N;
        printf("%d samples, %.2f in average.\n", N, avg);
        return 0;
}

Поскольку мои комментарии в коде пытаются выделить, было бы полезно понять, если:

  1. одновременное использованиеrand и rand_r в этом случае корректны;

  2. инициализация семени для rand_r, то есть переменная my_seeds, в порядке;

  3. дляраспараллеливание и использование связанных переменных безопасны.

Я надеюсь обобщить различные сомнения в одном простом, готовом к использованию примере, прочитав различные учебные пособия / источникиine (этот веб-сайт включен).

Ответы [ 2 ]

1 голос
/ 21 октября 2019
  1. одновременное использование rand и rand_r в этом случае является правильным;

Пока:

  • rand не используется одновременно (что в вашем коде является нормальным - вы вызываете его только один раз в главном потоке)
  • rand_r с той же переменной начального числане используется одновременно (что в вашем коде нормально - вы вызываете его только один раз для каждой начальной переменной)

проблем с безопасностью потоков нет.

инициализация семени для rand_r, то есть для переменной my_seeds, в порядке;

У вас есть отдельное семя для каждого (потенциально) одновременного использования rand_r. Пока та же самая начальная переменная не используется для одновременных вызовов rand_r (что в вашем коде не происходит), все хорошо.

безопасен для распараллеливания и использования связанных переменных.

Каждый «поток» в вашем коде имеет собственную начальную переменную для rand_r и собственную переменную результата. Таким образом, нет никаких проблем с параллелизмом. что.

Примечание: rand_r устарел, и rand и rand_r имеют относительно низкого качества prng's . В зависимости от ваших потребностей, возможно, стоит изучить альтернативные варианты.

0 голосов
/ 21 октября 2019
  1. Нет ничего неправильного в использовании обоих, если rand не вызывается одновременно.

  2. Неясно, что вы считаете "хорошим" или"хорошая идея". Это хорошо в том смысле, что вы получите разные последовательности случайных чисел, созданные для каждого семени. Это немного бессмысленно в том смысле, что вы генерируете только одно случайное число из каждого начального числа (что означает, что сгенерированные числа, скорее всего, будут следовать очень предсказуемому шаблону, как и ваши начальные числа).

  3. Тамнет условий гонки, так что это безопасно. Распараллеливание для <100 вызовов (предположительно) простого арифметического метода не будет стоить того с точки зрения производительности, но это не то, о чем вы спрашиваете. </p>

В целомэтот код не имеет формальных проблем с корректностью. Выполняет ли он какую-либо цель, которую вы хотели бы выполнить, - это другой вопрос. Обратите внимание, что randrand_r) имеют тенденцию быть только очень поверхностно случайными 1 , поэтому предсказуемость, упомянутая в пункте 2, просто более одинакова. Смотрите также Почему rand ()% 6 смещен? для еще одной проблемы качества случайности в коде. Другими словами, имейте в виду, что случайность, которую вы генерируете здесь, отсутствует во многих приложениях.

1 Предполагая, что unsigned int имеет 32 бита, есть только 32 битасостояние для PRNG, поэтому оно будет повторяться после (максимум) 2 32 вызовов в любом случае (что тривиально для перебора).

...