Какой-либо способ сделать это относительно простым (вложенный для копии памяти) код C++ более эффективный?

У вас будет небольшая проблема потери данных, если вы захотите использовать только два знака после запятой. Вам понадобится как минимум 5 десятичных знаков для значений от 1 до 2 000 000

Пример

Declare @YourTable table (Col3 float)
Insert into @YourTable values 
 (1),(1536),(1000000),(2000000)

Select A.*
      ,NewVal = convert(decimal(10,2), (Col3*100.0) /  ( Select max(Col3) from @YourTable)  )
 From  @YourTable A

Возвращает

Col3     NewVal
1        0.00     -- At decimal(10,5) you would see 0.00005
1536     0.08
1000000  50.00
2000000  100.00
5
задан Cœur 16 March 2019 в 17:36
поделиться

13 ответов

Я думаю доступы к массиву (они доступы действительного массива или оператор []?) собираются уничтожить Вас. Каждый представляет умножение.

В основном Вы хотите что-то вроде этого:

for (int y=0; y < height; y++) {
    unsigned char *destBgr = imgRgb.GetScanline(y); // inline methods are better
    unsigned char *destBW = imgBW.GetScanline(y);
    for (int x=0; x < width; x++) {
        *destBgr++ = *pImage++;
        *destBW++ = *destBgr++ = *pImage++; // do this in one shot - don't double deref
        *destBgr++ = *pImage++;
    }
}

Это сделает два, умножается на строку развертки. Вы кодируете, делал 4, умножается на ПИКСЕЛЬ.

4
ответ дан 18 December 2019 в 06:04
поделиться

Если pImage находится уже полностью в памяти, почему необходимо массажировать данные? Я имею в виду, находится ли это уже в формате псевдо-RGB, почему Вы не можете только записать некоторые встроенные стандартные программы/макросы, которые могут выложить значения по требованию вместо того, чтобы копировать его вокруг?

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

0
ответ дан 18 December 2019 в 06:04
поделиться

Вот одна очень крошечная, очень простая оптимизация:

Вы обращаетесь к imageRGB[y][x] неоднократно, и который, вероятно, должен быть повторно вычислен на каждом шаге.

Вместо этого вычислите его однажды и посмотрите, делает ли это некоторое улучшение:

Pixel* apixel;

for (int y=0; y < 640; y++) {
    for (int x=0; x < 480; x++) {
        apixel = &imgRGB[y][x];

        apixel->blue = *pImage;
        pImage++;

        apixel->green = *pImage;
        imgBW[y][x]   = *pImage;
        pImage++;

        apixel->red = *pImage;
        pImage++;
    }
}
0
ответ дан 18 December 2019 в 06:04
поделиться

Если возможно, зафиксируйте, это в более высоком уровне затем укусило или вертящая инструкция!

  • Вы могли специализировать класс изображения B&W к тому, который ссылается на зеленый канал класса цветного изображения (таким образом сохраняющий копию на пиксель). Если Вы всегда создаете их в паре, Вам даже, возможно, не понадобилось бы наивное imgBW класс вообще.

  • Заботясь, о как Ваше хранилище данные в imgRGB, Вы могли скопировать триплет за один раз с входных данных. Лучше, Вы могли бы скопировать все это или даже просто сохранить ссылку (который делает предыдущее предложение легким также).

Если Вы не управляете реализацией всего здесь, Вы могли бы застрять, то:

  • Последнее средство: разверните цикл (выдайте кого-то упоминающего устройство Вареного пудинга или просто попросите, чтобы компилятор сделал это для Вас...), хотя я не думаю, что Вы будете видеть много улучшения...
0
ответ дан 18 December 2019 в 06:04
поделиться

Кажется, что Вы определили каждый пиксель как некоторую структуру или объект. Используя тип примитива (говорят, интервал) могло быть быстрее. Как другие упомянули, компилятор, вероятно, оптимизирует доступ к массиву с помощью инкрементов указателя. Если компиляция не делает этого для Вас, можно сделать это сами для предотвращения умножения при использовании массива [] [].

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

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

0
ответ дан 18 December 2019 в 06:04
поделиться

Удостоверьтесь pImage, imgRGB, и imgBW отмечены __, ограничивают. Используйте SSE и сделайте это шестнадцать байтов за один раз.

