OpenCV: цикл по всем пик­се­лям изоб­ра­же­ния и сов­ме­ще­ние ука­за­те­лей

За­ча­стую мы про­грам­ми­ру­ем «как удоб­но», и счи­та­ем, что это за­да­ча ком­пи­ля­то­ра — ге­не­ри­ро­вать эф­фек­тив­ный код. При этом мы за­бы­ва­ем, на­сколь­ко тя­жёл труд ком­пи­ля­то­ра. Ведь он дол­жен ге­не­ри­ро­вать в первую оче­редь кор­рект­ный код, и уже во вто­рую оче­редь — быст­рый. По­это­му мно­гие оп­ти­ми­за­ции ком­пи­ля­тор при­ме­ня­ет очень осто­рож­но, и от­клю­ча­ет их при ма­лей­шем по­до­зре­нии на не­при­ме­ни­мость. В свя­зи с этим ком­пи­ля­то­ру нуж­но по­мо­гать, тем бо­лее, что ино­гда это сде­лать со­всем про­сто.

Рас­смот­рим за­да­чу об­хо­да всех пик­се­лей изоб­ра­же­ния cv::Mat ти­па CV_8UC1 (один unsigned char на пик­сель) на при­ме­ре ин­вер­ти­ро­ва­ния цве­тов. Ком­пи­ли­ро­вать бу­дем в Visual Studio 2010 с оп­ци­ей оп­ти­ми­за­ции /O2.

Пер­вая по­пыт­ка: Mat::at

Ти­пич­ная реа­ли­за­ция вы­гля­дит сле­дую­щим об­ра­зом:

void invert(cv::Mat &image) //image.type() == CV_8UC1
{
    for(int y(0); y < image.rows; ++y)
    {
        for(int x(0); x < image.cols; ++x)
            image.at<unsigned char>(y, x) = 255 - image.at<unsigned char>(y, x);
    }
}

Ли­стинг 1. Про­стая реа­ли­за­ция ин­вер­ти­ро­ва­ния од­но­ка­наль­но­го изоб­ра­же­ния в OpenCV

Срав­не­ние вре­мён ра­бо­ты раз­лич­ных вер­сий ко­да при­ве­де­но в кон­це ста­тьи.

К че­му здесь мож­но при­драть­ся? Кто-то мо­жет ска­зать, что на каж­дой ите­ра­ции цик­ла про­ис­хо­дит дву­крат­ный вы­зов функ­ции cv::Mat::at<>(), и же­ла­тель­но за­пом­нить ссыл­ку, воз­вра­ща­е­мую этой функ­ци­ей, во вре­мен­ную пе­ре­мен­ную, что­бы обой­тись без дву­крат­но­го вы­зо­ва. На са­мом де­ле вы­зо­вов функ­ции at по­сле ком­пи­ля­ции не оста­нет­ся. Эти функ­ции встраи­ва­ют­ся в вы­зы­ваю­щий код (inline expansion), а дву­крат­ное вы­чис­ле­ние од­но­го и то­го же ад­ре­са ком­пи­ля­тор за­ме­ня­ет на од­но­крат­ное вы­чис­ле­ние.

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

$LL3@invert:
//Вычисление адреса пикселя unsigned char *p (функция at)
mov edx, DWORD PTR [eax+44]
mov edx, DWORD PTR [edx]
imul edx, edi
add edx, DWORD PTR [eax+16]
lea esi, DWORD PTR [edx+ecx]

or dl, 255             //dl = 255
sub dl, BYTE PTR [esi] //dl -= *p
inc ecx                //++x
mov BYTE PTR [esi], dl //*p = dl

mov edx, DWORD PTR [eax+12] //edx = image.cols
cmp ecx, edx                //x < edx ?
jl SHORT $LL3@invert        //Если да, то повторяемм цикл

Ли­стинг 2. Ас­сем­блер­ный код, сге­не­ри­ро­ван­ный ком­пи­ля­то­ром Visual Studio 2010
для внут­рен­не­го цик­ла про­грам­мы ли­стин­га 1

Для на­гляд­но­сти я раз­бил ас­сем­блер­ные ин­ст­рук­ции на 3 бло­ка:

  1. вы­чис­ле­ние ад­ре­са пик­се­ля (функ­ция at), на­зо­вём этот ад­рес unsigned char *p;
  2. мо­дифи­ка­ция пик­се­ля *p = 255 - *p;
  3. на­ра­щи­ва­ние счёт­чи­ка цик­ла и про­вер­ка усло­вия вы­хо­да из цик­ла.

Ин­те­рес­но, что ин­ст­рук­ция inc ecx (на­ра­щи­ва­ние счёт­чи­ка цик­ла) на са­мом де­ле при­над­ле­жит тре­тье­му бло­ку. Ком­пи­ля­тор по­ста­вил эту ин­ст­рук­цию в се­ре­ди­ну вы­чис­ле­ния вы­ра­же­ния *p = 255 - *p, ви­ди­мо, для то­го, что­бы чем-то за­нять про­цес­сор на слу­чай про­ма­ха кэ­ша при вы­пол­не­нии чте­ния па­мя­ти sub dl, BYTE PTR [esi].

Итак, ка­кие здесь про­бле­мы? В гла­за бро­са­ют­ся 6 об­ра­ще­ний к па­мя­ти за од­ну ите­ра­цию цик­ла, 3 из ко­то­рых при­хо­дят­ся на функ­цию at (опе­ра­ция lea esi, DWORD PTR [edx+ecx] об­ра­ще­ни­ем к па­мя­ти не яв­ля­ет­ся; это про­сто из­вра­щён­ный спо­соб за­пи­сать в ре­гистр сум­му двух дру­гих ре­ги­стров). Мы зна­ем, что об­ра­ще­ние к па­мя­ти — наи­боль­шее зло, ибо оно мо­жет при­ве­сти к про­ма­ху кэ­ша и про­стою кон­вейе­ра.

