Использование 1 потока в многопоточности быстрее, чем однопоточность? - PullRequest
1 голос
/ 24 апреля 2020

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

for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int neighbor = countNeighbors(gamefieldSerial, i, j, width, height);

                if (gamefieldSerial[i][j] == DEAD) {
                    if (neighbor == 3) {
                        gamefieldBuffer[i][j] = LIVE;
                    }
                }
                else if (gamefieldSerial[i][j] == LIVE) {
                    if (neighbor < 2 || neighbor > 3) {
                        gamefieldBuffer[i][j] = DEAD;
                    }
                }
            }
        }

        copyField(gamefieldBuffer, gamefieldSerial, width, height);

countNeighbors() просто считает живые ячейки вокруг текущей ячейки (которая является gamefieldSerial [i] [j]) и возвращается. int** gamefieldBuffer для написания нового поколения игрового поля и copyField() для его применения ( Отделение старого состояния от нового состояния , это было очень полезно).

И в параллельной обработке это идет как ниже (например, мое поле 512 * 512). Я думаю, что каждый поток (4) занимает четверть поля (gamefieldParallel) и использует gamefieldBuffer1, gamefieldBuffer2, gamefieldBuffer3, gamefieldBuffer4 (что точно так же, как gamefieldBuffer), чтобы написать новое поколение четверти игрового поля и применить его с for-l oop.

#pragma omp parallel num_threads(4)
        {
#pragma omp sections firstprivate(gamefieldParallel)
            {
#pragma omp section
                {
                    for (int i = 0; i < 256; i++) {
                        for (int j = 0; j < 256; j++) {
                            int neighbor = countNeighbors(gamefieldParallel, i, j, width, height);

                            if (gamefieldParallel[i][j] == DEAD) {
                                if (neighbor == 3) {
                                    gamefieldBuffer1[i][j] = LIVE;
                                }
                            }
                            else if (gamefieldParallel[i][j] == LIVE) {
                                if (neighbor < 2 || neighbor > 3) {
                                    gamefieldBuffer1[i][j] = DEAD;
                                }
                            }
                        }
                    }
                }

#pragma omp section
                {
                    for (int i = 0; i < 256; i++) {
                        for (int j = 256; j < 512; j++) {
                            int neighbor = countNeighbors(gamefieldParallel, i, j, width, height);

                            if (gamefieldParallel[i][j] == DEAD) {
                                if (neighbor == 3) {
                                    gamefieldBuffer2[i][j] = LIVE;
                                }
                            }
                            else if (gamefieldParallel[i][j] == LIVE) {
                                if (neighbor < 2 || neighbor > 3) {
                                    gamefieldBuffer2[i][j] = DEAD;
                                }
                            }
                        }
                    }
                }

#pragma omp section
                {
                    for (int i = 256; i < 512; i++) {
                        for (int j = 0; j < 256; j++) {
                            int neighbor = countNeighbors(gamefieldParallel, i, j, width, height);

                            if (gamefieldParallel[i][j] == DEAD) {
                                if (neighbor == 3) {
                                    gamefieldBuffer3[i][j] = LIVE;
                                }
                            }
                            else if (gamefieldParallel[i][j] == LIVE) {
                                if (neighbor < 2 || neighbor > 3) {
                                    gamefieldBuffer3[i][j] = DEAD;
                                }
                            }
                        }
                    }
                }

#pragma omp section
                {
                    for (int i = 256; i < 512; i++) {
                        for (int j = 256; j < 512; j++) {
                            int neighbor = countNeighbors(gamefieldParallel, i, j, width, height);

                            if (gamefieldParallel[i][j] == DEAD) {
                                if (neighbor == 3) {
                                    gamefieldBuffer4[i][j] = LIVE;
                                }
                            }
                            else if (gamefieldParallel[i][j] == LIVE) {
                                if (neighbor < 2 || neighbor > 3) {
                                    gamefieldBuffer4[i][j] = DEAD;
                                }
                            }
                        }
                    }
                }
            } // end of sections
        } // end of parallel

        for (int i = 0; i < 256; i++) {
            for (int j = 0; j < 256; j++) {
                gamefieldParallel[i][j] = gamefieldBuffer1[i][j];
            }
        }
        for (int i = 0; i < 256; i++) {
            for (int j = 256; j < 512; j++) {
                gamefieldParallel[i][j] = gamefieldBuffer2[i][j];
            }
        }
        for (int i = 256; i < 512; i++) {
            for (int j = 0; j < 256; j++) {
                gamefieldParallel[i][j] = gamefieldBuffer3[i][j];
            }
        }
        for (int i = 256; i < 512; i++) {
            for (int j = 256; j < 512; j++) {
                gamefieldParallel[i][j] = gamefieldBuffer4[i][j];
            }
        }

По этой причине я попытался следовать этим советам.

  • Отделить старое состояние от нового: поэтому я использую несколько полей .
  • Все ваши потоки могут с радостью делиться старым состоянием (потому что никто не меняет его во время итерации): firstprivate (), чтобы дать каждому потоку копию игрового поля.
  • Разбейте работу на чанки - по одному для каждого потока. Итак, я разделил его на части.
  • Дайте каждому потоку отдельный кусок памяти для хранения его результатов. То есть gamefieldBuffer1, 2, 3, 4.
  • Раз все Темы закончены, у вас есть основной поток, объединяющий все их результаты в одном новом игровом поле. Это сделано для -l oop.

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

Мой второй вопрос: когда я устанавливаю num_threads () в параллельном регионе как 1, а не 4, почему это быстрее, чем последовательная обработка. результат с 4 потоками результат с 1 потоком С изображением выше 4 потока немного быстрее, чем последовательный, но в большинстве случаев он медленнее, чем последовательный.

Я думал использование 1 потока в многопоточности равнозначно однопоточности, но это не так? Я не привык к многоядерному программированию (или многопоточности? что-то с использованием openmp ..: p), чему я учусь в школе в этом семестре. Может я что то не так знаю, верно? Может кто-нибудь сообщить мне об этом?

Спасибо.

...