LINUX.ORG.RU

Как можно затюнинговать этот участок кода?

 , , ,


4

13

Я не програмист и С знаю достаточно плохо, но вот тут хочеться разобраться и попытаться оптимизировать этот участок. Это код из gstreamer, который участвует в перегоне RGB в UYV http://cgit.freedesktop.org/gstreamer/gst-plugins-base/tree/gst/videoconvert/...

#define SCALE    (8)
#define SCALE_F  ((float) (1 << SCALE))

static void
videoconvert_convert_matrix8 (VideoConvert * convert, gpointer pixels)
{
  int i;
  int r, g, b;
  int y, u, v;
  guint8 *p = pixels;

  for (i = 0; i < convert->width; i++) {
    r = p[i * 4 + 1];
    g = p[i * 4 + 2];
    b = p[i * 4 + 3];

    y = (convert->cmatrix[0][0] * r + convert->cmatrix[0][1] * g +
        convert->cmatrix[0][2] * b + convert->cmatrix[0][3]) >> SCALE;
    u = (convert->cmatrix[1][0] * r + convert->cmatrix[1][1] * g +
        convert->cmatrix[1][2] * b + convert->cmatrix[1][3]) >> SCALE;
    v = (convert->cmatrix[2][0] * r + convert->cmatrix[2][1] * g +
        convert->cmatrix[2][2] * b + convert->cmatrix[2][3]) >> SCALE;

    p[i * 4 + 1] = CLAMP (y, 0, 255);
    p[i * 4 + 2] = CLAMP (u, 0, 255);
    p[i * 4 + 3] = CLAMP (v, 0, 255);
  }
}

При записи скринкаста в фуллхд videoconvert_convert_matrix8 жрет нереально много времени. Может чей-то опытый глаз поможет, хоть напрвит в сторону чего тут можно оптимизировать. например i * 4 повторяеться 6 раз, хотя понимаю что оно то почти и не дает нагрузку.

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 3)

например i * 4 повторяеться 6 раз

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

thesame ★★★★
()

Как можно затюнинговать этот участок кода?

  • Заюзать libswscale для этого. В ней много разных заточенных под mmx и sse функций для преобразования. Можно расчитывать на ускорение в несколько раз;
  • Переложить преобразование на GPU. В зависимости от GPU и его расположения (интегрированный/дискретный) может и ускорить и затормозить по сравнению с libswscale.
i-rinat ★★★★★
()
17 июня 2014 г.

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

#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X)
#define RGB2Y(R, G, B) CLIP(( (  66 * (R) + 129 * (G) +  25 * (B) + 128) >> 8) +  16)
#define RGB2U(R, G, B) CLIP(( ( -38 * (R) -  74 * (G) + 112 * (B) + 128) >> 8) + 128)
#define RGB2V(R, G, B) CLIP(( ( 112 * (R) -  94 * (G) -  18 * (B) + 128) >> 8) + 128)

static void
videoconvert_convert_matrix8 (VideoConvert * convert, gpointer pixels)
{
  int i,t;
  int y, u, v;
  int r, g, b;
  guint8 *p = pixels;

  for (i = 0; i < convert->width; i++) {
    t=i*4;
    y=t+1; 
    u=y+1;
    v=u+1;
    r=p[t+1];
    g=p[t+2];
    b=p[t+3];
    p[y] = RGB2Y(r,g,b);
    p[u] = RGB2U(r,g,b);
    p[v] = RGB2V(r,g,b);
  }
}

и результат

до
 
perf stat -p `pidof gst-launch-1.0`  -d -a 
^C
 Performance counter stats for process id '18980':

      30384.173422 task-clock                #    0.481 CPUs utilized          
            27,332 context-switches          #    0.900 K/sec                  
             5,241 cpu-migrations            #    0.172 K/sec                  
             1,642 page-faults               #    0.054 K/sec                  
    90,790,820,778 cycles                    #    2.988 GHz                    
    25,993,859,696 stalled-cycles-frontend   #   28.63% frontend cycles idle   
   <not supported> stalled-cycles-backend  
   245,329,236,634 instructions              #    2.70  insns per cycle        
                                             #    0.11  stalled cycles per insn
    17,607,065,972 branches                  #  579.481 M/sec                  
        31,268,606 branch-misses             #    0.18% of all branches        
   <not supported> L1-dcache-loads:HG      
       704,453,875 L1-dcache-load-misses:HG  #    0.00% of all L1-dcache hits  
       333,110,129 LLC-loads:HG              #   10.963 M/sec                  
   <not supported> LLC-load-misses:HG      

      63.130500236 seconds time elapsed


после