Что­бы по­нять при­чи­ну об­ра­ще­ний, посмот­рим на ис­ход­ный код функ­ции at (я убрал ужас­ный assert, ко­то­рый там был):

template<typename _Tp> inline _Tp& Mat::at(int i0, int i1)
{
    return ((_Tp*)(data + step.p[0]*i0))[i1];
}

Ли­стинг 3. Ис­ход­ный код ис­поль­зу­е­мо­го ва­ри­ан­та функ­ции Mat::at

В прин­ци­пе, всё по­нят­но: к ад­ре­су пер­во­го пик­се­ля изоб­ра­же­ния при­бав­ля­ет­ся дли­на стро­ки в бай­тах, умно­жен­ная на но­мер стро­ки, и но­мер столб­ца, умно­жен­ный на раз­мер пик­се­ля. По­след­нее умно­же­ние вы­пол­ня­ет­ся опе­ра­ци­ей ин­дек­са­ции и в на­шем слу­чае от­сут­ству­ет в ас­сем­блер­ном ко­де, так как раз­мер пик­се­ля ра­вен еди­ни­це (unsigned char).

Наш враг — сов­ме­ще­ние ука­за­те­лей

Но за­чем каж­дый раз пе­ре­чи­ты­вать все эти чис­ла из па­мя­ти? Ведь не бу­дет же, на­при­мер, ши­ри­на изоб­ра­же­ния ме­нять­ся по ме­ре его ин­вер­ти­ро­ва­ния! Или бу­дет?

На са­мом де­ле по ме­ре об­ра­бот­ки изоб­ра­же­ния его ши­ри­на (и дру­гие па­ра­мет­ры) мо­гут из­ме­нить­ся! Для это­го до­ста­точ­но пе­ред вы­зо­вом функ­ции invert рас­по­ло­жить бу­фер па­мя­ти изоб­ра­же­ния по­верх ст­рук­ту­ры cv::Mat &image (ад­рес бу­фе­ра хра­нит­ся в по­ле cv::Mat::data ти­па uchar*). То есть ши­ри­на и вы­со­та об­ра­ба­ты­ва­е­мо­го изоб­ра­же­ния, ад­рес его пер­во­го пик­се­ля, дли­на стро­ки (не все­гда рав­на ши­ри­не), и дру­гие па­ра­мет­ры мо­гут яв­лять­ся пик­се­ля­ми это­го са­мо­го изоб­ра­же­ния и, зна­чит, из­ме­нить­ся при его ин­вер­ти­ро­ва­нии. О та­ком не­ве­ро­ят­ном сце­на­рии мы да­же по­ду­мать не мог­ли, а за­бот­ли­вый ком­пи­ля­тор всё преду­смот­рел и сге­не­ри­ро­вал ужас­но не­эф­фек­тив­ный, но кор­рект­ный код.

По по­во­ду сов­ме­ще­ния ука­за­те­лей смот­ри­те так­же эту ста­тью на рус­ском язы­ке.

Яв­ле­ние, ко­то­рое я опи­сал, на­зы­ва­ет­ся сов­ме­ще­ни­ем ука­за­те­лей (pointer aliasing). Воз­мож­ность та­ко­го сов­ме­ще­ния — од­на из ос­нов­ных про­блем, ме­шаю­щих ком­пи­ля­то­ру про­из­во­дить оп­ти­ми­за­ции. Что­бы убе­дить­ся, что имен­но сов­ме­ще­ние ука­за­те­лей яв­ля­ет­ся при­чи­ной пло­хо­го ко­да, рас­смот­рим функ­цию, не про­из­во­дя­щую мо­дифи­ка­цию изоб­ра­же­ния:

int sum(cv::Mat image) //image.type() == CV_8UC1
{
    int sum(0);
    for(int y(0); y < image.rows; ++y)
    {
        for(int x(0); x < image.cols; ++x)
            sum += image.at<unsigned char>(y, x);
    }
    return sum;
}

Ли­стинг 4. Мо­дифи­ка­ция изоб­ра­же­ния убра­на.
Об­ра­ти­те вни­ма­ние на то, что изоб­ра­же­ние пе­ре­да­ёт­ся не по ссыл­ке

$LL3@invert:
movzx ebx, BYTE PTR [ecx+eax]
inc eax
add esi, ebx
cmp eax, edx
jl SHORT $LL3@invert

Ли­стинг 5. Ас­сем­блер­ный код, сге­не­ри­ро­ван­ный ком­пи­ля­то­ром
для внут­рен­не­го цик­ла ли­стин­га 4. Срав­ни­те с ли­стин­гом 2

Кру­то, не прав­да ли? От цик­ла остал­ся лишь не­об­хо­ди­мый ми­ни­мум ин­ст­рук­ций: един­ствен­ное об­ра­ще­ние к па­мя­ти, сум­ми­ро­ва­ние, на­ра­щи­ва­ние счёт­чи­ка цик­ла, и про­вер­ка за­вер­ше­ния цик­ла.

От­ве­чаю на на­зрев­ший у вас во­прос: пе­ре­да­ча изоб­ра­же­ния не по ссыл­ке в ко­де из ли­стин­га 1 по­чти не ме­ня­ет ас­сем­блер­ный код внут­рен­не­го цик­ла: там про­сто не­мно­го из­ме­нит­ся ад­ре­са­ция.

