Я пытаюсь ускорить мой png-кодер, используя openmp (omp order pragma), который прекрасно работает с GCC (MinGW).На MSVC результирующее ускорение сильно варьируется.Размер проблемы, над которой я работаю, означает, что распараллеленный цикл for состоит примерно из шести (6) итераций.Запустив это на машине 4C / 8T, я ожидаю, что будет выполнено примерно две «волны» потоков.Это должно занять примерно в два раза больше времени, чем одна итерация на одном ядре.Опять же, это примерно то, что я вижу с GCC, но не с MSVC (занимает примерно 3-4 раза дольше).
Мне удалось отыскать простой пример, демонстрирующий то же поведение.Играя с некоторыми параметрами (количество итераций, время вычислений и т. Д.), Я обнаружил, что MSVC работает в основном противоречиво.Добавление нескольких итераций немного помогает в согласованности, что имеет смысл, но это не то, что я могу сделать в исходной задаче.
#include <windows.h>
#include <stdint.h>
#include <stdio.h>
// Timer stuff...
int64_t get_ts() {
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return li.QuadPart;
}
double get_time_ms(int64_t prev) {
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
double frequency = (double)li.QuadPart;
QueryPerformanceCounter(&li);
return (double)(li.QuadPart-prev)/(frequency*1E-3);
}
#define TIMING_START int64_t _start = get_ts()
#define TIMING_END printf("In %s: %.02fms\n", __FUNCTION__, get_time_ms(_start));
// Takes roughly 1.7ms on an old i7
void mySlowFunc() {
//TIMING_START;
volatile int a = 0;
for(int j = 0; j < 1000001; j++) {
a += j;
}
//TIMING_END;
}
#define NUM_ITER 6
int main(int argc, char* argv[]) {
// baseline for comparison
printf("===== Call on single core:\n");
for(int i = 0; i < 5; i++) {
TIMING_START;
for(int i = 0; i < NUM_ITER; i++) {
mySlowFunc();
}
TIMING_END;
}
printf("===== Call on multiple cores: %d\n", omp_get_max_threads());
for(int i = 0; i < 5; i++) {
TIMING_START;
#pragma omp parallel
{
int y;
#pragma omp for ordered
for(y = 0; y < NUM_ITER; y++) {
mySlowFunc();
#pragma omp ordered
{
volatile int i = 0;
}
}
}
TIMING_END;
}
return 0;
}
Следующие примеры выходных данных находятся на той же машине, в той же ОС (Win10, Intel i7860)
Пример вывода MinGW (-O3 -fopenmp -march = native -mtune = native):
===== Call on single core:
In main: 10.57ms // 6 iterations @1.7ms each; this performs as expected
In main: 10.42ms
In main: 10.57ms
In main: 10.59ms
In main: 10.36ms
===== Call on multiple cores: 8
In main: 4.44ms
In main: 3.53ms
In main: 3.06ms
In main: 3.16ms // roughly 3x increase with 4C/8T. Seems reasonable
In main: 3.10ms
Пример вывода MSVC (/ MD / O2 / Ob2 / openmp):
===== Call on single core:
In misc_ordered: 10.49ms
In misc_ordered: 10.43ms
In misc_ordered: 10.45ms
In misc_ordered: 11.29ms
In misc_ordered: 10.36ms
===== Call on multiple cores: 8
In misc_ordered: 3.29ms // expected
In misc_ordered: 4.02ms
In misc_ordered: 6.26ms // why??? >:-(
In misc_ordered: 6.27ms
In misc_ordered: 6.21ms
Еще раз: обратите внимание, что многократные прогоны с MSVC случайным образом дают результаты от 3 до 6 мс.
Мне кажется, что реализация openmp в MSVC пытается равномерно распределить рабочую нагрузку.Но разве планировщик Windows не должен делать это в любом случае?И если да, то почему он ведет себя по-разному на двух разных исполняемых файлах?В качестве альтернативы, возможно, некоторые потоки ожидают (все еще происходит упорядочение), но как я могу попытаться проверить и исправить это?