perf stat -p `pidof gst-launch-1.0`  -d -a 
^C
 Performance counter stats for process id '18804':

      21621.851703 task-clock                #    0.364 CPUs utilized          
            22,496 context-switches          #    0.001 M/sec                  
             2,712 cpu-migrations            #    0.125 K/sec                  
             1,547 page-faults               #    0.072 K/sec                  
    67,110,958,855 cycles                    #    3.104 GHz                    
    12,420,679,145 stalled-cycles-frontend   #   18.51% frontend cycles idle   
   <not supported> stalled-cycles-backend  
   202,995,433,561 instructions              #    3.02  insns per cycle        
                                             #    0.06  stalled cycles per insn
     7,312,441,083 branches                  #  338.197 M/sec                  
        26,623,263 branch-misses             #    0.36% of all branches        
   <not supported> L1-dcache-loads:HG      
       658,903,226 L1-dcache-load-misses:HG  #    0.00% of all L1-dcache hits  
       309,353,254 LLC-loads:HG              #   14.307 M/sec                  
   <not supported> LLC-load-misses:HG      

      59.371335826 seconds time elapsed

Думаю это не плохо с 0.48 снизить до 0.36

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от wakuwaku

Ну я тож так думаю, и я пытался, но мои знания не делеки от хелоуворда, есть даже уже готовый пример из ssr
https://github.com/MaartenBaert/ssr/blob/master/src/AV/FastScaler_Convert_SSS...

и софтовый
https://github.com/MaartenBaert/ssr/blob/master/src/AV/FastScaler_Convert_Fal...
который как раз есть копия моего насколько я вижу.

Но пока мне не хватит знаний всунуть его в gstreamer. А разработчики gstreamer считают что у них и так все оптимизированно замечательно. Но тот же ssr на конвертацию тратит 2-5% а на кодирование h264 40%, то gstreamer наоборот умеет аппратное кодирование но конвертация жрет 40%.

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от vertexua

насколько я могу судить то да, если верить перфу и топу

Novell-ch ★★★★★
() автор топика

Если не можешь написать на SIMD сам, заюзай готовое, поддерживаю libswscale.

mix_mix ★★★★★
()
Ответ на: комментарий от tailgunner

Ок.

Cast Carb_blog2. Цырь, тут говорят, что любая макака лучше тебя соптимизирует С.

Pavval ★★★★★
()
Ответ на: комментарий от Novell-ch
#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X)
#define RGB2Y(R, G, B) CLIP(( (  t66[R] + t129[G] +  t25[B] + 128) >> 8) +  16)
#define RGB2U(R, G, B) CLIP(( ( -t38[R] - t74[G]  +  t112[B] + 128) >> 8) + 128)
#define RGB2V(R, G, B) CLIP(( (  t112[R] -t94[G]  -  t18[B] + 128) >> 8) + 128)

static int t66[256], t129[256], t25[256];
static int t38[256], t74[256], t112[256];
static int t94[256], t18[256];

static void
videoconvert_convert_matrix8 (VideoConvert * convert, gpointer pixels)
{
  int t;
  guint8 r, g, b;
  guint8 *p = pixels;

  for (t = 0; t < count * 4; t += 4) {
    r=p[t+1];
    g=p[t+2];
    b=p[t+3];
    p[t+1] = RGB2Y(r,g,b);
    p[t+2] = RGB2U(r,g,b);
    p[t+3] = RGB2V(r,g,b);
  }

  return 0;
}

...

  for (i = 0; i < 255; i++)
  {
    t66[i] = i * 66;
    t129[i] = i * 129;
    t25[i] = i * 25;
    t38[i] = i * 38;
    t74[i] = i * 74;
    t112[i] = i * 112;
    t94[i] = i * 94;
    t18[i] = i * 18;
  }
  • убраны лишние переменные i, y, u, v;
  • умножение заменено табличным преобразованием;

Выигрыш: ~20%

anonymous
()
Ответ на: комментарий от anonymous

Не вижу что такое count, и плюс не знаю куда закинуть
for (i = 0; i < 255; i++), непосредственно перед вызовом функции?

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Novell-ch

Для тестов я немного менял код: мой count - это твой convert->width.

Инициализацию таблиц нужно закинуть в какую-нибудь функцию инициализации модуля преобразования или на худой конец в первый вызов этой функции.

anonymous
()

Подписался на годный тред.

devl547 ★★★★★
()

например i * 4 повторяеться 6 раз

Хорошо на FMA ложится.

devl547 ★★★★★
()
Ответ на: комментарий от anonymous

я так и сделал и получит рузультат обратный,

18069.998618 task-clock                #    0.457 CPUs utilized          
             8,639 context-switches          #    0.478 K/sec                  
               534 cpu-migrations            #    0.030 K/sec                  
            12,201 page-faults               #    0.675 K/sec                  
    54,849,170,949 cycles                    #    3.035 GHz                    
    12,468,219,062 stalled-cycles-frontend   #   22.73% frontend cycles idle   
   <not supported> stalled-cycles-backend  
   145,613,928,157 instructions              #    2.65  insns per cycle        
                                             #    0.09  stalled cycles per insn
    10,440,175,398 branches                  #  577.763 M/sec                  
        16,663,902 branch-misses             #    0.16% of all branches        
   <not supported> L1-dcache-loads:HG      
       366,710,855 L1-dcache-load-misses:HG  #    0.00% of all L1-dcache hits  
       143,060,908 LLC-loads:HG              #    7.917 M/sec                  
   <not supported> LLC-load-misses:HG      

      39.571966346 seconds time elapsed