Но что­бы по­лу­чить от ком­пи­ля­то­ра столь эф­фек­тив­ный код, при­шлось по­жерт­во­вать пе­ре­да­чей cv::Mat по ссыл­ке. Не­смот­ря на то, что cv::Mat осна­щён счёт­чи­ком ссылок, и ко­пи­ро­ва­ние объ­ек­та это­го клас­са не озна­ча­ет ко­пи­ро­ва­ние его дан­ных, на­клад­ные рас­хо­ды всё ещё су­ще­ствен­ны, и при­мер­но рав­ны вре­ме­ни об­ра­бот­ки (сум­ми­ро­ва­ния) мат­ри­цы 100×100 пик­се­лей. По­пыт­ка пе­ре­дать объ­ект по ссыл­ке (пусть да­же кон­стант­ной) при­ве­дёт к ге­не­ра­ции ко­да, ана­ло­гич­но­го ли­стин­гу 2. Ви­ди­мо, ком­пи­ля­тор опа­са­ет­ся, что па­ра­мет­ры объ­ек­та бу­дут из­ме­не­ны извне во вре­мя его об­ра­бот­ки.

Стро­гое сов­ме­ще­ние ука­за­те­лей

Мож­но по­ду­мать, что про­бле­мы оп­ти­ми­за­ции, свя­зан­ные с сов­ме­ще­ни­ем ука­за­те­лей, ушли с вве­де­ни­ем стро­го­го сов­ме­ще­ния ука­за­те­лей (strict pointer aliasing) в но­вых стан­дар­тах Си (C99) и Си++ (C++03). Вкрат­це, стро­гое сов­ме­ще­ние ука­за­те­лей — это ко­гда толь­ко ука­за­те­ли од­но­го ти­па ком­пи­ля­тор счи­та­ет по­до­зри­тель­ны­ми на сов­ме­ще­ние. Ис­клю­че­ние — ука­затель на char, ко­то­ро­му раз­ре­ше­но на­кла­ды­вать­ся на дру­гие ука­за­те­ли.

Си­ту­а­ция на­по­ми­на­ет си­ту­а­цию с ку­чей кри­вых сай­тов, сво­им су­ще­ство­ва­ни­ем сдер­жи­ваю­щих раз­ви­тие бра­у­зе­ров.

Од­на­ко боль­шое ко­ли­че­ство ко­да (осо­бен­но низ­ко­уров­не­во­го) за­ви­сит от воз­мож­но­сти не­стро­го­го сов­ме­ще­ния. Вся биб­лио­те­ка OpenCV про­сто рас­сы­пет­ся при ком­пи­ля­ции с вклю­чён­ным стро­гим сов­ме­ще­ни­ем, имен­но по­это­му её нуж­но ком­пи­ли­ро­вать с клю­чём -fno-strict-aliasing (при ис­поль­зо­ва­нии GCC). К сча­стью для поль­зо­ва­те­лей Windows, ком­пи­ля­тор Visual Studio не под­дер­жи­ва­ет strict aliasing, по­это­му от­клю­чать его не при­хо­дит­ся.

По­это­му бу­дем ис­кать дру­гие пу­ти по­лу­че­ния оп­ти­маль­но­го ко­да, от­лич­ные от вклю­че­ния strict aliasing в ком­пи­ля­то­ре.

Ис­поль­зу­ем Mat::ptr

Ещё раз озву­чу вы­яс­нен­ную на­ми при­чи­ну ге­не­ра­ции не­опти­маль­но­го ко­да (ли­стинг 2): ком­пи­ля­тор опа­са­ет­ся, что не­ко­то­рые па­ра­мет­ры изоб­ра­же­ния из­ме­нят­ся во вре­мя ра­бо­ты внут­рен­не­го цик­ла, из-за че­го по­сто­ян­но пе­ре­чи­ты­ва­ет их из па­мя­ти, что, в свою оче­редь, ве­дёт к не­воз­мож­но­сти вы­но­са по­вто­ряю­щих­ся вы­чис­ле­ний (на­при­мер, умно­же­ния но­ме­ра те­ку­щей стро­ки на её раз­мер) за пре­де­лы внут­рен­не­го цик­ла, так как ре­зуль­тат этих вы­чис­ле­ний мо­жет (по мне­нию ком­пи­ля­то­ра) в лю­бой мо­мент из­ме­нить­ся.

Те­перь, ко­гда из­ве­ст­на при­чи­на, с ней мож­но бо­роть­ся. Для это­го нуж­но ско­пи­ро­вать ча­сто ис­поль­зуе­мые во внут­рен­нем цик­ле дан­ные в ло­каль­ные пе­ре­мен­ные. Эти пе­ре­мен­ные, бу­дучи со­зда­ны по­сле ис­поль­зу­е­мо­го ука­за­те­ля (ука­за­те­ля на дан­ные изоб­ра­же­ния cv::Mat::data), оче­вид­но яв­ля­ют­ся от ука­за­те­ля не­за­ви­си­мы­ми (с точ­ки зре­ния ком­пи­ля­то­ра), и сов­ме­ще­ние не­воз­мож­но.

Ес­ли посмот­реть ис­ход­ный код функ­ции Mat::at (ли­стинг 3), то по­лу­ча­ет­ся, что нам на­до со­хра­нить в ло­каль­ные пе­ре­мен­ные зна­че­ния image.data и image.step.p[0], а сам ис­ход­ный код вста­вить в те­ло цик­ла. В об­щем, это пло­хое ре­ше­ние.

К сча­стью, есть го­раз­до бо­лее под­хо­дя­щая функ­ция Mat::ptr, ко­то­рую мож­но ис­поль­зо­вать за пре­де­ла­ми внут­рен­не­го цик­ла (и, зна­чит, мож­но не вол­но­вать­ся об эф­фек­тив­но­сти ге­не­ри­ру­е­мо­го для неё ко­да). Эта функ­ция воз­вра­ща­ет ука­затель на пер­вый пик­сель нуж­ной нам стро­ки:

