Я тут немного поисследовал. В общем, во-первых я переписал последний вариант Сергея на полноценный RGB и байтовыми буферами. Также N я фиксировал (N=13), но зато тест идет не по одному кадру, а по 1000 кадрам (соответственно замеряем время для 1000 кадров - так точнее, и так реалистичнее (от длительного нагрева проц. может снизить частоту)).
Во-вторых я реализовал алгоритм Сергея на С++, чтобы можно было сравнить производительность собственно языков.
В-третьих я из нашего прототипа вытащил алгоритм размытия в отдельный проект. И замерял производительность именно этого алгоритма (а не всего pipeline'а, как раньше).
Итак, вначале результаты. Напомню, что радиус размытия (N) = 13. Обрабатываются все три канала. Число обрабатываемых кадров = 1000.
C# (алгоритм Cергея):
time: 107 seconds
fps : 9.35
С++ (алгоритм Сергея):
time: 96 seconds
fps : 10.42
C++ (наш с Надей алгоритм):
time: 22 seconds
fps : 45.45 fps
Тестировалось всё ровно на том же ультрабуке, что и демонстрировалось на хакатоне.
Далее исходники:
С#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication5
{
class Program
{
static unsafe void Main(string[] args)
{
const int H = 480;
const int W = 640;
byte[] a = new byte[H * W * 3];
byte[] b = new byte[H * W * 3];
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
const int N = 13;
timer.Reset();
timer.Start();
const int frames = 1000;
for (int nn = 0; nn<frames; nn++)
{
fixed (byte* pa = &a[0])
{
fixed (byte* pb = &b[0])
{
for (int n = 0; n < N; n++)
{
for (int y = 1; y < H - 1; y++)
{
for (int x = 1; x < W - 1; x++)
{
for (int color_shift = 0; color_shift < 3; color_shift++)
{
*(pb + (H * y + x) * 3 + color_shift) = (byte)(0.25 * (*(pa + (H * (y - 1) + x) * 3 + color_shift)
+ *(pa + (H * (y + 1) + x) * 3 + color_shift)
+ *(pa + (H * y + x - 1) * 3 + color_shift)
+ *(pa + (H * y + x + 1) * 3 + color_shift)));
}
}
}
for (int y = 1; y < H - 1; y++)
{
for (int x = 1; x < W - 1; x++)
{
for (int color_shift = 0; color_shift < 3; color_shift++)
{
*(pa + (H * y + x) * 3 + color_shift) = (byte)(0.25 * (*(pb + (H * (y - 1) + x) * 3 + color_shift)
+ *(pb + (H * (y + 1) + x) * 3 + color_shift)
+ *(pb + (H * y + x - 1) * 3 + color_shift)
+ *(pb + (H * y + x + 1) * 3 + color_shift)));
}
}
}
}
}
}
}
timer.Stop();
double dt = timer.Elapsed.TotalSeconds;
System.Console.WriteLine("N = {0} (R = {1}), dt={2} seconds, FPS = {3}",
N, 2 * N, dt, (frames / dt));
System.Console.ReadLine();
}
}
}
С++ (алгоритм Сергея):
#include <iostream>
#include <ctime>
const int width = 640;
const int height = 480;
const size_t blurRange = 13;
enum Color {
RED = 0,
GREEN,
BLUE
};
int index(int x, int y, Color color) {
return width*y*3+x*3+color;
}
int main()
{
volatile unsigned char* volatile in = new unsigned char[width*height*3];
volatile unsigned char* volatile out = new unsigned char[width*height*3];
time_t begin;
time(&begin);
const int frames = 1000;
for (int nn=0; nn<frames; nn++) {
for (int i=0; i<blurRange; i++) {
for (int y=1; y<height-1; y++)
for (int x=1; x<width-1; x++) {
out[index(x,y,RED)]= 0.25*(
(float)in[index(x,y-1,RED)]+
in[index(x,y+1,RED)]+
in[index(x-1,y,RED)]+
in[index(x+1,y,RED)]);
out[index(x,y,GREEN)]= 0.25*(
(float)in[index(x,y-1,GREEN)]+
in[index(x,y+1,GREEN)]+
in[index(x-1,y,GREEN)]+
in[index(x+1,y,GREEN)]);
out[index(x,y,BLUE)]= 0.25*(
(float)in[index(x,y-1,BLUE)]+
in[index(x,y+1,BLUE)]+
in[index(x-1,y,BLUE)]+
in[index(x+1,y,BLUE)]);
}
for (int y=1; y<height-1; y++)
for (int x=1; x<width-1; x++) {
in[index(x,y,RED)]=0.25*(
(float)out[index(x,y-1,RED)]+
out[index(x,y+1,RED)]+
out[index(x-1,y,RED)]+
out[index(x+1,y,RED)]);
in[index(x,y,GREEN)]=0.25*(
(float)out[index(x,y-1,GREEN)]+
out[index(x,y+1,GREEN)]+
out[index(x-1,y,GREEN)]+
out[index(x+1,y,GREEN)]);
in[index(x,y,BLUE)]=0.25*(
(float)out[index(x,y-1,BLUE)]+
out[index(x,y+1,BLUE)]+
out[index(x-1,y,BLUE)]+
out[index(x+1,y,BLUE)]);
}
}
}
time_t end;
time(&end);
auto seconds = difftime(end, begin);
std::cout << float(frames)/seconds << " " << seconds << std::endl;
}
С++ (наш алгоритм):
#include <iostream>
#include <ctime>
const int width = 640;
const int height = 480;
const size_t blurRange = 13;
enum Color {
RED = 0,
GREEN,
BLUE
};
int index(int x, int y, Color color) {
return width*y*3+x*3+color;
}
template <size_t N>
struct ring_buffer {
float buf[N];
size_t head;
size_t size;
ring_buffer():head(0), size(0) {memset(buf, 0, sizeof(buf));}
void push_back(float v) {
if (++head >= N) {head=0;}
if (size<N) size++;
buf[head]=v;
}
float back() {return buf[head];}
float front() {return buf[head!=N-1?head+1:0];}
};
int main()
{
volatile unsigned char* volatile in = new unsigned char[width*height*3];
volatile unsigned char* volatile out = new unsigned char[width*height*3];
time_t begin;
time(&begin);
const int frames = 1000;
for (int nn=0; nn<frames; nn++) {
for (int y=blurRange; y<height-blurRange; y++) {
float red = 0;
float green = 0;
float blue = 0;
ring_buffer<blurRange*2> redRows ;
ring_buffer<blurRange*2> blueRows;
ring_buffer<blurRange*2> greenRows;
for (int x=blurRange; x<width-blurRange; x++) {
float vr = 0;
float vg = 0;
float vb = 0;
for (int y1 = y-blurRange; y1<y+blurRange; y1++) {
vr += in[index(x,y1,RED) ];
vg += in[index(x,y1,GREEN)];
vb += in[index(x,y1,BLUE) ];
}
vr = vr/(blurRange*blurRange*4.0);
vg = vg/(blurRange*blurRange*4.0);
vb = vb/(blurRange*blurRange*4.0);
auto front_r = redRows.front();
red += vr - redRows.front();
green += vg - greenRows.front();
blue += vb - blueRows.front();
redRows.push_back(vr);
blueRows.push_back(vb);
greenRows.push_back(vg);
out[index(x,y,RED) ] = red*(blurRange*2.0/redRows.size);
out[index(x,y,GREEN)] = green*(blurRange*2.0/greenRows.size);
out[index(x,y,BLUE) ] = blue*(blurRange*2.0/blueRows.size);
}
}
}
time_t end;
time(&end);
auto seconds = difftime(end, begin);
std::cout << float(frames)/seconds << " " << seconds << std::endl;
return 0;
}