подрихтовав свой

static void
videoconvert_convert_matrix8 (VideoConvert * convert, gpointer pixels)
{
  int t;
  guint8 r, g, b;
  guint8 *p = pixels;

  for (t = 0; t < convert->width*4; t+=4) {
    r=p[t+1];
    g=p[t+2];
    b=p[t+3];
    p[t+1] = RGB2Y(r,g,b);
    p[t+2] = RGB2U(r,g,b);
    p[t+3] = RGB2V(r,g,b);
  }
}

12890.322127 task-clock                #    0.375 CPUs utilized          
             7,961 context-switches          #    0.618 K/sec                  
               629 cpu-migrations            #    0.049 K/sec                  
            11,079 page-faults               #    0.859 K/sec                  
    39,079,436,257 cycles                    #    3.032 GHz                    
     7,285,528,357 stalled-cycles-frontend   #   18.64% frontend cycles idle   
   <not supported> stalled-cycles-backend  
   121,373,576,357 instructions              #    3.11  insns per cycle        
                                             #    0.06  stalled cycles per insn
     4,304,913,042 branches                  #  333.965 M/sec                  
        13,963,979 branch-misses             #    0.32% of all branches        
   <not supported> L1-dcache-loads:HG      
       240,788,059 L1-dcache-load-misses:HG  #    0.00% of all L1-dcache hits  
       134,563,122 LLC-loads:HG              #   10.439 M/sec                  
   <not supported> LLC-load-misses:HG      

      34.403731457 seconds time elapsed

и вот так с тем что я привел, с лишними перменными и тд

      14806.823788 task-clock                #    0.369 CPUs utilized          
             9,277 context-switches          #    0.627 K/sec                  
               531 cpu-migrations            #    0.036 K/sec                  
            12,720 page-faults               #    0.859 K/sec                  
    44,977,358,278 cycles                    #    3.038 GHz                    
     7,938,928,959 stalled-cycles-frontend   #   17.65% frontend cycles idle   
   <not supported> stalled-cycles-backend  
   137,311,261,317 instructions              #    3.05  insns per cycle        
                                             #    0.06  stalled cycles per insn
     5,015,188,461 branches                  #  338.708 M/sec                  
        16,206,964 branch-misses             #    0.32% of all branches        
   <not supported> L1-dcache-loads:HG      
       280,896,625 L1-dcache-load-misses:HG  #    0.00% of all L1-dcache hits  
       156,673,250 LLC-loads:HG              #   10.581 M/sec                  
   <not supported> LLC-load-misses:HG      


Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Carb_blog2

я запускаю так
для vaapi аппаратное кодирование на интеле
gst-launch-1.0 -e matroskamux name=muxer ! progressreport ! queue ! filesink location=/home/pont/disk/rec_2014-06-17_190428.mkv \ ximagesrc use-damage=0 ! queue ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=NV12,framerate=25/1 ! queue ! vaapiencode_h264 ! queue ! muxer.video_0

для omx а радеоне

gst-launch-1.0 -e matroskamux name=muxer ! progressreport ! queue ! filesink location=/home/pont/disk/rec_2014-06-17_191004.mkv \ ximagesrc use-damage=0 ! queue ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=NV12,framerate=25/1 ! queue ! omxh264enc ! h264parse ! queue ! muxer.video_0


елси этого всего нет - то софтово
gst-launch-1.0 -e matroskamux name=muxer ! progressreport ! queue ! filesink location=/home/pont/disk/rec_2014-06-17_191037.mkv \ ximagesrc use-damage=0 ! queue ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=NV12,framerate=25/1 ! queue ! x264enc speed-preset=slow ! queue ! muxer.video_0 \

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

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Novell-ch

Хм, покажи опции компиляции.

Я тут посмотрел, и, похоже, CLIP не нужен. Попробуй убрать его из дефанов преобразований.

anonymous
()
Ответ на: комментарий от Novell-ch

для vaapi аппаратное кодирование на интеле

Это видюшки на ителах? От хасвелла пойдёт?

$gst-launch-1.0 -e matroskamux name=muxer ! progressreport ! queue ! filesink location=./test.mkv \ ximagesrc use-damage=0 ! queue ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=NV12,framerate=25/1 ! queue ! x264enc speed-preset=slow ! queue ! muxer.video_0
WARNING: erroneous pipeline: could not link queue2 to x264enc0

gst - 1.2.4, как и все плагины. Какая у вас версия?