void invert(cv::Mat image) //image.type() == CV_8UC1
{
    for(int y(0); y < image.rows; ++y)
    {
        unsigned char *const scanLine( image.ptr<unsigned char>(y) );

        for(int x(0); x < image.cols; ++x)
            scanLine[x] = 255 - scanLine[x];
    }
}

Ли­стинг 6. Ис­поль­зу­ем функ­цию ptr

$LL3@invert:
or  dl, 255
sub dl, BYTE PTR [eax+ecx]
inc eax
mov BYTE PTR [eax+ecx-1], dl
mov edx, DWORD PTR [esi+12]
cmp eax, edx
jl  SHORT $LL3@invert

Ли­стинг 7. Ас­сем­блер­ный код внут­рен­не­го цик­ла из ли­стин­га 6

Об­ра­ти­те вни­ма­ние на то, как на­стой­чи­во ком­пи­ля­тор пы­та­ет­ся вста­вить на­ра­щи­ва­ние счёт­чи­ка цик­ла до за­пи­си ре­зуль­ти­рую­ще­го зна­че­ния. Это да­же при­во­дит к то­му, что ком­пи­ля­тор вы­нуж­ден об­рат­но от­нять при­бав­лен­ную еди­ни­цу при за­пи­си в па­мять: mov BYTE PTR [eax+ecx-1], dl. Тем не ме­нее, этот код бо­лее эф­фек­ти­вен, чем ес­ли бы на­ра­щи­ва­ние счёт­чи­ка бы­ло по­сле за­пи­си в па­мять, так как эко­но­мит­ся один такт при про­ма­хе кэ­ша.

Оста­лось од­но лиш­нее чте­ние па­мя­ти для ши­ри­ны изоб­ра­же­ния. Из­ба­вим­ся от не­го, со­хра­няя раз­ме­ры в ло­каль­ных пе­ре­мен­ных:

void invert(cv::Mat &image) //image.type() == CV_8UC1
{
    int const imageWidth(image.cols), imageHeight(image.rows);
   
    for(int y(0); y < imageHeight; ++y)
    {
        unsigned char *const scanLine( image.ptr<unsigned char>(y) );

        for(int x(0); x < imageWidth; ++x)
            scanLine[x] = 255 - scanLine[x];
    }
}

Ли­стинг 8. Раз­ме­ры изоб­ра­же­ния со­хра­не­ны в ло­каль­ных пе­ре­мен­ных

$LL3@invert:
or  bl, 255
sub bl, BYTE PTR [ecx+eax]
inc ecx
mov BYTE PTR [ecx+eax-1], bl
cmp ecx, esi
jl  SHORT $LL3@invert

Ли­стинг 9. Ас­сем­блер­ный код внут­рен­не­го цик­ла из ли­стин­га 8.
По срав­не­нию с ли­стин­гом 7 ис­чез­ла опе­ра­ция чте­ния ши­ри­ны изоб­ра­же­ния из па­мя­ти

Мы ви­дим, что ком­пи­ля­тор со­хра­нил ши­ри­ну изоб­ра­же­ния в ре­ги­стре esi, и не чи­та­ет её каж­дый раз из па­мя­ти.

Мож­но ли вы­жать из это­го ко­да ещё что-ни­будь? Ко­неч­но! За­ме­тим, что ес­ли кру­тить цикл не по воз­рас­та­нию, а по убы­ва­нию, то цикл все­гда бу­дет ид­ти до ну­ля, и ком­пи­ля­то­ру во­об­ще не на­до бу­дет ис­поль­зо­вать cmp для про­вер­ки усло­вия оста­нов­ки, так как каж­дая ариф­ме­ти­че­ская опе­ра­ция со­хра­ня­ет во фла­го­вых ре­ги­страх про­цес­со­ра знак сво­е­го ре­зуль­та­та. Со­от­вет­ствен­но, в этом слу­чае не нуж­но со­хра­нять раз­ме­ры изоб­ра­же­ния в ло­каль­ных пе­ре­мен­ных.

Кро­ме то­го, од­на опе­ра­ция ис­поль­зу­ет­ся для за­груз­ки чис­ла 255 в ре­гистр bl. Ес­ли вме­сто вы­чи­та­ния ис­поль­зо­вать би­то­вое от­ри­ца­ние (стран­но, что ком­пи­ля­тор сам не до­га­дал­ся об этом), то мож­но сэко­но­мить ещё од­ну опе­ра­цию. В ито­ге по­лу­ча­ем:

void invert(cv::Mat &image) //image.type() == CV_8UC1
{
    for(int y(image.rows - 1); y >= 0; --y)
    {
        unsigned char *const scanLine( image.ptr<unsigned char>(y) );

        for(int x(image.cols - 1); x >= 0 ; --x)
            scanLine[x] = ~scanLine[x];
    }
}

Ли­стинг 10. На­прав­ле­ния хо­да цик­лов из­ме­не­ны.
Вме­сто вы­чи­та­ния при­ме­не­но би­то­вое от­ри­ца­ние

$LL3@invert:
dec ecx
mov dl, BYTE PTR [ecx+eax+1]
not dl
mov BYTE PTR [ecx+eax+1], dl
jns SHORT $LL3@invert

Ли­стинг 11. Ас­сем­блер­ный код внут­рен­не­го цик­ла из ли­стин­га 10

Об­ра­ти­те вни­ма­ние, что умень­ше­ние счёт­чи­ка цик­ла ком­пи­ля­тор по­ста­вил ещё рань­ше, и те­перь обе опе­ра­ции об­ра­ще­ния к па­мя­ти име­ют +1 для ком­пен­са­ции это­го преж­де­вре­мен­но­го вы­чи­та­ния.