На самом деле от того, что Вы делаете там, похоже, что Вы могли использовать простой memcpy () для копирования pImage в imgRGB (так как imgRGB находится в главном строкой формате и по-видимому в том же порядке как pImage). Вы могли заполнить imgBW при помощи серии SSE swizzle и сохранить операцию в секунду для уплотнений зеленых значений, но это могло бы быть громоздким, так как необходимо будет продолжить работать (3*16 =) 48 байтов за один раз.

Вы - верный pImage, и Ваши выходные массивы - все в dcache при запуске этого? Попытайтесь использовать подсказку упреждающей выборки для выборки 128 байтов вперед и мера, чтобы видеть, улучшает ли это вещи.

Редактирование, Если Вы не находитесь на x86, заменяет "SSE" соответствующей системой команд SIMD для Ваших аппаратных средств, конечно. (Это было бы VMX, Altivec, SPU, VLIW, HLSL, и т.д.),

1
ответ дан 18 December 2019 в 06:04
поделиться

Я принимаю следующее в данный момент, поэтому сообщите мне, являются ли мои предположения неправильными:

a) imgRGB является структурой типа


    struct ImgRGB
    {
      unsigned char blue;
      unsigned char green;
      unsigned char red;
    };

или по крайней мере что-то подобное.

b) imgBW выглядит примерно так:


    struct ImgBW
    {
       unsigned char BW;
    };

c) Код является единственным, распараллелил

Принимая вышеупомянутое, я вижу несколько проблем с Вашим кодом:

  • Вы помещаете присвоение на часть BW прямо в середине присвоений на другие контейнеры. Если Вы работаете над современным ЦП, возможности состоят в том, что с размером Ваших данных Ваш кэш L1 делается недействительным каждый раз, когда Вы переключаете контейнеры, и Вы смотрите на перезагрузку или переключение строки кэша. Кэши оптимизированы для линейного доступа в эти дни, настолько скачкообразно двигающегося, туда и сюда не помогает. Доступ к оперативной памяти намного медленнее, так, чтобы был бы значимый хит производительности. Чтобы проверить, является ли это проблемой, временно я удалил бы присвоение на imgBW и меру, если существует значимое ускорение.
  • Доступ к массиву не помогает, и он потенциально замедлит код немного, хотя достойный оптимизатор должен заботиться об этом. Я, вероятно, записал бы цикл вдоль этих строк вместо этого, но не буду ожидать большое увеличение производительности. Возможно, пара процента.

    for (int y=0; y blue = *pImage;
            ...
        }
    }
  • Для непротиворечивости я изменился бы от использования постфикса для добавления префикса инкремента, но я не буду ожидать видеть большое усиление.
  • Если можно потратить впустую немного устройства хранения данных (хорошо, 25%), Вы могли бы получить от добавления четвертого фиктивного неподписанного символа к структуре ImgRGB при условии, что это увеличится, размер структуры к размеру международного Собственного ints являются обычно самыми быстрыми к доступу и если Вы смотрите на структуру символов, которые не заполняют интервал полностью, Вы потенциально сталкиваетесь со всеми видами интересных проблем доступа, которые могут замедлить Ваш код заметно, потому что компилятору, возможно, придется генерировать дополнительные инструкции извлечь неподписанные символы. Снова, попробуйте это и измерьте результат - это могло бы иметь заметное значение или ни один вообще. В том же духе повышение размера элементов структуры от неподписанного символа до неподписанного интервала могло бы потратить впустую много пространства, но потенциально может ускорить код. Тем не менее, целый pImage является указателем на неподписанный символ, Вы только устранили бы половину проблемы.

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

1
ответ дан 18 December 2019 в 06:04
поделиться

Вы могли бы попытаться использовать простой бросок, чтобы получить Ваши данные RGB и просто повторно вычислить полутоновые данные:

#pragma pack(1)
typedef unsigned char bw_t;
typedef struct {
    unsigned char blue;
    unsigned char green;
    unsigned char red;
} rgb_t;
#pragma pack(pop)

rgb_t *imageRGB = (rgb_t*)pImage;
bw_t *imageBW = (bw_t*)calloc(640*480, sizeof(bw_t));
// RGB(X,Y) = imageRGB[Y*480 + X]
// BW(X,Y) = imageBW[Y*480 + X]