Carb_blog2
()
Ответ на: комментарий от intelfx

Я знаю, что это софтовый.

Про видюшку я просил, ибо у меня она есть, но вырублена в биосе, а юзаю я нвидиа. Мне лень ребутаться, врубать, ставить дрова, если потом может не сработать.

Carb_blog2
()
Ответ на: комментарий от intelfx

Ещё раз, мне лень ребуться, чтобы всё это врубать.

Я хочу проверить на софтовом, чтож он не работает? Там какая-то ошибка в правилах.

Carb_blog2
()
Ответ на: комментарий от Carb_blog2

Скопировал ваш
[code]
gst-launch-1.0 -e matroskamux name=muxer ! progressreport ! queue ! filesink location=./test.mkv \ ximagesrc use-damage=0 ! queue ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=NV12,framerate=25/1 ! queue ! x264enc speed-preset=slow ! queue ! muxer.video_0
[/code]
выдало ошибку но потом заработало, вообще не вижу смысла с стольких queue

можно и так
[code]
gst-launch-1.0 -e matroskamux name=muxer ! progressreport ! queue ! filesink location=./test.mkv \ ximagesrc use-damage=0 ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=NV12,framerate=25/1 ! x264enc speed-preset=fast ! muxer.video_0
[/code]

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от anonymous

[ 6s] + CFLAGS='-fmessage-length=0 -grecord-gcc-switches -fstack-protector -O2 -Wall -D_FORTIFY_SOURCE=2 -funwind-tables -fasynchronous-unwind-tables -g'
[ 6s] + export CFLAGS
[ 6s] + CXXFLAGS='-fmessage-length=0 -grecord-gcc-switches -fstack-protector -O2 -Wall -D_FORTIFY_SOURCE=2 -funwind-tables -fasynchronous-unwind-tables -g'
[ 6s] + export CXXFLAGS
[ 6s] + FFLAGS='-fmessage-length=0 -grecord-gcc-switches -fstack-protector -O2 -Wall -D_FORTIFY_SOURCE=2 -funwind-tables -fasynchronous-unwind-tables -g'


пробовал убрать CLIP, с ним и без него видео имеет траблы с серым цветом, он стает черный, ну и загрузка больше что с клипом что без.

Novell-ch ★★★★★
() автор топика
Последнее исправление: Novell-ch (всего исправлений: 1)
Ответ на: комментарий от Novell-ch

Скопировал ваш

Оно не моё, а ваше.

выдало ошибку но потом заработало, вообще не вижу смысла с стольких queue

О5 же ваше.

У меня оно не работает, я нашел причину - x264enc не хочет ваше NV12, а ошибку не выдаёт. Если убрать формат, то всё работает норм, только у меня юзает не videoconvert_convert_matrix8(), а videoconvert_convert_matrix16() на софтваре.

Поставил я тут дрова, всё работает на хардваре - дёргает videoconvert_convert_matrix8(). Как будет не лень - погляжу. У вас же новый процессор? cat /proc/cpuinfo

Carb_blog2
()
Ответ на: комментарий от Carb_blog2
cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 58
model name      : Intel(R) Core(TM) i5-3330 CPU @ 3.00GHz
stepping        : 9
microcode       : 0x15
cpu MHz         : 2280.000
cache size      : 6144 KB
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 4
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms
bogomips        : 5986.72
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual

странно что x264enc не хочет nv12,

Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-h264
              framerate: [ 0/1, 2147483647/1 ]
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
          stream-format: { avc, byte-stream }
              alignment: au
                profile: { high-4:4:4, high-4:2:2, high-10, high, main, baseline, constrained-baseline, high-4:4:4-intra, high-4:2:2-intra, high-10-intra }

  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-raw
                 format: { I420, YV12, Y42B, Y444, NV12, I420_10LE, I422_10LE, Y444_10LE }
              framerate: [ 0/1, 2147483647/1 ]
                  width: [ 16, 2147483647 ]
                 height: [ 16, 2147483647 ]

а вот интел интел еще хавает I420 и YV12, а omx нет, потому решил оставить на nv12

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Novell-ch

Мой бенчмарк - https://github.com/amozzhuhin/rgb2yuv-bench. C твоими флагами на i7 выдаёт:

rgb2yuv_wiki: 0.900 sec
rgb2yuv_novell_ch: 0.390 sec
rgb2yuv_tables: 0.320 sec

Т.е прирост с таблицами около 20%, относительно твоей первоначальной реализации. На слабом железе будет ещё больше заметен разрыв.

пробовал убрать CLIP, с ним и без него видео имеет траблы с серым цветом, он стает черный, ну и загрузка больше что с клипом что без.

Да, есть несколько стандартов на преобразование RGB <-> YUV, всё зависит от применения. В реализации GStreamer коэффициенты для преобразования подготавливаются в videoconvert_convert_compute_matrix. Их также можно занести в таблицы.

