Jump to content

    

OpenMP + ARM Linux нет увеличения скорости for loop

Здравствуйте! Подскажите пожалуйста почему при подключении openmp не удается уменьшить время работы программы.

Плата imx8 с Cortex A53

Содержимое файла main.cpp

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <math.h>
#include <time.h>
#include <omp.h>

#define CHANNELS 4
using namespace std;
using namespace cv;

int main()
{
    cout << "Start programm" << endl;

    vector<Mat>      matrix_1(CHANNELS);
    vector<Mat>      matrix_2(CHANNELS);
    vector<Mat>      result(CHANNELS);
    Mat rnd_image = Mat(32, 32, CV_32FC1, Scalar(0));
    // matrix initialization

    clock_t start, end;
    double cpu_time_used;
	
	start = clock();
    for (int i = 0; i < CHANNELS; i++) {
        randn(rnd_image, Scalar(0), Scalar(16000));
        rnd_image.copyTo(matrix_1[i]);
        randn(rnd_image, Scalar(0), Scalar(16000));
        rnd_image.copyTo(matrix_2[i]);
        multiply(matrix_1[i], matrix_2[i], result[i]);
    }
    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    cout << "Time taken by for_loop: " << cpu_time_used << " seconds " << endl;
	
    start = clock();
	
	#pragma omp parallel num_threads(2)
	for (int i = 0; i < CHANNELS; i++) {
		randn(rnd_image, Scalar(0), Scalar(16000));
		rnd_image.copyTo(matrix_1[i]);
		randn(rnd_image, Scalar(0), Scalar(16000));
		rnd_image.copyTo(matrix_2[i]);
		multiply(matrix_1[i], matrix_2[i], result[i]);
	}

    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    cout << "Time taken by for_loop with openmp: " << cpu_time_used << " seconds " << endl;
    return 0;
}

Команда для компиляции (флаг -fopenmp добавлен)

 g++ main.cpp -fopenmp -I /usr/include/opencv2 -L /usr/lib -lopencv_core

Прописал переменную 

root@imx8m-var-dart:~/test# printenv OMP_NUM_THREADS
2
root@imx8m-var-dart:~/test#

Компилирую и запускаю программу

root@imx8m-var-dart:~/test# g++ main.cpp -fopenmp -I /usr/include/opencv2     -L /usr/lib     -lopencv_core
root@imx8m-var-dart:~/test# ./a.out
Start programm
Time taken by for_loop: 0.000802 seconds
Time taken by for_loop with openmp: 0.000789 seconds
root@imx8m-var-dart:~/test#

По идее время на выполнение должно быть в два раза меньше... Что делаю не так?

Share this post


Link to post
Share on other sites
5 minutes ago, Alexey_Rostov said:

По идее время на выполнение должно быть в два раза меньше... Что делаю не так?

Так 4 итерации цикла - маловато для оценки, мягко говоря.

Share this post


Link to post
Share on other sites
3 minutes ago, aaarrr said:

Так 4 итерации цикла - маловато для оценки, мягко говоря.

Но в каждой итерации у меня перемножаются по два массива... Как я понимаю каждое ядро выполняет по одной итерации. Более того, запускаю программу на Windows в Visual studio, прирост в производительности есть.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
8 minutes ago, aaarrr said:

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

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

Увеличил размер перемножаемых массивов до 2048 х 2048

результат

root@imx8m-var-dart:~/test# ./a.out
Start programm
Time taken by for_loop: 0.931792 seconds
Time taken by for_loop with openmp: 1.80278 seconds
root@imx8m-var-dart:~/test#

С openmp время увеличилось в два раза...

Share this post


Link to post
Share on other sites
randn(rnd_image

А ничего, что эта штука - общая переменная для обоих потоков. И они вынуждены вызывать синхронизацию, в результате чего время будет больше, чем без OMP!

Заводите временные переменные прямо внутри цикла.

Share this post


Link to post
Share on other sites
29 minutes ago, Eddy_Em said:

randn(rnd_image

А ничего, что эта штука - общая переменная для обоих потоков. И они вынуждены вызывать синхронизацию, в результате чего время будет больше, чем без OMP!

Заводите временные переменные прямо внутри цикла.

Исправил, вынес инициализацию вне for

 

root@imx8m-var-dart:~/test# ./a.out
Start programm
Time taken by for_loop: 0.05834 seconds
Time taken by for_loop with openmp: 0.100663 seconds
root@imx8m-var-dart:~/test#

без изменений...

Запускаю эту же программу под Ubuntu 16.04

alexey@ubuntu:~/test$ g++ main.cpp -fopenmp -I /usr/include/opencv2 -L /usr/lib -lopencv_core
alexey@ubuntu:~/test$ ./a.out 
Start programm
Time taken by for_loop: 0.091176 seconds 
Time taken by for_loop with openmp: 0.017327 seconds 
alexey@ubuntu:~/test$ 

 

Share this post


Link to post
Share on other sites

А нельзя ли сделать MWE такой, чтобы гадость (opencv) с собой не тащило? Могу на генте проверить.

 

Edited by Eddy_Em

Share this post


Link to post
Share on other sites
7 minutes ago, Eddy_Em said:

А нельзя ли сделать MWE такой, чтобы гадость (opencv) с собой не тащило? Могу на генте проверить.

 

 

Запускаю на Windows в VS 2019, программа работает как и ожидается: использование OpenMP позволило ускорить for в несколько раз. На Ubuntu и на ARM Linux не получается ....

Share this post


Link to post
Share on other sites

Блин! ЗАчем здесь clock? оно ж не время считает!!!

Вот, пожалуйста, мой пример:

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static double dtime(){
    double t;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
    return t;
}

static void mularr0(int *a1, int *a2, int *a3){
        for(int i = 0; i < 1000; ++i) a3[i] = a1[i] * a2[i];
}

static void mularr8(int *a1, int *a2, int *a3){
        #pragma omp parallel for num_threads(8)
        for(int i = 0; i < 1000; ++i) a3[i] = a1[i] * a2[i];
}

void main(){
        int arr1[1000], arr2[1000], arr3[1000];
        for(int i = 0; i < 1000; ++i){ arr1[i] = rand(); arr2[i] = rand();}
        double start = dtime();
        for(int x = 0; x < 1000000; ++x) mularr0(arr1, arr2, arr3);
        printf("1 thread: %gs\n", dtime() - start);
        start = dtime();
        for(int x = 0; x < 1000000; ++x) mularr8(arr1, arr2, arr3);
        printf("8 threads: %gs\n", dtime() - start);
}

Проверяем без openmp:

gcc 1.c && ./a.out 
1 thread: 2.37255s
8 threads: 2.35688s

А теперь - с openmp:

gcc 1.c -fopenmp && ./a.out 
1 thread: 2.40718s
8 threads: 1.47267s

Прикола ради добавил 4 потока, получил:

gcc 1.c -fopenmp && ./a.out 
1 thread: 2.44632s
4 threads: 1.4663s
8 threads: 1.48998s

Хотя у меня 8 "железных" ядер... Но накладные расходы во втором случае все съедают.

Edited by Eddy_Em

Share this post


Link to post
Share on other sites
3 minutes ago, Eddy_Em said:

Блин! ЗАчем здесь clock? оно ж не время считает!!!

Разницу между start и end. На Винде с клоком тоже работает...

 cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

 

Вот вывод консоли для Windows

Start programm
Time taken by for_loop: 0.401 seconds
Time taken by for_loop with openmp: 0.143 seconds
Done !

Размер массивов задал 8192х8192. Выйгрыш во времени более чем в два раза

Share this post


Link to post
Share on other sites

Читаем man clock:

Quote

Функция clock() возвращает приблизительное процессорное время, использованное программой.

И понимаем, что в многопоточном режиме она выдаст СУММАРНОЕ время по всем потокам!

// прикола ради я проверил у себя, заменив dtime() на clock(), реально в многопоточном режиме вышло время значительно больше, т.к. время выполнения было умножено на 8.

Edited by Eddy_Em

Share this post


Link to post
Share on other sites
Just now, Eddy_Em said:

Читаем man clock:

И понимаем, что в многопоточном режиме она выдаст СУММАРНОЕ время по всем потокам!

Спасибо, уже понял, что некорректно ее использовать: в VS по точкам дебага временные затраты другие иллюстрируются.... 

Share this post


Link to post
Share on other sites
4 minutes ago, Alexey_Rostov said:

Вот вывод консоли для Windows

похоже, в мастдайке функция clock() делает что-то другое…

Share this post


Link to post
Share on other sites
13 minutes ago, Eddy_Em said:

Блин! ЗАчем здесь clock? оно ж не время считает!!!

Вот, пожалуйста, мой пример:


#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static double dtime(){
    double t;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
    return t;
}

static void mularr0(int *a1, int *a2, int *a3){
        for(int i = 0; i < 1000; ++i) a3[i] = a1[i] * a2[i];
}

static void mularr8(int *a1, int *a2, int *a3){
        #pragma omp parallel for num_threads(8)
        for(int i = 0; i < 1000; ++i) a3[i] = a1[i] * a2[i];
}

void main(){
        int arr1[1000], arr2[1000], arr3[1000];
        for(int i = 0; i < 1000; ++i){ arr1[i] = rand(); arr2[i] = rand();}
        double start = dtime();
        for(int x = 0; x < 1000000; ++x) mularr0(arr1, arr2, arr3);
        printf("1 thread: %gs\n", dtime() - start);
        start = dtime();
        for(int x = 0; x < 1000000; ++x) mularr8(arr1, arr2, arr3);
        printf("8 threads: %gs\n", dtime() - start);
}

Проверяем без openmp:


gcc 1.c && ./a.out 
1 thread: 2.37255s
8 threads: 2.35688s

А теперь - с openmp:


gcc 1.c -fopenmp && ./a.out 
1 thread: 2.40718s
8 threads: 1.47267s

Прикола ради добавил 4 потока, получил:


gcc 1.c -fopenmp && ./a.out 
1 thread: 2.44632s
4 threads: 1.4663s
8 threads: 1.48998s

Хотя у меня 8 "железных" ядер... Но накладные расходы во втором случае все съедают.

 

 

Спасибо еще раз! Заработало!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now