Под­ве­дём ито­ги. Код из ли­стин­га 1 мы мо­дифи­ци­ро­ва­ли, по­лу­чив код из ли­стин­га 11. При этом в но­вом ко­де все­го на од­ну строч­ку боль­ше, и од­на из стро­чек су­ще­ствен­но ко­ро­че, так что нель­зя од­но­знач­но ска­зать, ка­кой из ва­ри­ан­тов бо­лее сло­жен. В ре­зуль­та­те вме­сто 12-ти ас­сем­блер­ных ин­ст­рук­ций, со­дер­жа­щих 6 об­ра­ще­ний к па­мя­ти, мы по­лу­чи­ли 5 ин­ст­рук­ций и 2 об­ра­ще­ния к па­мя­ти.

Я счи­таю, что по­след­ний ва­ри­ант — са­мый эф­фек­тив­ный из чи­тае­мых ва­ри­ан­тов реа­ли­за­ции. Мож­но, ко­неч­но, про­дви­нуть­ся даль­ше, за­гру­жать бай­ты по 4 шту­ки в 32-х-бит­ную пе­ре­мен­ную, мож­но по­пы­тать­ся ис­поль­зо­вать су­пер­ска­ляр­ные рас­ши­ре­ния, не­сколь­ко ядер про­цес­со­ра и про­чие пре­ле­сти со­вре­мен­ных ком­пью­те­ров. Но всё это от­дель­ная ра­бо­та, тре­бую­щая до­пол­ни­тель­но­го вре­ме­ни, а нам нуж­но де­лать боль­шой про­ект, в ко­то­ром на­хож­де­ние не­га­тив­но­го изоб­ра­же­ния (или что-то по­доб­ное) — лишь мел­кая под­за­да­ча, на ре­ше­ние ко­то­рой у нас име­ет­ся не бо­лее пя­ти ми­нут.

И, на­по­сле­док, не удер­жусь, при­ве­ду ва­ри­ант с ите­ра­то­ром:

void invert(cv::Mat &image) //image.type() == CV_8UC1
{
    for(cv::MatIterator_<unsigned char> i( image.begin<unsigned char>() );
    i != image.end<unsigned char>(); ++i)
        *i = ~*i;
}

Ли­стинг 12. Об­ход мат­ри­цы при по­мо­щи ите­ра­то­ра

Это­му изящ­но­му ко­ду со­от­вет­ству­ет бо­лее сот­ни ас­сем­блер­ных ин­ст­рук­ций, по­это­му не при­во­жу их здесь.

Срав­не­ние вре­ме­ни ра­бо­ты раз­лич­ных вер­сий ко­да

Мой про­цес­сор: Intel Core i5 M 460 2.53GHz, 2 яд­ра + Hyper-threading. Код за­пус­кал­ся на од­ном яд­ре для изоб­ра­же­ний 1024×1024 пик­се­ля (1 ме­га­байт) и 8192×8192 пик­се­ля (64 ме­га­бай­та).

Ва­ри­ант ко­даВре­мя ра­бо­ты (сек)
1024×1024 пик­се­ля
Вре­мя ра­бо­ты (сек)
8192×8192 пик­се­ля
1Mat::at (ли­стинг 1)0.002150.146
2Mat::ptr (ли­стинг 6)0.001310.089
3Раз­ме­ры в пе­ре­мен­ных (ли­стинг 8)0.001300.088
4Цик­лы по убы­ва­нию (ли­стинг 10)0.001300.087
5Ите­ра­тор (ли­стинг 12)0.02111.36

Таб­ли­ца 1. Вре­ме­на ра­бо­ты раз­лич­ных вер­сий ко­да. Мень­ше — луч­ше

При­ят­но осо­зна­вать, что тот ва­ри­ант (но­мер 3), ко­то­рый я ин­ту­и­тив­но вы­брал пол­то­ра го­да на­зад, ко­гда на­чал ис­поль­зо­вать OpenCV в сво­ей де­я­тель­но­сти, те­перь по­лу­чил обос­но­ва­ние. Я люб­лю скан­лай­ны со вре­мён Delphi. Там был ме­тод ScanLine у TBitmap.

Мы ви­дим, что об­ход мат­ри­цы при по­мо­щи Mat::ptr при­мер­но в 1.6 раз быст­рее, чем при по­мо­щи Mat::at. Даль­ней­шие оп­ти­ми­за­ции, не­смот­ря на оче­вид­ное со­кра­ще­ние объ­ё­ма ис­пол­ня­е­мо­го ко­да, к ро­сту про­из­во­ди­тель­но­сти не при­ве­ли. Ви­ди­мо, су­пер­ска­ляр­ность про­цес­со­ра по­ела всю не­эф­фек­тив­ность ко­да. Ва­ри­ант с ите­ра­то­ром ока­зал­ся в 16 раз мед­лен­нее са­мо­го быст­ро­го ва­ри­ан­та, по­это­му его ис­поль­зо­вать не сле­ду­ет.