for (int y = 0; y < 640; ++y)
{
   // try and pull some larger number of bytes from pImage (24 is arbitrary)
   // 24 / sizeof(rgb_t) = 8
   for (int x = 0; x < 480; x += 24)
   {
       imageBW[y*480 + x    ] = GRAYSCALE(imageRGB[y*480 + x    ]);
       imageBW[y*480 + x + 1] = GRAYSCALE(imageRGB[y*480 + x + 1]);
       imageBW[y*480 + x + 2] = GRAYSCALE(imageRGB[y*480 + x + 2]);
       imageBW[y*480 + x + 3] = GRAYSCALE(imageRGB[y*480 + x + 3]);
       imageBW[y*480 + x + 4] = GRAYSCALE(imageRGB[y*480 + x + 4]);
       imageBW[y*480 + x + 5] = GRAYSCALE(imageRGB[y*480 + x + 5]);
       imageBW[y*480 + x + 6] = GRAYSCALE(imageRGB[y*480 + x + 6]);
       imageBW[y*480 + x + 7] = GRAYSCALE(imageRGB[y*480 + x + 7]);
   }
}
2
ответ дан 18 December 2019 в 06:04
поделиться

Вы могли оптимизировать далеко часть адресной арифметики с указателями, которую Вы делаете много раз с нижними операторами [] [] и используете итератор вместо этого (то есть, совершенствуете указатель).

3
ответ дан 18 December 2019 в 06:04
поделиться

Пропускная способность памяти является Вашим узким местом здесь. Существует теоретическое минимальное время, требуемое передавать все данные и от системной памяти. Я записал немного теста для сравнения версии OP с некоторым простым ассемблером, чтобы видеть, насколько хороший компилятор был. Я использую VS2005 с настройками режима выпуска по умолчанию. Вот код:

#include <windows.h>
#include <iostream>
using namespace std;

const int
c_width = 640,
c_height = 480;

typedef struct _RGBData
{
  unsigned char
    r,
    g,
    b;
    // I'm assuming there's no padding byte here
} RGBData;

//  similar to the code given
void SimpleTest
(
  unsigned char *src,
  RGBData *rgb,
  unsigned char *bw
)
{
  for (int y = 0 ; y < c_height ; ++y)
  {
    for (int x = 0 ; x < c_width ; ++x)
    {
      rgb [x + y * c_width].b = *src;
      src++;

      rgb [x + y * c_width].g = *src;
      bw [x + y * c_width] = *src;
      src++;

      rgb [x + y * c_width].r = *src;
      src++;
    }
  }
}

//  the assembler version
void ASM
(
  unsigned char *src,
  RGBData *rgb,
  unsigned char *bw
)
{
  const int
    count = 3 * c_width * c_height / 12;

  _asm
  {
    push ebp
    mov esi,src
    mov edi,bw
    mov ecx,count
    mov ebp,rgb
l1:
    mov eax,[esi]
    mov ebx,[esi+4]
    mov edx,[esi+8]
    mov [ebp],eax
    shl eax,16
    mov [ebp+4],ebx
    rol ebx,16
    mov [ebp+8],edx
    shr edx,24
    and eax,0xff000000
    and ebx,0x00ffff00
    and edx,0x000000ff
    or eax,ebx
    or eax,edx
    add esi,12
    bswap eax
    add ebp,12
    stosd
    loop l1
    pop ebp
  }
}

//  timing framework
LONGLONG TimeFunction
(
  void (*function) (unsigned char *src, RGBData *rgb, unsigned char *bw),
  char *description,
  unsigned char *src, 
  RGBData *rgb,
  unsigned char *bw
)
{
  LARGE_INTEGER
    start,
    end;

  cout << "Testing '" << description << "'...";
  memset (rgb, 0, sizeof *rgb * c_width * c_height);
  memset (bw, 0, c_width * c_height);

  QueryPerformanceCounter (&start);

  function (src, rgb, bw);

  QueryPerformanceCounter (&end);

  bool
    ok = true;

  unsigned char
    *bw_check = bw,
    i = 0;

  RGBData
    *rgb_check = rgb;

  for (int count = 0 ; count < c_width * c_height ; ++count)
  {
    if (bw_check [count] != i || rgb_check [count].r != i || rgb_check [count].g != i || rgb_check [count].b != i)
    {
      ok = false;
      break;
    }

    ++i;
  }

  cout << (end.QuadPart - start.QuadPart) << (ok ? " OK" : " Failed") << endl;
  return end.QuadPart - start.QuadPart;
}