Дальше уже этот код не оптимизировать средствами Си. Можно только затачивать под конкретное железо или распараллеливать.

anonymous
()
Ответ на: комментарий от anonymous

Спасибо за труд, будет с чем разобраться, у меня получилось так

[code]
rgb2yuv_wiki: 0.671 sec
rgb2yuv_novell_ch: 0.421 sec
rgb2yuv_tables: 0.362 sec
[/code]

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Novell-ch

еще меня смущает разница в выводе пикселей

rgb2yuv_wiki: 0.671 sec
rgb2yuv_novell_ch: 0.424 sec
rgb2yuv_tables: 0.360 sec
1000: 0 0 0
1001: 131 129 129
1002: 127 128 128
1003: 178 164 164
1004: 0 0 0
1005: 51 60 60
1006: 114 114 114
1007: 125 127 127
1008: 0 0 0
1009: 64 72 72
1010: 190 190 190
1011: 87 99 99
1012: 0 0 0
1013: 119 119 119
1014: 133 133 133

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

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Novell-ch

еще меня смущает разница в выводе пикселей

Насколько я понимаю, нет единого правильного способа перевода RGB в YUV (точнее YCbCr). Для разного применения используются разные коэффициенты преобразования. Тебе надо будет взять коэффициенты из videoconvert_convert_compute_matrix.

Также, реализация которую ты взял и в GStreamer - это приближенные вычисления в числах с фиксированной точкой, чтобы на слабом железе без FPU работало приемлемо. Результат выражения с честной плавающей точкой будет отличатся.

anonymous
()
Ответ на: комментарий от Novell-ch

Появилось тут у меня время - поясню твои линки для потомков.

Штука в том, что я не знаю почему, но в видосиках юзается даунистический формат записи «пикселей» 0rgb0rgb.

Формат 0rgb0rgb не векторизируется в принципе, вернее векторизуется, но не имеет смысла: Формат вектора:

vec xrgb2 = {0, r, g, b, 0, r, g, b};//формат в gst

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

Мы можем хранить пиксели в формате rrrrrrrgggggggbbbbbbb - т.е. 4-8 красных, потом зелёных, а потом синих. Это даст нам вектора формата:

vec r8 = {r, r, r, r, r, r, r, r};
vec g8 = {g, g, g, g, g, g, g, g};
vec b8 = {b, b, b, b, b, b, b, b};

Тут векторизуется проще паренной репы - мы работаем с векторами так же, как-будто это скаляр. Это даёт линейный буст.

Почему в видосиках сразу такой формат не юзается, а юзается ущербный 0rgb0rgb0rgb я не знаю.

Тот код, который ты дал - переводит даунистический формат 0rgb0rgb в формат rrggbb, расширяя пиксели с 8 до 16бит

Делает он это следующим образом:

{0, r1, g1, b1}//pixel1 4*8bit
//+
{0, r2, g2, b2}//pixel2 4*8bit
//=
{r1, 0, r2, 0}//p1+p2 2*16bit
//делется так:
//(
{r1, g1, b1, 0}//смешаем на байт влево
//and
{0xff, 0, 0, 0}//маска - обнуляем остальные байты
//)
//or
//(
{0, 0, r2, g2}//смешаем на байт вправо
//and
{0, 0, 0xff, 0}//маска - обнуляем остальные байты
//)
//==
{r1, 0, r2, 0}//теперь его можно использовать не как 4*8bit, а как:
{r1, r2}//2*16bit.

Точно так же меняя аргументы сдвига - мы сдвигаем каждый из цветов на 0-е и 3-е место.

//Т.е. из:
uint8_t p1[4] = {0, r1, g1, b1};
uint8_t p2[4] = {0, r2, g2, b2};
//мы получаем:
uint16_t r[2] = {r1, r2};
uint16_t g[2] = {g1, g2};
uint16_t b[2] = {b1, b2};

Далее, как я уже говорил - мы множим эти массивы(вектора) как скаляры - поведение будет тоже самое.

Мы получим:

uint16_t y[2] = {y1, y2};
uint16_t u[2] = {u1, u2};
uint16_t v[2] = {v1, v2};

Далее, осталось их преобразовать из формата yyuuvv в формат 0yuv0yuv - делается обратным преобразованием - т.е. нам надо каждый сдвинуть на своё место - там это сделано шафлом.

Пичально, что у тебя нету avx2 - пойду я пока попью чайку, почитаю мануал по ивику и подумаю как это можно быстрее всего запилить. Запилю и на примере кода уже расскажу и поясню как пилить по-пацаночке, тоже для потомков. Раз уж ты жалуешься, что по инфы мало.

Carb_blog2
()

Длинные циклы можно параллелить с помощью OpenMP. Нужно просто добавить перед циклом директиву и собрать с -fopenmp