За­клю­чи­тель­ные со­ве­ты

  • Посмот­ри­те, ка­кие внеш­ние дан­ные (дан­ные, на ко­то­рые ва­шей функ­ции да­ны ука­за­те­ли или ссыл­ки) вы ак­тив­но ис­поль­зу­е­те внут­ри функ­ции. Сде­лай­те ко­пии этих дан­ных в ло­каль­ных пе­ре­мен­ных, ес­ли вы уве­ре­ны, что эти дан­ные не долж­ны ме­нять­ся по хо­ду ра­бо­ты функ­ции.

  • Ес­ли у вас есть ука­за­те­ли, ко­то­рые ука­зы­ва­ют на дан­ные, не пе­ре­се­каю­щие­ся ни с чем дру­гим в ва­шей функ­ции, то по­мо­ги­те ком­пи­ля­то­ру — ис­поль­зуй­те клю­че­вое сло­во __restrict (не осве­ще­но в дан­ной ста­тье). Осо­бен­но это по­лез­но для ар­гу­мен­тов функ­ций. К со­жа­ле­нию, __restrict не ра­бо­та­ет для ссылок.

  • В ка­че­стве счёт­чи­ка и в усло­вии оста­нов­ки цик­ла ис­поль­зуй­те толь­ко ло­каль­ные пе­ре­мен­ные. Осо­бен­но хо­ро­шо, ко­гда в усло­вии оста­нов­ки цик­ла ис­поль­зу­ет­ся срав­не­ние счёт­чи­ка с ну­лём.

  • В прин­ци­пе, ком­пи­ля­тор мо­жет оп­ти­ми­зи­ро­вать Mat::at до пре­дель­но оп­ти­маль­но­го со­стоя­ния (смот­ри­те ли­стин­ги 4 и 5), но для это­го долж­ны вы­пол­нить­ся не­сколь­ко усло­вий: объ­ект cv::Mat (и, зна­чит, па­ра­мет­ры, вхо­дя­щие в функ­цию at) долж­ны быть ло­каль­ны от­но­си­тель­но функ­ции, и изоб­ра­же­ние не долж­но мо­дифи­ци­ро­вать­ся. Но луч­ше, всё же, ис­поль­зо­вать функ­цию Mat::ptr, так как она ра­бо­та­ет за пре­де­ла­ми внут­рен­не­го цик­ла и, зна­чит, не яв­ля­ет­ся ис­точ­ни­ком не­эф­фек­тив­но­сти.

  • Не ис­поль­зуй­те ите­ра­то­ры для об­хо­да cv::Mat.

102 отзыва на запись «OpenCV: цикл по всем пик­се­лям изоб­ра­же­ния и сов­ме­ще­ние ука­за­те­лей»