int main
(
  int argc,
  char *argv []
)
{
  unsigned char
    *source_data = new unsigned char [c_width * c_height * 3];

  RGBData
    *rgb = new RGBData [c_width * c_height];

  unsigned char
    *bw = new unsigned char [c_width * c_height];

  int
    v = 0;

  for (unsigned char *dest = source_data ; dest < &source_data [c_width * c_height * 3] ; ++dest)
  {
    *dest = v++ / 3;
  }

  LONGLONG
    totals [2] = {0, 0};

  for (int i = 0 ; i < 10 ; ++i)
  {
    cout << "Iteration: " << i << endl;
    totals [0] += TimeFunction (SimpleTest, "Initial Copy", source_data, rgb, bw);
    totals [1] += TimeFunction (       ASM, "    ASM Copy", source_data, rgb, bw);
  }

  LARGE_INTEGER
    freq;

  QueryPerformanceFrequency (&freq);

  freq.QuadPart /= 100000;

  cout << totals [0] / freq.QuadPart << "ns" << endl;
  cout << totals [1] / freq.QuadPart << "ns" << endl;


  delete [] bw;
  delete [] rgb;
  delete [] source_data;

  return 0;
}

И отношение между C и ассемблером, который я получал, было о 2.5:1, т.е. C был 2.5 раза временем ассемблерной версии.

Я только что заметил, что исходные данные были в порядке BGR. Если копия подкачала B и компоненты R затем, это действительно делает ассемблерный код немного более сложным. Но это также сделало бы код C более сложным также.

Идеально, необходимо разработать то, что теоретическое минимальное время, и сравните его с тем, что Вы на самом деле получаете. Чтобы сделать это, необходимо знать частоту памяти и тип памяти и работы MMU ЦП.

3
ответ дан 18 December 2019 в 06:04
поделиться

То, что мне нравится делать в таких ситуациях, входят в отладчик и ступают посредством дизассемблирования для наблюдения то, что это действительно делает (или имейте компилятор, генерируют протокол ассемблирования). Это может дать Вам много ключа к разгадке о том, где inefficencies. Они часто не, где Вы думаете!

Путем реализации изменений, предложенных Assaf и David Lee выше, можно добраться прежде и после количества инструкции. Это действительно помогает мне в оптимизации трудных внутренних циклов.

4
ответ дан 18 December 2019 в 06:04
поделиться

Несколько шагов можно взять. Результат в конце этого ответа.

Во-первых, используйте указатели.

const unsigned char *pImage;

RGB *rgbOut = imgRGB;
unsigned char *bwOut = imgBW;

for (int y=0; y < 640; ++y) {
    for (int x=0; x < 480; ++x) {
        rgbOut->blue = *pImage;
        ++pImage;

        unsigned char tmp = *pImage;  // Save to reduce amount of reads.
        rgbOut->green = tmp;
        *bwOut = tmp;
        ++pImage;

        rgbOut->red = *pImage;
        ++pImage;

        ++rgbOut;
        ++bwOut;
    }
}

Если imgRGB и imgBW объявляются как:

unsigned char imgBW[480][640];
RGB imgRGB[480][640];

Можно объединить эти два цикла:

const unsigned char *pImage;

RGB *rgbOut = imgRGB;
unsigned char *bwOut = imgBW;

for (int i=0; i < 640 * 480; ++i) {
    rgbOut->blue = *pImage;
    ++pImage;

    unsigned char tmp = *pImage;  // Save to reduce amount of reads.
    rgbOut->green = tmp;
    *bwOut = tmp;
    ++pImage;

    rgbOut->red = *pImage;
    ++pImage;

    ++rgbOut;
    ++bwOut;
}

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

const unsigned char *pImage;

RGB *rgbOut = imgRGB;
unsigned char *bwOut = imgBW;

const uint32_t *curPixelGroup = pImage;

for (int i=0; i < 640 * 480; ++i) {
    uint64_t pixels = 0;

#define WRITE_PIXEL         \
    rgbOut->blue = pixels;  \
    pixels >>= 8;           \
                            \
    rgbOut->green = pixels; \
    *bwOut = pixels;        \
    pixels >>= 8;           \
                            \
    rgbOut->red = pixels;   \
    pixels >>= 8;           \
                            \
    ++rgbOut;               \
    ++bwOut;

#define READ_PIXEL(shift) \
    pixels |= (*curPixelGroup++) << (shift * 8);

    READ_PIXEL(0);  WRITE_PIXEL;
    READ_PIXEL(1);  WRITE_PIXEL;
    READ_PIXEL(2);  WRITE_PIXEL;
    READ_PIXEL(3);  WRITE_PIXEL;
    /* Remaining */ WRITE_PIXEL;

#undef COPY_PIXELS
}