void
videoconvert_convert_matrix8_simpl (VideoConvert * convert, gpointer pixels)
{
  int i,t;
  int y, u, v;
  int r, g, b;
  guint8 *p = pixels;

  #pragma omp parallel for private(t, r, g, b, y, u, v)
  for (i = 0; i < convert->width; i++) {
    t=i*4; 
    y=t+1;
    u=y+1;
    v=u+1;
    r=p[t+1];
    g=p[t+2];
    b=p[t+3];
    p[y] = RGB2Y(r,g,b);
    p[u] = RGB2U(r,g,b);
    p[v] = RGB2V(r,g,b);
  }
}

У меня на 4-х ядрах (и на игрушечном тесте) получился прирост скорости в 2.5 раза

C SSE тоже можно ускорить. Я помедитировал в ссылку на ssr которую вы дали и написал оптимизированную функцию: http://pastebin.com/eS4ZWKA1 , собирать с -msse4.1

Суммарно, с SSE3 и OpenMP у меня получилось ускорить где-то раз в 6. Но это все зависит от железа, конечно.

Кстати, если не жалко памяти, можно вообще сгенерировать всю таблицу преобразований rgb->yuv и читать из нее. Нужно будет 64М если не экономить. Скорее всего это был бы самый быстрый способ

Deleted
()
Ответ на: комментарий от Deleted

огромное спасибо за работу, потестю сейчас с OpenMP, и вопрос какие хедеры нужны для работы с sse3, я поробовал и у меня ругаеться,

videoconvert.c: In function 'videoconvert_convert_matrix8':
[   33s] videoconvert.c:239:17: warning: implicit declaration of function '_mm_shuffle_epi8' [-Wimplicit-function-declaration]
[   33s]                  res = _mm_or_si128(_mm_shuffle_epi8(y, mask_y1), _mm_shuffle_epi8(u, mask_u1));
[   33s]                  ^
[   33s] videoconvert.c:239:17: warning: nested extern declaration of '_mm_shuffle_epi8' [-Wnested-externs]
[   33s] videoconvert.c:239:17: error: incompatible type for argument 1 of '_mm_or_si128'
[   33s] In file included from videoconvert.c:30:0:
[   33s] /usr/lib64/gcc/x86_64-suse-linux/4.8/include/emmintrin.h:1242:1: note: expected '__m128i' but argument is of type 'int'
[   33s]  _mm_or_si128 (__m128i __A, __m128i __B)
[   33s]  ^
[   33s] videoconvert.c:239:17: error: incompatible type for argument 2 of '_mm_or_si128'
[   33s]                  res = _mm_or_si128(_mm_shuffle_epi8(y, mask_y1), _mm_shuffle_epi8(u, mask_u1));
[   33s]                  ^
[   33s] In file included from videoconvert.c:30:0:
[   33s] /usr/lib64/gcc/x86_64-suse-linux/4.8/include/emmintrin.h:1242:1: note: expected '__m128i' but argument is of type 'int'
[   33s]  _mm_or_si128 (__m128i __A, __m128i __B)
[   33s]  ^
[   33s] videoconvert.c:240:17: error: incompatible type for argument 2 of '_mm_or_si128'
[   33s]                  res = _mm_or_si128(res, _mm_shuffle_epi8(v, mask_v1));

да и все же алгоритм который я привел чуть не так цвета переводит, в родном (данные из convert->cmatrix ) будут другие числа и другие смещения,

// #define RGB2Y(R, G, B) (( (  47 * (R) + 157 * (G) +  16 * (B) + 4096) >> SCALE)
// #define RGB2U(R, G, B) ((( ( -26 * (R) +  -87 * (G) + 112 * (B) + 32768) >> SCALE) + 128)
// #define RGB2V(R, G, B) ((( ( 112 * (R) +  -102 * (G) +  -10 * (B) + 32768) >> SCALE) + 128)

пока я остановился на таком, в нем с цветами норм, но он не такой быстрый как был, но быстрее родного за счет выноса convert->cmatrix, вот его и попробую с openmp.

static int p00,p01,p02,p03;
static int p04,p05,p06,p07;
static int p08,p09,p10,p11;
static int op=0;

static void
videoconvert_convert_matrix8 (VideoConvert * convert, gpointer pixels)
{
 while (op<2) {
  p00=convert->cmatrix[0][0];
  p01=convert->cmatrix[0][1];
  p02=convert->cmatrix[0][2];
  p03=convert->cmatrix[0][3];
  p04=convert->cmatrix[1][0];
  p05=convert->cmatrix[1][1];
  p06=convert->cmatrix[1][2];
  p07=convert->cmatrix[1][3];
  p08=convert->cmatrix[2][0];
  p09=convert->cmatrix[2][1];
  p10=convert->cmatrix[2][2];
  p11=convert->cmatrix[2][3];
  op=2;
  }
  
  int t;
  int r, g, b;
  int y, u, v;
  guint8 *p = pixels;

  for (t = 0; t < convert->width; t++) {
    r = p[t*4 + 1];
    g = p[t*4 + 2];
    b = p[t*4 + 3];

    y = (p00 * r + p01 * g +
        p02 * b + p03) >> SCALE;
    u = (p04 * r + p05 * g +
        p06 * b + p07) >> SCALE;
    v = (p08 * r + p09 * g +
        p10 * b + p11) >> SCALE;

    p[t*4 + 1] = CLAMP (y, 0, 255);
    p[t*4 + 2] = CLAMP (u, 0, 255);
    p[t*4 + 3] = CLAMP (v, 0, 255);
  }
}

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Novell-ch

какие хедеры нужны для работы с sse3

#include <xmmintrin.h>
#include <emmintrin.h>
#include <tmmintrin.h>

другие числа и другие смещения

http://pastebin.com/X0Xfkg5t - функция с вашими коэффициентами. Я ее не проверял, только посмотрел что собирается

Deleted
()
Ответ на: комментарий от Deleted

Это волшебно,

Performance counter stats for process id '17341':

      11624.741497 task-clock                #    0.215 CPUs utilized          
            23,139 context-switches          #    0.002 M/sec                  
             2,382 cpu-migrations            #    0.205 K/sec                  
             1,408 page-faults               #    0.121 K/sec                  
    35,074,816,299 cycles                    #    3.017 GHz                    
    11,263,050,921 stalled-cycles-frontend   #   32.11% frontend cycles idle   
   <not supported> stalled-cycles-backend  
    84,552,447,638 instructions              #    2.41  insns per cycle        
                                             #    0.13  stalled cycles per insn
     4,414,370,895 branches                  #  379.739 M/sec                  
        26,244,714 branch-misses             #    0.59% of all branches        
   <not supported> L1-dcache-loads:HG      
       610,313,651 L1-dcache-load-misses:HG  #    0.00% of all L1-dcache hits  
       282,274,006 LLC-loads:HG              #   24.282 M/sec                  
   <not supported> LLC-load-misses:HG      

      54.139526076 seconds time elapsed

захват видео фуллхд и кодирование его аппаратно и всего 20% проца занято.

Пробовал с openmp, загрузка была еще выше, там какая то либа типа libopenmp или как то так жрала больше в пару раз чем если не использовать оптимизацию. вот что показвает perf top тот кусок что жрет больше всего, но и так нереально круто стало, http://pastebin.com/KNQjhh80

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Deleted

да вторая не работает, точне цвета как бы инвертированы, полный абстракционизм и лсдэшный трип.
Но и первой за глаза хватает.
Попробую вторую переделать что бы она брала из convert->cmatrix[][] данные. Или может там не хватет аналога CLAMP (v, 0, 255) я его упустил в своем посте.

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

Прадставил картину, комп с 3 видеокартами, интел через ваапи кодит h264, радеон рендерит 3д и отдает интелу, и нвидия ускоряет видеоконверт с rgb в yuv черезу куду, это же unix way в чистом виде.

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

пока не научится жать h264 через gstreamer, интел и радеон не будут лишние.

Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Я оказался ССЗБ непонятно зачем дописал + 128 для g и b. Переделал функцию так, что бы юзала convert->cmatrix вместо хардкода, все леатет, спасибо вам и всем отметившимся за труд.

static void
videoconvert_convert_matrix8 (VideoConvert * convert, gpointer pixels)
{
        int i,j;
        guint8 *p = pixels;
 
        __m128i v_byte1 = _mm_set1_epi32(0x000000ff);
        __m128i v_byte3 = _mm_set1_epi32(0x00ff0000);
        __m128i v_mat_00 = _mm_set1_epi16((short int)convert->cmatrix[0][0]);
        __m128i v_mat_01 = _mm_set1_epi16((short int)convert->cmatrix[0][1]);
        __m128i v_mat_02 = _mm_set1_epi16((short int)convert->cmatrix[0][2]);
        __m128i v_mat_03 = _mm_set1_epi16((short int)convert->cmatrix[0][3]);
        __m128i v_mat_04 = _mm_set1_epi16((short int)convert->cmatrix[1][0]);
        __m128i v_mat_05 = _mm_set1_epi16((short int)convert->cmatrix[1][1]);
        __m128i v_mat_06 = _mm_set1_epi16((short int)convert->cmatrix[1][2]);
        __m128i v_mat_07 = _mm_set1_epi16((short int)convert->cmatrix[1][3]);
        __m128i v_mat_08 = _mm_set1_epi16((short int)convert->cmatrix[2][0]);
        __m128i v_mat_09 = _mm_set1_epi16((short int)convert->cmatrix[2][1]);
        __m128i v_mat_10 = _mm_set1_epi16((short int)convert->cmatrix[2][2]);
        __m128i v_mat_11 = _mm_set1_epi16((short int)convert->cmatrix[2][3]);
        
        __m128i mask2   = _mm_set1_epi32(0x00ff00ff);
 
        __m128i mask_y1 = _mm_set_epi8((char)128, (char)128, 12, (char)128,   (char)128, (char)128, 8, (char)128,
                                        (char)128, (char)128, 4, (char)128,   (char)128, (char)128, 0, (char)128);
 
        __m128i mask_y2 = _mm_set_epi8((char)128, (char)128, 14,  (char)128,  (char)128, (char)128, 10, (char)128,
                                        (char)128, (char)128, 6, (char)128,   (char)128, (char)128, 2, (char)128);
 
        __m128i mask_u1 = _mm_set_epi8((char)128, 12, (char)128, (char)128,   (char)128, 8, (char)128, (char)128,
                                        (char)128, 4, (char)128, (char)128,   (char)128, 0, (char)128, (char)128);
 
        __m128i mask_u2 = _mm_set_epi8((char)128, 14, (char)128, (char)128,   (char)128, 10, (char)128, (char)128,
                                        (char)128, 6, (char)128, (char)128,   (char)128, 2, (char)128, (char)128);
 
        __m128i mask_v1 = _mm_set_epi8(12, (char)128, (char)128, (char)128,   8, (char)128, (char)128, (char)128,
                                        4, (char)128, (char)128, (char)128,   0, (char)128, (char)128, (char)128);
 
        __m128i mask_v2 = _mm_set_epi8(14, (char)128, (char)128, (char)128,   10, (char)128, (char)128, (char)128,
                                        6, (char)128, (char)128, (char)128,   2, (char)128, (char)128, (char)128);
 
        
        for (i=0; i<convert->width / 8; i++) {
                __m128i a1, a2, r, g, b, y, u, v, res;
 
                a1 = _mm_loadu_si128((__m128i *)&p[i*32]);
                a2 = _mm_loadu_si128((__m128i *)&p[i*32 + 16]);
 
                r = _mm_or_si128(_mm_and_si128(_mm_srli_si128(a1, 1), v_byte1), _mm_and_si128(_mm_slli_si128(a2, 1), v_byte3));
                g = _mm_or_si128(_mm_and_si128(_mm_srli_si128(a1, 2), v_byte1), _mm_and_si128(a2, v_byte3));
                b = _mm_or_si128(_mm_and_si128(_mm_srli_si128(a1, 3), v_byte1), _mm_and_si128(_mm_srli_si128(a2, 1), v_byte3));
 
 
                y = _mm_add_epi16(
                        _mm_add_epi16(
                                _mm_mullo_epi16(r, v_mat_00),
                                _mm_mullo_epi16(g, v_mat_01)),
                        _mm_add_epi16(
                                _mm_mullo_epi16(b, v_mat_02),
                                v_mat_03));
 
                y = _mm_and_si128(_mm_srai_epi16(y, 8), mask2);
 
                u = _mm_add_epi16(
                        _mm_add_epi16(
                                _mm_mullo_epi16(r, v_mat_04),
                                _mm_mullo_epi16(g, v_mat_05)),
                        _mm_add_epi16(
                                _mm_mullo_epi16(b, v_mat_06),
                                v_mat_07));
 
                u  = _mm_and_si128(_mm_srai_epi16(u, 8), mask2);
 
                v = _mm_add_epi16(
                        _mm_add_epi16(
                                _mm_mullo_epi16(r, v_mat_08),
                                _mm_mullo_epi16(g, v_mat_09)),
                        _mm_add_epi16(
                                _mm_mullo_epi16(b, v_mat_10),
                                v_mat_11));
 
                v = _mm_and_si128(_mm_srai_epi16(v, 8), mask2);
 
 
                res = _mm_or_si128(_mm_shuffle_epi8(y, mask_y1), _mm_shuffle_epi8(u, mask_u1));
                res = _mm_or_si128(res, _mm_shuffle_epi8(v, mask_v1));
 
                _mm_storeu_si128((__m128i *)&p[i*32], res);
 
                res = _mm_or_si128(_mm_shuffle_epi8(y, mask_y2), _mm_shuffle_epi8(u, mask_u2));
               res = _mm_or_si128(res, _mm_shuffle_epi8(v, mask_v2));
 
                _mm_storeu_si128((__m128i *)&p[i*32 + 16], res);
        }
}
Novell-ch ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

Потомучто будет говно. Тут дёргается вызов на каждую строку видео, если бы ещё на каждый кадр, то ладно, но тут на каждую строку.

1080*24 = 25к созданий тредов/жоинов. Профита не даст. У меня это 0.1 утилизация ведра - это 25*10 = 250к созданий тредов/жоинов в секунду. Это выше предела.

Carb_blog2
()
Ответ на: комментарий от bhfq

Апстрим «кросс(м)архитектурный», такое туда не пойдёт. Хотя там есть какая-то бадяга на orc, но типа это тоже «кросс(м)архитектурный».

Carb_blog2
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.