What is beyond ordinary c++, when trying to optimize a function?
Backstory: Writing a QImage to Sixel renderer. Feel like I have optimized it the best I can using basic c++. I have heard suggestions here or there that you can utilize things like GPU, SIMD, insert assembly code, etc. Have no experience in any of that, and would consider that beyond the bounds of ordinary C++. Code is down to 20-30ms rendering, but wish to get it to below 10ms. Unsure if there are even more options beyond GPU, SIMD, etc. Code: For context: void QCursesPixmap::drawBySixel( const QImage &image ) { static QTextStream ts(stdout); static const char * p = "\033Pq"; static const char * q = "\033\\"; static const char * colourPalette = R"(#0;2;0;0;0#1;2;0;0;20#2;2;0;0;40#3;2;0;0;60#4;2;0;0;80#5;2;0;0;100#6;2;0;16;0#7;2;0;16;20#8;2;0;16;40#9;2;0;16;60#10;2;0;16;80#11;2;0;16;100#12;2;0;33;0#13;2;0;33;20#14;2;0;33;40#15;2;0;33;60#16;2;0;33;80#17;2;0;33;100#18;2;0;50;0#19;2;0;50;20#20;2;0;50;40#21;2;0;50;60#22;2;0;50;80#23;2;0;50;100#24;2;0;66;0#25;2;0;66;20#26;2;0;66;40#27;2;0;66;60#28;2;0;66;80#29;2;0;66;100#30;2;0;83;0#31;2;0;83;20#32;2;0;83;40#33;2;0;83;60#34;2;0;83;80#35;2;0;83;100#36;2;0;100;0#37;2;0;100;20#38;2;0;100;40#39;2;0;100;60#40;2;0;100;80#41;2;0;100;100#42;2;20;0;0#43;2;20;0;20#44;2;20;0;40#45;2;20;0;60#46;2;20;0;80#47;2;20;0;100#48;2;20;16;0#49;2;20;16;20#50;2;20;16;40#51;2;20;16;60#52;2;20;16;80#53;2;20;16;100#54;2;20;33;0#55;2;20;33;20#56;2;20;33;40#57;2;20;33;60#58;2;20;33;80#59;2;20;33;100#60;2;20;50;0#61;2;20;50;20#62;2;20;50;40#63;2;20;50;60#64;2;20;50;80#65;2;20;50;100#66;2;20;66;0#67;2;20;66;20#68;2;20;66;40#69;2;20;66;60#70;2;20;66;80#71;2;20;66;100#72;2;20;83;0#73;2;20;83;20#74;2;20;83;40#75;2;20;83;60#76;2;20;83;80#77;2;20;83;100#78;2;20;100;0#79;2;20;100;20#80;2;20;100;40#81;2;20;100;60#82;2;20;100;80#83;2;20;100;100#84;2;40;0;0#85;2;40;0;20#86;2;40;0;40#87;2;40;0;60#88;2;40;0;80#89;2;40;0;100#90;2;40;16;0#91;2;40;16;20#92;2;40;16;40#93;2;40;16;60#94;2;40;16;80#95;2;40;16;100#96;2;40;33;0#97;2;40;33;20#98;2;40;33;40#99;2;40;33;60#100;2;40;33;80#101;2;40;33;100#102;2;40;50;0#103;2;40;50;20#104;2;40;50;40#105;2;40;50;60#106;2;40;50;80#107;2;40;50;100#108;2;40;66;0#109;2;40;66;20#110;2;40;66;40#111;2;40;66;60#112;2;40;66;80#113;2;40;66;100#114;2;40;83;0#115;2;40;83;20#116;2;40;83;40#117;2;40;83;60#118;2;40;83;80#119;2;40;83;100#120;2;40;100;0#121;2;40;100;20#122;2;40;100;40#123;2;40;100;60#124;2;40;100;80#125;2;40;100;100#126;2;60;0;0#127;2;60;0;20#128;2;60;0;40#129;2;60;0;60#130;2;60;0;80#131;2;60;0;100#132;2;60;16;0#133;2;60;16;20#134;2;60;16;40#135;2;60;16;60#136;2;60;16;80#137;2;60;16;100#138;2;60;33;0#139;2;60;33;20#140;2;60;33;40#141;2;60;33;60#142;2;60;33;80#143;2;60;33;100#144;2;60;50;0#145;2;60;50;20#146;2;60;50;40#147;2;60;50;60#148;2;60;50;80#149;2;60;50;100#150;2;60;66;0#151;2;60;66;20#152;2;60;66;40#153;2;60;66;60#154;2;60;66;80#155;2;60;66;100#156;2;60;83;0#157;2;60;83;20#158;2;60;83;40#159;2;60;83;60#160;2;60;83;80#161;2;60;83;100#162;2;60;100;0#163;2;60;100;20#164;2;60;100;40#165;2;60;100;60#166;2;60;100;80#167;2;60;100;100#168;2;80;0;0#169;2;80;0;20#170;2;80;0;40#171;2;80;0;60#172;2;80;0;80#173;2;80;0;100#174;2;80;16;0#175;2;80;16;20#176;2;80;16;40#177;2;80;16;60#178;2;80;16;80#179;2;80;16;100#180;2;80;33;0#181;2;80;33;20#182;2;80;33;40#183;2;80;33;60#184;2;80;33;80#185;2;80;33;100#186;2;80;50;0#187;2;80;50;20#188;2;80;50;40#189;2;80;50;60#190;2;80;50;80#191;2;80;50;100#192;2;80;66;0#193;2;80;66;20#194;2;80;66;40#195;2;80;66;60#196;2;80;66;80#197;2;80;66;100#198;2;80;83;0#199;2;80;83;20#200;2;80;83;40#201;2;80;83;60#202;2;80;83;80#203;2;80;83;100#204;2;80;100;0#205;2;80;100;20#206;2;80;100;40#207;2;80;100;60#208;2;80;100;80#209;2;80;100;100#210;2;100;0;0#211;2;100;0;20#212;2;100;0;40#213;2;100;0;60#214;2;100;0;80#215;2;100;0;100#216;2;100;16;0#217;2;100;16;20#218;2;100;16;40#219;2;100;16;60#220;2;100;16;80#221;2;100;16;100#222;2;100;33;0#223;2;100;33;20#224;2;100;33;40#225;2;100;33;60#226;2;100;33;80#227;2;100;33;100#228;2;100;50;0#229;2;100;50;20#230;2;100;50;40#231;2;100;50;60#232;2;100;50;80#233;2;100;50;100#234;2;100;66;0#235;2;100;66;20#236;2;100;66;40#237;2;100;66;60#238;2;100;66;80#239;2;100;66;100#240;2;100;83;0#241;2;100;83;20#242;2;100;83;40#243;2;100;83;60#244;2;100;83;80#245;2;100;83;100#246;2;100;100;0#247;2;100;100;20#248;2;100;100;40#249;2;100;100;60#250;2;100;100;80#251;2;100;100;96#252;2;100;100;97#253;2;100;100;98#254;2;100;100;99#255;2;100;100;100)"; /* Lookup Table */ const int w = image.width (); const int h = image.height (); const int bpl = image.bytesPerLine(); const quint8 *ui8 = image.constBits(); quint8 *lut_r{nullptr}; quint8 *lut_g{nullptr}; quint8 *lut_b{nullptr}; int bpp; { switch ( image.format() ) { default: return; case QImage::Format_RGB32: { static quint8 r[256], g[256], b[256]; static bool initialized{false}; if ( !initialized ) { for ( int i = 0; i < 256; i++ ) {
Backstory:
- Writing a QImage to Sixel renderer.
- Feel like I have optimized it the best I can using basic c++.
- I have heard suggestions here or there that you can utilize things like GPU, SIMD, insert assembly code, etc.
- Have no experience in any of that, and would consider that beyond the bounds of ordinary C++.
- Code is down to 20-30ms rendering, but wish to get it to below 10ms.
- Unsure if there are even more options beyond GPU, SIMD, etc.
Code:
For context:
void QCursesPixmap::drawBySixel( const QImage &image )
{
static QTextStream ts(stdout);
static const char * p = "\033Pq";
static const char * q = "\033\\";
static const char * colourPalette = R"(#0;2;0;0;0#1;2;0;0;20#2;2;0;0;40#3;2;0;0;60#4;2;0;0;80#5;2;0;0;100#6;2;0;16;0#7;2;0;16;20#8;2;0;16;40#9;2;0;16;60#10;2;0;16;80#11;2;0;16;100#12;2;0;33;0#13;2;0;33;20#14;2;0;33;40#15;2;0;33;60#16;2;0;33;80#17;2;0;33;100#18;2;0;50;0#19;2;0;50;20#20;2;0;50;40#21;2;0;50;60#22;2;0;50;80#23;2;0;50;100#24;2;0;66;0#25;2;0;66;20#26;2;0;66;40#27;2;0;66;60#28;2;0;66;80#29;2;0;66;100#30;2;0;83;0#31;2;0;83;20#32;2;0;83;40#33;2;0;83;60#34;2;0;83;80#35;2;0;83;100#36;2;0;100;0#37;2;0;100;20#38;2;0;100;40#39;2;0;100;60#40;2;0;100;80#41;2;0;100;100#42;2;20;0;0#43;2;20;0;20#44;2;20;0;40#45;2;20;0;60#46;2;20;0;80#47;2;20;0;100#48;2;20;16;0#49;2;20;16;20#50;2;20;16;40#51;2;20;16;60#52;2;20;16;80#53;2;20;16;100#54;2;20;33;0#55;2;20;33;20#56;2;20;33;40#57;2;20;33;60#58;2;20;33;80#59;2;20;33;100#60;2;20;50;0#61;2;20;50;20#62;2;20;50;40#63;2;20;50;60#64;2;20;50;80#65;2;20;50;100#66;2;20;66;0#67;2;20;66;20#68;2;20;66;40#69;2;20;66;60#70;2;20;66;80#71;2;20;66;100#72;2;20;83;0#73;2;20;83;20#74;2;20;83;40#75;2;20;83;60#76;2;20;83;80#77;2;20;83;100#78;2;20;100;0#79;2;20;100;20#80;2;20;100;40#81;2;20;100;60#82;2;20;100;80#83;2;20;100;100#84;2;40;0;0#85;2;40;0;20#86;2;40;0;40#87;2;40;0;60#88;2;40;0;80#89;2;40;0;100#90;2;40;16;0#91;2;40;16;20#92;2;40;16;40#93;2;40;16;60#94;2;40;16;80#95;2;40;16;100#96;2;40;33;0#97;2;40;33;20#98;2;40;33;40#99;2;40;33;60#100;2;40;33;80#101;2;40;33;100#102;2;40;50;0#103;2;40;50;20#104;2;40;50;40#105;2;40;50;60#106;2;40;50;80#107;2;40;50;100#108;2;40;66;0#109;2;40;66;20#110;2;40;66;40#111;2;40;66;60#112;2;40;66;80#113;2;40;66;100#114;2;40;83;0#115;2;40;83;20#116;2;40;83;40#117;2;40;83;60#118;2;40;83;80#119;2;40;83;100#120;2;40;100;0#121;2;40;100;20#122;2;40;100;40#123;2;40;100;60#124;2;40;100;80#125;2;40;100;100#126;2;60;0;0#127;2;60;0;20#128;2;60;0;40#129;2;60;0;60#130;2;60;0;80#131;2;60;0;100#132;2;60;16;0#133;2;60;16;20#134;2;60;16;40#135;2;60;16;60#136;2;60;16;80#137;2;60;16;100#138;2;60;33;0#139;2;60;33;20#140;2;60;33;40#141;2;60;33;60#142;2;60;33;80#143;2;60;33;100#144;2;60;50;0#145;2;60;50;20#146;2;60;50;40#147;2;60;50;60#148;2;60;50;80#149;2;60;50;100#150;2;60;66;0#151;2;60;66;20#152;2;60;66;40#153;2;60;66;60#154;2;60;66;80#155;2;60;66;100#156;2;60;83;0#157;2;60;83;20#158;2;60;83;40#159;2;60;83;60#160;2;60;83;80#161;2;60;83;100#162;2;60;100;0#163;2;60;100;20#164;2;60;100;40#165;2;60;100;60#166;2;60;100;80#167;2;60;100;100#168;2;80;0;0#169;2;80;0;20#170;2;80;0;40#171;2;80;0;60#172;2;80;0;80#173;2;80;0;100#174;2;80;16;0#175;2;80;16;20#176;2;80;16;40#177;2;80;16;60#178;2;80;16;80#179;2;80;16;100#180;2;80;33;0#181;2;80;33;20#182;2;80;33;40#183;2;80;33;60#184;2;80;33;80#185;2;80;33;100#186;2;80;50;0#187;2;80;50;20#188;2;80;50;40#189;2;80;50;60#190;2;80;50;80#191;2;80;50;100#192;2;80;66;0#193;2;80;66;20#194;2;80;66;40#195;2;80;66;60#196;2;80;66;80#197;2;80;66;100#198;2;80;83;0#199;2;80;83;20#200;2;80;83;40#201;2;80;83;60#202;2;80;83;80#203;2;80;83;100#204;2;80;100;0#205;2;80;100;20#206;2;80;100;40#207;2;80;100;60#208;2;80;100;80#209;2;80;100;100#210;2;100;0;0#211;2;100;0;20#212;2;100;0;40#213;2;100;0;60#214;2;100;0;80#215;2;100;0;100#216;2;100;16;0#217;2;100;16;20#218;2;100;16;40#219;2;100;16;60#220;2;100;16;80#221;2;100;16;100#222;2;100;33;0#223;2;100;33;20#224;2;100;33;40#225;2;100;33;60#226;2;100;33;80#227;2;100;33;100#228;2;100;50;0#229;2;100;50;20#230;2;100;50;40#231;2;100;50;60#232;2;100;50;80#233;2;100;50;100#234;2;100;66;0#235;2;100;66;20#236;2;100;66;40#237;2;100;66;60#238;2;100;66;80#239;2;100;66;100#240;2;100;83;0#241;2;100;83;20#242;2;100;83;40#243;2;100;83;60#244;2;100;83;80#245;2;100;83;100#246;2;100;100;0#247;2;100;100;20#248;2;100;100;40#249;2;100;100;60#250;2;100;100;80#251;2;100;100;96#252;2;100;100;97#253;2;100;100;98#254;2;100;100;99#255;2;100;100;100)";
/* Lookup Table */
const int w = image.width ();
const int h = image.height ();
const int bpl = image.bytesPerLine();
const quint8 *ui8 = image.constBits();
quint8 *lut_r{nullptr};
quint8 *lut_g{nullptr};
quint8 *lut_b{nullptr};
int bpp;
{
switch ( image.format() ) {
default: return;
case QImage::Format_RGB32: {
static quint8 r[256], g[256], b[256];
static bool initialized{false};
if ( !initialized ) {
for ( int i = 0; i < 256; i++ ) {
r[i] = (i * 6 >> 8) * 42;
g[i] = (i * 7 >> 8) * 6;
b[i] = i * 6 >> 8;
}}
initialized = true;
bpp = 4;
lut_r = r;
lut_g = g;
lut_b = b;
break;
}}}
ts << p << colourPalette;
for ( int six = 0; six < w*h*bpp - (bpl*6); six += bpl*6 ) {
quint8 colourCount[256] = {0};
#define COLOR(NUM) lut_r[ui8[2+i+bpl*NUM]] + lut_g[ui8[1+i+bpl*NUM]] + lut_b[ui8[i+bpl*NUM]]
for ( int i = six; i < bpl + six; i += bpp ) {
colourCount[ COLOR(0) ]++;
colourCount[ COLOR(1) ]++;
colourCount[ COLOR(2) ]++;
colourCount[ COLOR(3) ]++;
colourCount[ COLOR(4) ]++;
colourCount[ COLOR(5) ]++;
}
for ( int n = 0; n < 256; n++ ) {
if ( colourCount[n] == 0 ) { continue; }
ts << '#' << n;
char c('?');
int repeat(0);
for ( int i = six; i < bpl + six; i+= bpp ) {
char p0( '?' +
( ( n == COLOR(0) ? 1 << 0 : 0 )
| ( n == COLOR(1) ? 1 << 1 : 0 )
| ( n == COLOR(2) ? 1 << 2 : 0 )
| ( n == COLOR(3) ? 1 << 3 : 0 )
| ( n == COLOR(4) ? 1 << 4 : 0 )
| ( n == COLOR(5) ? 1 << 5 : 0 )
));
if ( p0 == c ) {
repeat++;
} else {
switch (repeat) {
case 3: ts << c; Q_FALLTHROUGH();
case 2: ts << c; Q_FALLTHROUGH();
case 1: ts << c; Q_FALLTHROUGH();
case 0: ts << c; break;
default: ts << '!' << repeat + 1 << c;
}
c = p0;
repeat = 0;
}
}
if ( c != '?' ) {
switch (repeat) {
case 3: ts << c; Q_FALLTHROUGH();
case 2: ts << c; Q_FALLTHROUGH();
case 1: ts << c; Q_FALLTHROUGH();
case 0: ts << c; break;
default: ts << '!' << repeat + 1 << c;
}
}
repeat = 0;
ts << '$';
}
ts << '-';
}
ts << q;
ts.flush();
}
Question:
When I get to a point like above, how do I, as a software engineer, ascertain:
- If I can go further?
- Should I go further?
- Where can I go further?
[ Further where? Further beyond the realms of regular C++. ]
Would you say that there are a set number of options one can consider after this, say:
- SIMD
- Assembly
- GPU
- ASICs
- Multithreading
- Foo
- Bar
- Etc?