(Ваш компилятор, вероятно, оптимизирует далеко избыточное or операция в первом READ_PIXEL. Это также оптимизирует сдвиги, удаляя избыточное << 0, также.)


Если структура RGB таким образом:

struct RGB {
     unsigned char blue, green, red;
};

Можно оптимизировать еще больше, скопировать в структуру непосредственно, вместо через ее участников (red, green, blue). Это может быть сделано с помощью анонимных структур (или кастинг, но это делает код немного более грязным и вероятно более подверженным ошибке). (Снова, это - иждивенец в системах с прямым порядком байтов, и т.д. и т.д.):

union RGB {
    struct {
        unsigned char blue, green, red;
    };

    uint32_t rgb:24;  // Make sure it's a bitfield, otherwise the union will strech and ruin the ++ operator.
};

const unsigned char *pImage;

RGB *rgbOut = imgRGB;
unsigned char *bwOut = imgBW;

const uint32_t *curPixelGroup = pImage;

for (int i=0; i < 640 * 480; ++i) {
    uint64_t pixels = 0;

#define WRITE_PIXEL         \
    rgbOut->rgb = pixels;   \
    pixels >>= 8;           \
                            \
    *bwOut = pixels;        \
    pixels >>= 16;          \
                            \
    ++rgbOut;               \
    ++bwOut;

#define READ_PIXEL(shift) \
    pixels |= (*curPixelGroup++) << (shift * 8);

    READ_PIXEL(0);  WRITE_PIXEL;
    READ_PIXEL(1);  WRITE_PIXEL;
    READ_PIXEL(2);  WRITE_PIXEL;
    READ_PIXEL(3);  WRITE_PIXEL;
    /* Remaining */ WRITE_PIXEL;

#undef COPY_PIXELS
}

Можно оптимизировать запись пикселя так же, как мы сделали с чтением (пишущий в словах, а не 24 бита). На самом деле это было бы довольно хорошей идеей и будет большим следующим шагом в оптимизации. Слишком усталый для кодирования его, все же. =]


Конечно, можно записать стандартную программу в ассемблере. Это делает это менее портативным, чем это уже, как бы то ни было.

2
ответ дан 18 December 2019 в 06:04
поделиться

Очевидный вопрос, необходимо ли скопировать данные во-первых? Вы не можете только определить функции средства доступа для извлечения R, G и значений B для какого-либо данного пикселя от исходного входного массива?

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

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

Я думаю, что лучший подход должен будет развернуть цикл достаточно раз, чтобы гарантировать, что каждое повторение обрабатывает блок данных, делимых на 4 байта (так в каждом повторении, цикл может просто считать небольшое количество ints, а не большое количество символов), Конечно, это требует, чтобы Вы кашировали биты этих ints при записи, но это - быстрая операция, и самое главное, это сделано в регистрах, не обременяя подсистему памяти или кэш ЦП:

// First, we need to treat the input image as an array of ints. This is a bit nasty and technically unportable, but you get the idea)
unsigned int* img = reinterpret_cast<unsigned int*>(pImage);

for (int y = 0; y < 640; ++y)
{
  for (int x = 0; x < 480; x += 4)
  {
    // At the start of each iteration, read 3 ints. That's 12 bytes, enough to write exactly 4 pixels.
    unsigned int i0 = *img;
    unsigned int i1 = *(img+1);
    unsigned int i2 = *(img+2);
    img += 3;

    // This probably won't make a difference, but keeping a reference to the found pixel saves some typing, and it may assist the compiler in avoiding aliasing.
    ImgRGB& pix0 = imgRGB[y][x];
    pix0.blue = i0 & 0xff;
    pix0.green = (i0 >> 8) & 0xff;
    pix0.red = (i0 >> 16) & 0xff;
    imgBW[y][x] = (i0 >> 8) & 0xff;

    ImgRGB& pix1 = imgRGB[y][x+1];
    pix1.blue = (i0 >> 24) & 0xff;
    pix1.green = i1 & 0xff;
    pix1.red = (i0 >> 8) & 0xff;
    imgBW[y][x+1] = i1 & 0xff;

    ImgRGB& pix2 = imgRGB[y][x+2];
    pix2.blue = (i1 >> 16) & 0xff;
    pix2.green = (i1 >> 24) & 0xff;
    pix2.red = i2 & 0xff;
    imgBW[y][x+2] = (i1 >> 24) & 0xff;

    ImgRGB& pix3 = imgRGB[y][x+3];
    pix3.blue = (i2 >> 8) & 0xff;
    pix3.green = (i2 >> 16) & 0xff;
    pix3.red = (i2 >> 24) & 0xff;
    imgBW[y][x+3] = (i2 >> 16) & 0xff;
  }
}

