Optimized decimal and hex string parsing in hot path.
Using specialized and inline-able parsers instead of strtoul() improved pixel throughput from ~63MB/s to ~83MB/s (+30%) on a single core on my machine. As a side effect, the PX command now allows any non-decimal character as separator between parameters ¯\_(ツ)_/¯
This commit is contained in:
parent
b3b6c1c042
commit
1b36a66243
2 changed files with 66 additions and 26 deletions
|
|
@ -1,15 +1,16 @@
|
||||||
.PHONY: default all clean
|
.PHONY: default all clean
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -pthread -O2
|
CFLAGS = -Wall -pthread
|
||||||
LIBS = -levent -levent_pthreads -lrt -lGL -lGLEW -lglfw
|
LIBS = -levent -levent_pthreads -lrt -lGL -lGLEW -lglfw
|
||||||
TARGET = pixelnuke
|
TARGET = pixelnuke
|
||||||
|
|
||||||
|
default: CFLAGS += -O2 -flto
|
||||||
default: $(TARGET)
|
default: $(TARGET)
|
||||||
all: default
|
all: default
|
||||||
|
|
||||||
debug: CFLAGS += -DDEBUG -g
|
debug: CFLAGS += -DDEBUG -g
|
||||||
debug: default
|
debug: $(TARGET)
|
||||||
|
|
||||||
OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c))
|
OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c))
|
||||||
HEADERS = $(wildcard *.h)
|
HEADERS = $(wildcard *.h)
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,14 @@ unsigned int px_height = 1024;
|
||||||
unsigned int px_pixelcount = 0;
|
unsigned int px_pixelcount = 0;
|
||||||
unsigned int px_clientcount = 0;
|
unsigned int px_clientcount = 0;
|
||||||
|
|
||||||
|
// User sessions
|
||||||
|
typedef struct PxSession {
|
||||||
|
|
||||||
|
} PxSession;
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
static int util_str_starts_with(const char* prefix, const char* str) {
|
static inline int fast_str_startswith(const char* prefix, const char* str) {
|
||||||
char cp, cs;
|
char cp, cs;
|
||||||
while ((cp = *prefix++) == (cs = *str++)) {
|
while ((cp = *prefix++) == (cs = *str++)) {
|
||||||
if (cp == 0)
|
if (cp == 0)
|
||||||
|
|
@ -21,39 +26,71 @@ static int util_str_starts_with(const char* prefix, const char* str) {
|
||||||
return !cp;
|
return !cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decimal string to unsigned int. This variant does NOT consume +, - or whitespace.
|
||||||
|
// If **endptr is not NULL, it will point to the first non-decimal character, which
|
||||||
|
// may be \0 at the end of the string.
|
||||||
|
static inline uint32_t fast_strtoul10(const char *str, const char **endptr) {
|
||||||
|
uint32_t result = 0;
|
||||||
|
unsigned char c;
|
||||||
|
for (; (c = *str - '0') <= 9; str++)
|
||||||
|
result = result * 10 + c;
|
||||||
|
if (endptr)
|
||||||
|
*endptr = str;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as fast_strtoul10, but for hex strings.
|
||||||
|
static inline uint32_t fast_strtoul16(const char *str, const char **endptr) {
|
||||||
|
uint32_t result = 0;
|
||||||
|
unsigned char c;
|
||||||
|
while ((c = *str - '0') <= 9 // 0-9
|
||||||
|
|| ((c -= 7) >= 10 && c <= 15) // A-F
|
||||||
|
|| ((c -= 32) >= 10 && c <= 15)) { // a-f
|
||||||
|
result = result * 16 + c;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
if (endptr)
|
||||||
|
*endptr = str;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// server callbacks
|
// server callbacks
|
||||||
void px_on_connect(NetClient *client) {
|
void px_on_connect(NetClient *client) {
|
||||||
px_clientcount++;
|
px_clientcount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void px_on_close(NetClient *client, int error) {
|
void px_on_close(NetClient *client, int error) {
|
||||||
px_clientcount--;
|
px_clientcount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void px_on_read(NetClient *client, char *line) {
|
void px_on_read(NetClient *client, char *line) {
|
||||||
if (util_str_starts_with("PX ", line)) {
|
if (fast_str_startswith("PX ", line)) {
|
||||||
const char * ptr = line + 3;
|
const char * ptr = line + 3;
|
||||||
char * endptr = (char*) ptr;
|
const char * endptr = ptr;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
unsigned int x = strtoul(ptr, &endptr, 10);
|
uint32_t x = fast_strtoul10(ptr, &endptr);
|
||||||
if (endptr == ptr || errno) {
|
if (endptr == ptr) {
|
||||||
net_err(client,
|
net_err(client,
|
||||||
"First parameter missing or invalid (should be decimal)");
|
"Invalid command (expected decimal as first parameter)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (*endptr == '\0') {
|
||||||
|
net_err(client, "Invalid command (second parameter required)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int y = strtoul((ptr = endptr), &endptr, 10);
|
endptr++; // eat space (or whatever non-decimal is found here)
|
||||||
if (endptr == ptr || errno) {
|
|
||||||
|
uint32_t y = fast_strtoul10((ptr = endptr), &endptr);
|
||||||
|
if (endptr == ptr) {
|
||||||
net_err(client,
|
net_err(client,
|
||||||
"Second parameter missing or invalid (should be decimal)");
|
"Invalid command (expected decimal as second parameter)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PX <x> <y> -> Get RGB color at position (x,y) or '0x000000' for out-of-range queries
|
// PX <x> <y> -> Get RGB color at position (x,y) or '0x000000' for out-of-range queries
|
||||||
if (*endptr == 0) {
|
if (*endptr == '\0') {
|
||||||
uint32_t c;
|
uint32_t c;
|
||||||
canvas_get_px(x, y, &c);
|
canvas_get_px(x, y, &c);
|
||||||
char str[64];
|
char str[64];
|
||||||
|
|
@ -62,20 +99,22 @@ void px_on_read(NetClient *client, char *line) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endptr++; // eat space (or whatever non-decimal is found here)
|
||||||
|
|
||||||
// PX <x> <y> BB|RRGGBB|RRGGBBAA
|
// PX <x> <y> BB|RRGGBB|RRGGBBAA
|
||||||
unsigned long int c = strtoul((ptr = endptr), &endptr, 16);
|
uint32_t c = fast_strtoul16((ptr = endptr), &endptr);
|
||||||
if (endptr == ptr || errno) {
|
if (endptr == ptr) {
|
||||||
net_err(client,
|
net_err(client,
|
||||||
"Third parameter missing or invalid (should be hex color)");
|
"Third parameter missing or invalid (should be hex color)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endptr - 1 - 6 == ptr) {
|
if (endptr - ptr == 6) {
|
||||||
// RGB -> RGBA
|
// RGB -> RGBA (most common)
|
||||||
c = (c << 8) + 0xff;
|
c = (c << 8) + 0xff;
|
||||||
} else if (endptr - 1 - 8 == ptr) {
|
} else if (endptr - ptr == 8) {
|
||||||
// done
|
// done
|
||||||
} else if (endptr - 1 - 2 == ptr) {
|
} else if (endptr - ptr == 2) {
|
||||||
// WW -> RGBA
|
// WW -> RGBA
|
||||||
c = (c << 24) + (c << 16) + (c << 8) + 0xff;
|
c = (c << 24) + (c << 16) + (c << 8) + 0xff;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -87,22 +126,23 @@ void px_on_read(NetClient *client, char *line) {
|
||||||
px_pixelcount++;
|
px_pixelcount++;
|
||||||
canvas_set_px(x, y, c);
|
canvas_set_px(x, y, c);
|
||||||
|
|
||||||
} else if (util_str_starts_with("SIZE", line)) {
|
} else if (fast_str_startswith("SIZE", line)) {
|
||||||
|
|
||||||
char str[64];
|
char str[64];
|
||||||
snprintf(str, 64, "SIZE %d %d", px_width, px_height);
|
snprintf(str, 64, "SIZE %d %d", px_width, px_height);
|
||||||
net_send(client, str);
|
net_send(client, str);
|
||||||
|
|
||||||
} else if (util_str_starts_with("STATS", line)) {
|
} else if (fast_str_startswith("STATS", line)) {
|
||||||
|
|
||||||
char str[128];
|
char str[128];
|
||||||
snprintf(str, 128, "STATS px:%u conn:%u", px_pixelcount, px_clientcount);
|
snprintf(str, 128, "STATS px:%u conn:%u", px_pixelcount,
|
||||||
|
px_clientcount);
|
||||||
net_send(client, str);
|
net_send(client, str);
|
||||||
|
|
||||||
} else if (util_str_starts_with("HELP", line)) {
|
} else if (fast_str_startswith("HELP", line)) {
|
||||||
|
|
||||||
net_send(client,
|
net_send(client,
|
||||||
"\
|
"\
|
||||||
PX x y: Get color at position (x,y)\n\
|
PX x y: Get color at position (x,y)\n\
|
||||||
PX x y rrggbb(aa): Draw a pixel (with optional alpha channel)\n\
|
PX x y rrggbb(aa): Draw a pixel (with optional alpha channel)\n\
|
||||||
SIZE: Get canvas size\n\
|
SIZE: Get canvas size\n\
|
||||||
|
|
@ -115,7 +155,6 @@ STATS: Return statistics");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void px_on_key(int key, int scancode, int mods) {
|
void px_on_key(int key, int scancode, int mods) {
|
||||||
|
|
||||||
printf("Key pressed: key:%d scancode:%d mods:%d\n", key, scancode, mods);
|
printf("Key pressed: key:%d scancode:%d mods:%d\n", key, scancode, mods);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue