C++ множественное узкое место цикла

моя программа на C++ столкнулась с узким местом. Есть функция обрезки изображения rgb32. Посмотрите на код:

BOOL Convert2MultiImage(BYTE *p32Img, BYTE** p24Img, int dxSize32, int  dySize32, int row, int vertical)
{

BYTE *pTemp;
BYTE** ptr = new BYTE*[row * vertical];

pTemp = p32Img;

for (int num = 0; num < row * vertical; num++)
{
    ptr[num] = p24Img[num];
}

int displayWidth = dxSize32 * vertical;
int width4 = displayWidth * 4;
int width4_y = displayWidth * dySize32 * 4;
int x_4 = dxSize32 * 4;

int p = 0;
int seq = 0;

int  i, j;

for (int r = 0; r < row; r++)
{
    for (int v = 0; v < vertical; v++)
    {
        for (j = 0; j < dySize32; j++)
        {
            for (i = 0; i < dxSize32; i++)
            {
                p = (displayWidth * j + i + r * displayWidth * dySize32 + v * dxSize32) * 4;

                *(ptr[seq]++) = pTemp[p];
                *(ptr[seq]++) = pTemp[p + 1];
                *(ptr[seq]++) = pTemp[p + 2];

            }
        }
        seq++; 
    }
}

delete[] ptr;
ptr = NULL;

return true;
}

Параметр dxSize32 = 1920, dySize32 = 1080, row = 4, vertical = 4; функция означает вырезание из изображения 8K на 16 изображений 1080P.

этот код занимает около 300 мс, а мне нужно около 30 мс, мой процессор компьютера - двухъядерный I5-6400. Могу ли я использовать какое-нибудь оборудование для обработки этого кода, чтобы сократить затрачиваемое время?

СЕЙЧАС я использовал opencv, очень хорошо, теперь это занимает около 30 мс.

void COpencvCutImg::BGR32_Cut_MultiBGR24(BYTE* source, BYTE** target, int   width, int height, int row, int vertical)
{
Mat matSource = Mat(width, height, CV_8UC4, source);

int height1 = matSource.rows;
int width1 = matSource.cols;

int ceil_height = height1 / row;
int ceil_width = width1 / vertical;

Mat roi_img;

int seq = 0;
for (int i = 0; i<row; i++)
for (int j = 0; j<vertical; j++){

    Rect rect(j*ceil_width, i*ceil_height, ceil_width, ceil_height);

    roi_img = matSource(rect);

    Mat continuousBGRA(roi_img.size(), CV_8UC3, target[seq]);
    cv::cvtColor(roi_img, continuousBGRA, CV_BGRA2BGR, 3);

    seq++;
}

}

Скомпилировали с оптимизацией? Для архитектуры вашего процессора (SSE, AVX)? Обратите внимание, что SO не является сервером codereview, для этого нужен codereview.stackexchange.com :)

hellow 20.08.2018 11:16

Выполнение операций, например, Было бы неплохо, если бы для каждого изображения было 16 потоков. 4-вложенные циклы for очень неэффективны

Korni 20.08.2018 11:18

@Korni Вы не получите 10-кратного прироста с потоками, учитывая, что ваш процессор двухъядерный ... лучшее, на что вы можете надеяться, - это удвоение 100% прироста по этому пути.

UKMonkey 20.08.2018 11:19

1. Включите оптимизатор на максимум. 2. Превратите внутренний цикл в memcpy. 3. Рассмотрите возможность развернуть j-образную петлю восемь или 16 раз.

Martin Bonner supports Monica 20.08.2018 11:20

@UKMonkey Если у него два реальных ядра, могут быть доступны еще два «гиперпоточных» ядра, но даже в идеальном мире это только x4, и возможное вмешательство в кэш и т. д. Означает, что мир очень далек от идеала.

Martin Bonner supports Monica 20.08.2018 11:23

Что касается оптимизации кода, вы можете сделать что-то очевидным способом - переместить некоторые части p = (displayWidth * j + i + r * displayWidth * dySize32 + v * dxSize32) * 4; во внешние циклы (например, displayWidth * j можно вычислить вне (до) цикла i) или предварительно рассчитать перед циклами (например, displayWidth * dySize32)

VolAnd 20.08.2018 11:25

Вы также можете рассмотреть возможность метапрограммирования, чтобы легко разворачивать циклы во время компиляции.

Korni 20.08.2018 11:26

@VolAnd - эта часть не будет оптимизирована компилятором, если настройки включены?

user8235810 20.08.2018 11:28

Мне кажется, вы перемещаете около 30 мегабайт. Вам нужно подумать о пропускной способности основной памяти.

Martin Bonner supports Monica 20.08.2018 11:28

@donghui - Что вы имеете в виду, говоря «Могу ли я использовать какое-нибудь оборудование для обработки этого кода, чтобы сократить затрачиваемое время?»? Вы имеете в виду больше RAM / CPU или L1 / L2 кеша?

user8235810 20.08.2018 11:29

Для такого рода операций вы можете использовать OpenCV, он оптимизирован для такого рода операций.

Ricardo Alves 20.08.2018 11:31

@mfromla Да, нет возможности оптимизировать по инструкции ЦП, я помню, что функция внутри библиотеки Intel ipp очень быстрая, я не знаю, как этого добиться.

donghui.R 20.08.2018 11:34

Гиперпотоки @MartinBonner - это не настоящие ядра. Они предлагают второй конвейер к тому же ядру; что может улучшить производительность, но также может и снизить ее; ситуация в зависимости. В этом случае, когда все данные находятся в кеше; Я не вижу, как ядро ​​может стать более эффективным.

UKMonkey 20.08.2018 11:51

@ donghui.R Вы не ответили на вопрос: «Скомпилировали ли вы с оптимизацией?» - Пожалуйста, редактировать ваш вопрос, чтобы включить эту информацию.

Martin Bonner supports Monica 20.08.2018 12:19

Нет никакой гарантии, что несколько потоков будут более эффективными. В худшем случае потоки будут запускаться последовательно на одном ядре (возможно, время тоже поменяно местами). В лучшем случае ядра нужно запланировать для приема потоков, а затем отслеживать их завершение. Другими словами, много накладных расходов на потоки. Кроме того, может быть узкое место с совместным использованием данных между несколькими ядрами или потоками. IMHO, развертывание цикла или использование инструкций с параллельными данными было бы более эффективным.

Thomas Matthews 20.08.2018 16:46

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

Thomas Matthews 20.08.2018 16:48

@ VolAnd Да, я модифицировал код. Время работы сократилось примерно на 5%.

donghui.R 21.08.2018 03:12

@ Ricardo Alves , Спасибо, я использовал opencv, очень хорошо, теперь это занимает около 40 мс.

donghui.R 21.08.2018 10:46

@ donghui.R, вы можете вместо этого добавить свою правку как отвечать, если считаете, что она отвечает на ваш вопрос. Вполне приемлемо ответить на свои вопросы

Default 21.08.2018 11:35

@ donghui.R Пожалуйста, опубликуйте свое решение как ответ на этот вопрос и примите его, чтобы оно не было помечено как неотвеченное.

ivanmoskalev 21.08.2018 13:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
20
109
0

Другие вопросы по теме