It is also possible that Zynga’s chosen advertising network is to blame if we consider the case of the New York Times’ website
generic lisinopril
Cafergot Online
BUY VPXL
Buspar Without A Prescription
Retin-A
antabuse
propranolol online
motilium otc
buy propranolol
cheap anafranil
whoah this blog is fantastic i really like studying your articles. Keep up the good paintings! You understand, many individuals are looking around for this info, you could aid them greatly.
Fluticasone Propionate
sildalis visa
cialis medicine
wellbutrin without prescription
buy nexium
buy fluoxetine
wh0cd856566
wh0cd236726
wh0cd9446
reverse phone number free lookup how to do reverse phone lookup for free phone lookup free results free phone reverse number lookup
best free reverse cell phone lookup with name reverse cell phone lookup
for free real free reverse phone lookup with name for free phone number lookup
reverse phone number lookup usa free reverse phone number lookup cell phone lookup free reverse free reverse phone lookup for free really
is there a reverse phone lookup that is really free cell phone usa reverse phone lookup free lookup free reverse cell phone lookup free
really free reverse phone lookup with name reverse phone lookup for free reverse cell phone number lookup with name reverse lookup cell phone
free
best free reverse phone lookup phone reverse lookup free results
reverse phone lookup by address free complete reverse cell phone lookup
phone reverse lookup free reverse directory lookup phone number name reverse lookup cell phone
numbers for free reverse phone lookup free account
best free cell phone number reverse lookup reverse phone
lookup reverse phone lookup with name for free free phone
reverse lookup cell
free cell phone number reverse lookup reverse number lookup free phone reverse lookup cell phone cell phone
reverse number lookup free
reverse phone lookup cell phone free online free phone reverse lookup cell completely free reverse phone number lookup
reverse lookup phone number
free phone reverse lookup with name reverse lookup phone number free phone lookup free reverse phone lookup for cell phones reverse
cell phone number reverse phone number lookup name free lookup free
free reverse phone number lookup free reverse lookup of phone numbers
Thanks for any other informative website.
Where else may just I am getting that type of info written in such a perfect manner?
I have a project that I’m simply now running on, and I have been on the look out for such info.
Every weekend i used to pay a visit this site, as i wish for enjoyment, as this this website conations really pleasant funny material too.
First off I want to say terrific blog! I had a quick question in which I’d like
to ask if you don’t mind. I was curious to
find out how you center yourself and clear your mind prior to writing. I have had a difficult time clearing my thoughts in getting my ideas out there. I truly do take pleasure in writing however it just seems like the first 10 to 15 minutes are wasted simply
just trying to figure out how to begin. Any ideas or
hints? Appreciate it!
Hi there! I just would like to give you a big thumbs up for the great info you’ve got here on this post. I’ll be returning to your web site for more soon.
I always emailed this web site post page to all my associates, as
if like to read it after that my contacts will too.
May I simply say what a comfort to uncover an individual
who really knows what they’re talking about on the internet.
You certainly realize how to bring an issue to light and make it important. A lot more people ought to look at this and understand this
side of the story. It’s surprising you aren’t more popular since you certainly have the
gift.
I have been exploring for a little for any high quality articles or weblog posts on this
kind of area . Exploring in Yahoo I eventually stumbled upon this
site. Reading this info So i am happy to express that I’ve an incredibly
good uncanny feeling I found out just what I needed.
I most definitely will make certain to do not overlook this site and give it
a look regularly.
Does your blog have a contact page? I’m having problems locating it but, I’d like to shoot you an email.
I’ve got some recommendations for your blog you might be interested in hearing.
Either way, great website and I look forward to seeing it develop over time.
The other day, while I was at work, my sister stole my apple ipad and tested to see if it can survive a 25 foot
drop, just so she can be a youtube sensation. My iPad is now broken and she has 83 views.
I know this is completely off topic but I had to share it with someone!
Hello there! This post could not be written any better!
Reading this post reminds me of my previous room mate!
He always kept talking about this. I will forward this
page to him. Pretty sure he will have a good read. Thanks for sharing!
Hey I know this is off topic but I was wondering if you knew of any widgets I could add
to my blog that automatically tweet my newest twitter updates.
I’ve been looking for a plug-in like this for quite some
time and was hoping maybe you would have some experience with something
like this. Please let me know if you run into anything. I truly enjoy reading
your blog and I look forward to your new updates.
Thanks on your marvelous posting! I truly enjoyed reading
it, you’re a great author. I will be sure to bookmark your blog
and will eventually come back from now on. I want to encourage continue
your great posts, have a nice weekend!
Its like you read my mind! You appear to know a lot
about this, like you wrote the book in it or something.
I think that you could do with a few pics to drive the message
home a bit, but instead of that, this is great blog.
An excellent read. I’ll certainly be back.
Hi my friend! I wish to say that this post is amazing, nice written and
come with approximately all important infos. I would
like to look extra posts like this .
I know this web page presents quality dependent
articles and other data, is there any other web site which gives these stuff in quality?
Hello! This is kind of off topic but I need some advice from an established blog.
Is it tough to set up your own blog? I’m not very techincal but I can figure things
out pretty quick. I’m thinking about making my own but I’m not sure where
to begin. Do you have any ideas or suggestions?
Cheers
It’s perfect time to make some plans for the future
and it is time to be happy. I have read this post and if
I could I want to suggest you some interesting
things or advice. Maybe you could write next articles referring to this article.
I want to read even more things about it!
It’s impressive that you are getting thoughts from this article
as well as from our argument made at this place.
I am now not certain the place you are getting your info, but great topic.
I needs to spend a while finding out more or understanding more.
Thank you for excellent information I used
to be looking for this information for my mission.
If some one needs to be updated with hottest technologies then he must be visit this website and be up to date every day.
It’s going to be finish of mine day, however before finish
I am reading this wonderful paragraph to improve my know-how.
Attractive part of content. I just stumbled upon your site and in accession capital
to assert that I acquire actually enjoyed account your blog posts.
Any way I will be subscribing for your augment or even I
fulfillment you access constantly quickly.
Your style is very unique in comparison to other people I’ve
read stuff from. Thank you for posting when you have the opportunity, Guess I’ll just bookmark this blog.
I am actually happy to glance at this webpage posts which carries lots of helpful information, thanks
for providing such data.
Sweet blog! I found it while searching on Yahoo News.
Do you have any suggestions on how to get listed in Yahoo News? I’ve been trying for a while but I never seem to get there!
Thanks Here is my page — silk lash extensions london
You actually make it seem so easy with your presentation however I find this topic to be really something which I
believe I’d never understand. It sort of feels too complex and very wide for me.
I am having a look forward on your subsequent put
up, I will try to get the grasp of it!
Thanks for the auspicious writeup. It actually
used to be a enjoyment account it. Look advanced to far added agreeable from you!
By the way, how could we be in contact? my blog post: hair treatment; echelon.perxadvantage.com,
Wow, awesome blog layout! How long have you been blogging for?
you make blogging look easy. The overall look of
your site is excellent, let alone the content! My web blog; mink lashes cruel
What’s Happening i am new to this, I stumbled upon this I have discovered
It positively useful and it has helped me out loads.
I am hoping to give a contribution & aid other customers like its helped me. Great job. Also visit my site madden nfl mobile free coins no survey
Hello there! This post couldn’t be written any better! Going
through this article reminds me of my previous roommate! He constantly kept preaching about this. I will send this information to him.
Fairly certain he’ll have a very good read. Thank you for sharing! Also visit my web site; csgo skins price, https://www.smiletug.com/people/stacey01w74,
If you wish for to take much from this piece of writing then you have to apply these techniques to your won webpage. Review my web page; mink individual lashes kit
WOW just what I was searching for. Came here by searching for fifa 17 coins generator Feel free to surf to my web site — fifa coins free ps3 (http://www.hildebrandt.de/)
My partner and I stumbled over here coming from a different web page and thought I might check things out. I like what I see so now i am following you.
Look forward to looking at your web page repeatedly. Feel free to surf to my weblog: csgo skins price check
An intriguing discussion is worth comment.
I believe that you need to publish more about this issue,
it might not be a taboo subject but usually people do not discuss these subjects.
To the next! Best wishes!!
WOW just what I was searching for. Came here by
searching for cs go skins for money my website :: cs go skins price checker (http://www.gamunu.info)
Oh my goodness! Incredible article dude! Many thanks, However I am encountering troubles with your RSS.
I don’t understand the reason why I am unable to join it. Is there anyone else
having the same RSS problems? Anyone that knows the solution will you kindly respond?
Thanx!! Here is my homepage: best cs go skin betting
That is really attention-grabbing, You’re an excessively
skilled blogger. I have joined your rss feed and
stay up for in search of extra of your wonderful post.
Additionally, I’ve shared your web site in my social networks Also visit my site :: fifa 17 coins xbox one
Amazing! Its actually awesome post, I have got much clear
idea concerning from this article. my web page … professional mink lash kit
I take pleasure in, result in I discovered exactly
what I used to be looking for. You’ve ended my four day lengthy hunt! God Bless you man. Have a nice day. Bye
Excellent pieces. Keep writing such kind of info on your blog. Im really impressed by your site.
Hey there, You have performed an excellent job. I’ll certainly digg
it and individually recommend to my friends. I’m confident they’ll be benefited from this site.
Wow, this paragraph is pleasant, my sister is analyzing these things,
so I am going to inform her.
I am regular reader, how are you everybody? This paragraph posted at this website is actually nice. Here is my website — fifa coins ps4 ebay
Do you mind if I quote a couple of your posts as long as I
provide credit and sources back to your site? My blog site is in the
exact same niche as yours and my users would truly benefit from
some of the information you present here. Please let me know if this alright with you.
Cheers! My blog … cs go knife skins for sale (http://www.wikipizza.com/)
hello!,I really like your writing so so much! proportion we
keep in touch extra about your article on AOL?
I require a specialist on this house to solve my problem.
Maybe that’s you! Having a look ahead to see you. my weblog — silk lash extensions wholesale
Thanks for sharing your thoughts on free dating sites no fees.
Regards
I feel that is one of the most significant information for me.
And i am satisfied studying your article. But want to
remark on some basic things, The site taste
is great, the articles is in reality nice : D. Just right job, cheers
Very good information. Lucky me I ran across your website by accident (stumbleupon).
I have book marked it for later!
Great post. I was checking constantly this blog and I am impressed!
Very useful info specifically the last part I care for such info much.
I was looking for this particular info for a long time. Thank you and good luck.
I all the time used to read piece of writing in news papers but now as I am
a user of net therefore from now I am using net for content, thanks to web.
Hi there, this weekend is good in favor of me, as this moment i am reading this fantastic educational post here at
my home. Also visit my web-site: madden mobile online coin generator no download no survey
When I initially commented I clicked the «Notify me when new comments are added» checkbox and
now each time a comment is added I get several e-mails with the same comment. Is there any way you can remove me from that service?
Cheers! Also visit my site :: cs go skins price checker (http://www.kansasco-op.coop)
Hey there! Someone in my Myspace group shared this website with us so I came to take a look.
I’m definitely enjoying the information. I’m book-marking and will be tweeting this
to my followers! Excellent blog and fantastic design. Have a look at my blog … mink lash extensions reviews
This design is spectacular! You obviously know how
to keep a reader amused. Between your wit and your
videos, I was almost moved to start my own blog (well, almost…HaHa!) Wonderful job.
I really enjoyed what you had to say, and more than that,
how you presented it. Too cool!
Tirage divinatoire amour gratuit les signes horoscope Feel free to surf to my weblog — Get Laid free Tonight
I’m don’t certain the spot you are having your
info, although great subject matter. I need to take your time learning more or maybe figuring
out more. Thank you for superb details I found myself looking for this specific information at my own purpose. My web page :: http://Games4fun.fr
Hello it really is me, I am additionally checking out
this specific website regularly, this particular website is actually pleasurable plus the users tends to be truly revealing pleasurable head. my homepage sex facebook
Tarot tirage gratuit en ligne mon avenir dans les cartes Here is my web blog :: http://Ebooksget.us/groups/dating-assistance-for-guys-just-how-to-behave-on-first-date/
Safeguard sufficient poultry toys within the top a portion of
the bird cage to provide several experience of privateness, allowing your own puppy parrot
in order to hide around them. It is not created for safe or maybe big-scale application.
Quick whenever you understand what you’re looking for in a very product or service and difficult once you don’t
has a process for buying the greatest Clickbank goods. Feel free to visit my web blog — Facebookofsex.Com
In many cases, why not participate in the video game once you tend to be sufficient able in order to win. For instance, and commuting on a variety
of amazing apps are created for a variety of functions.
The man jumps, candy crush saga swindle surges, and many others.
0 or higher so that they can play the video game consuming labeled drinks,
creating certain brand name snacks. Here is my page … facebook en sex
we appreciate, end up inn I discovered exahtly things I used to be using a peek when it comes to. You may have finished my own four time long hunt! Jesus Bless you people. Need a good day. Bye my page :: Storedtheapp.com
sensacje xx wieku obtain… Feel free to surf to my web-site … lathie.Senerew.com
Incredible issues here. I am very happy to
check the write-up. Thanks a lot a lot and i am taking a look forward at reach your. Here is my site; free local hookup
When I initially commented I clicked the «inform me personally when brand-new reviews include included» checkbox and today each time
the opinion are added I have 3 e-mail with the exact same remark.
Can there be any way you are able to remove myself coming from which solution? Thank you so much! Also visit my webpage — personals
protection agencies newer jersey… Here is my page: a hook up
I’m not sure why but this site is loading incredibly slow for me.
Is anyone else having this issue or is it a issue on my end?
I’ll check back later on and see if the problem still exists.
WOW just what I was looking for. Came here by searching
for mink lash extensions nyc Review my website — silk lash extensions adelaide
Have you ever considered about including a little bit more than just your articles?
I mean, what you say is important and everything. But imagine if you added some great photos or videos
to give your posts more, «pop»! Your content is excellent but with pics
and videos, this website could definitely be one of the
best in its niche. Awesome blog! Check out my web blog; madden mobile coin shop
Hi! Someone in my Myspace group shared this site
with us so I came to give it a look. I’m definitely enjoying the information. I’m bookmarking and will be tweeting this to my followers!
Outstanding blog and outstanding design. Here is my weblog :: counter strike go skin trade
Thanks for every other fantastic article. The place else could anybody get that kind of information in such an ideal manner of writing?
I’ve a presentation next week, and I’m at the search for such info. My web blog :: csgo skins free spin

Оставить отзыв

Жёлтые поля обязательны к заполнению

   

Можете использовать теги <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang=""> <div class=""> <span class=""> <br>