также вероятно, что Вы - более обеспеченное заполнение временного значения ImgRGB, и затем пишущий, что вся структура к памяти сразу, означая, что первый блок был бы похож на это вместо этого: (следующие блоки были бы подобны, конечно),

ImgRGB& pix0 = imgRGB[y][x];
ImgRGB tmpPix0;
tmpPix0.blue = i0 & 0xff;
tmpPix0.green = (i0 >> 8) & 0xff;
tmpPix0.red = (i0 >> 16) & 0xff;
imgBW[y][x] = (i0 >> 8) & 0xff;
pix0 = tmpPix0;

В зависимости от того, насколько умный компилятор, это может сократить существенно на необходимом количестве чтений. Принятие исходного кода наивно компилируется (который, вероятно, маловероятен, но будет служить примером), это получит Вас от 3 чтений, и 4 записи на пиксель (считайте канал RGB и запишите RGB + BW) к чтениям 3/4 на пиксель и 2 записи. (одна запись для структуры RGB, и один для значения BW)

Вы могли также накопить 4 записи до изображения BW в единственном интервале и затем записать что сразу также, что-то вроде этого:

bw |= (i0 >> 8) & 0xff;
bw |=  (i1 & 0xff) << 8;
bw |=  ((i1 >> 24) & 0xff) << 16;
bw |=  ((i2 >> 16) & 0xff) << 24;

*(imgBW + y*480+x/4) = bw; // Assuming you can treat imgBW as an array of integers

Это сократило бы количество записей к 1,25 на пиксель (1 на структуру RGB, и 1 для каждых 4 значений BW)

Снова, преимущество, вероятно, будет намного меньшим (или даже не существовать), но это может стоить того, чтобы попытаться.

Беря это шаг вперед, то же могло быть сделано без слишком большой проблемы с помощью инструкций SSE, позволив Вам обработать в 4 раза больше значений на повторение. (Принятие Вы работаете на x86),

Конечно, важная правовая оговорка здесь - то, что вышеупомянутое является непортативным. reinterpret_cast является, вероятно, академической точкой (он будет, скорее всего, работать независимо от того, что, особенно если можно удостовериться, что исходный массив выровненный на 32-разрядной границе, которая будет обычно иметь место для больших выделений на всех платформах) большая проблема - то, что битовое жонглирование зависит от порядка байтов ЦП.

Но на практике, это должно работать над x86. и с небольшими изменениями, это должно работать над машинами с обратным порядком байтов также. (по модулю любые ошибки в моем коде, конечно. Я не протестировал или даже скомпилировал любой из него ;))

Но неважно как Вы решаете его, Вы собираетесь видеть самые большие улучшения скорости от уменьшения количества чтений и записей и попытки накопить как можно больше данных в регистрах ЦП. Считайте все, что Вы можете в больших блоках, как ints, переупорядочивать его в регистрах (накопите его во многие ints или запишите его во временные экземпляры структуры RGB), и затем выпишите им общую стоимость к памяти.

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

Наконец, добавление 4-го фиктивного значения к структуре RGB (таким образом, это имеет общий размер 32 битов), скорее всего, поможет много также (потому что затем запись такой структуры является единственной 32-разрядной записью, которая более проста и более эффективна, чем 24-разрядный ток),

При решении, сколько развернуть цикл (Вы могли сделать вышеупомянутое дважды или больше в каждом повторении), имейте в виду, сколько регистров Ваш ЦП имеет. Выливание в кэш, вероятно, причинит Вам боль, поскольку уже существует много доступов памяти, но с другой стороны, разворачивает столько, сколько можно предоставить, учитывая количество доступных регистров (вышеупомянутое использование 3 регистра для хранения входных данных, и один для накопления значений BW. Этому, возможно, понадобится один или еще два для вычислений необходимых адресов, таким образом, на x86, удваивая вышеупомянутое мог бы продвигать его немного (у Вас есть 8 общих количеств регистров, и у некоторых из них есть особые значения). С другой стороны, современный ЦП делают много для компенсации давления регистра, при помощи намного большего числа регистров негласно, поэтому далее разворачивание могло бы все еще быть общей победой производительности.

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

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

7
ответ дан 18 December 2019 в 06:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: