моя программа на 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++;
}
}
Выполнение операций, например, Было бы неплохо, если бы для каждого изображения было 16 потоков. 4-вложенные циклы for очень неэффективны
@Korni Вы не получите 10-кратного прироста с потоками, учитывая, что ваш процессор двухъядерный ... лучшее, на что вы можете надеяться, - это удвоение 100% прироста по этому пути.
1. Включите оптимизатор на максимум. 2. Превратите внутренний цикл в memcpy. 3. Рассмотрите возможность развернуть j-образную петлю восемь или 16 раз.
@UKMonkey Если у него два реальных ядра, могут быть доступны еще два «гиперпоточных» ядра, но даже в идеальном мире это только x4, и возможное вмешательство в кэш и т. д. Означает, что мир очень далек от идеала.
Что касается оптимизации кода, вы можете сделать что-то очевидным способом - переместить некоторые части p = (displayWidth * j + i + r * displayWidth * dySize32 + v * dxSize32) * 4; во внешние циклы (например, displayWidth * j можно вычислить вне (до) цикла i) или предварительно рассчитать перед циклами (например, displayWidth * dySize32)
Вы также можете рассмотреть возможность метапрограммирования, чтобы легко разворачивать циклы во время компиляции.
@VolAnd - эта часть не будет оптимизирована компилятором, если настройки включены?
Мне кажется, вы перемещаете около 30 мегабайт. Вам нужно подумать о пропускной способности основной памяти.
@donghui - Что вы имеете в виду, говоря «Могу ли я использовать какое-нибудь оборудование для обработки этого кода, чтобы сократить затрачиваемое время?»? Вы имеете в виду больше RAM / CPU или L1 / L2 кеша?
Для такого рода операций вы можете использовать OpenCV, он оптимизирован для такого рода операций.
@mfromla Да, нет возможности оптимизировать по инструкции ЦП, я помню, что функция внутри библиотеки Intel ipp очень быстрая, я не знаю, как этого добиться.
Гиперпотоки @MartinBonner - это не настоящие ядра. Они предлагают второй конвейер к тому же ядру; что может улучшить производительность, но также может и снизить ее; ситуация в зависимости. В этом случае, когда все данные находятся в кеше; Я не вижу, как ядро может стать более эффективным.
@ donghui.R Вы не ответили на вопрос: «Скомпилировали ли вы с оптимизацией?» - Пожалуйста, редактировать ваш вопрос, чтобы включить эту информацию.
Нет никакой гарантии, что несколько потоков будут более эффективными. В худшем случае потоки будут запускаться последовательно на одном ядре (возможно, время тоже поменяно местами). В лучшем случае ядра нужно запланировать для приема потоков, а затем отслеживать их завершение. Другими словами, много накладных расходов на потоки. Кроме того, может быть узкое место с совместным использованием данных между несколькими ядрами или потоками. IMHO, развертывание цикла или использование инструкций с параллельными данными было бы более эффективным.
Возможно, лучшим методом была бы оптимизация функции для доступа к кешу данных. Вы можете повысить эффективность, уменьшив перезагрузку кэша данных. Точно так же увеличение соотношения инструкций данных к инструкциям перехода / перехода повысит эффективность.
@ VolAnd Да, я модифицировал код. Время работы сократилось примерно на 5%.
@ Ricardo Alves , Спасибо, я использовал opencv, очень хорошо, теперь это занимает около 40 мс.
@ donghui.R, вы можете вместо этого добавить свою правку как отвечать, если считаете, что она отвечает на ваш вопрос. Вполне приемлемо ответить на свои вопросы
@ donghui.R Пожалуйста, опубликуйте свое решение как ответ на этот вопрос и примите его, чтобы оно не было помечено как неотвеченное.





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