monome / norns

norns is many sound instruments.
http://monome.org
GNU General Public License v3.0
633 stars 147 forks source link

make grid.refresh non-blocking #1404

Closed catfact closed 3 years ago

catfact commented 3 years ago

grid.refresh in lua calls down directly to monome_led_level_map, which in turn calls blocking I/O.

at 115kbaud, i calculate that it takes 2.36ms to send the 34 bytes required for each quad in the grid refresh. that's a lot of time to spend blocking the main lua thread; even if best practices are followed and grid refresh is on a dedicated timer.

it imight sound silly, but i'd consider creating a thread whose only job is to wait for dirty quads and call monome_led_level_map. the 1000's of CPU cycles burned in thread context switching will be more affordable than the millions of cycles spent waiting for I/O.

tehn commented 3 years ago

i did rudimentary benchmarking (bracket the led_level_map (at the c layer) with timestamps) which showed between 9 and 29 us (yes, us) per update. so i'm not sure the actual call is blocking on the uart transmission scale... i'm not sure how linux does things internally but maybe it's doing some sort of DMA (common in stm32 etc, right?)

tested with new ACM grid and also old FTDI grid. the old grid uses the FT245 which is actually a FIFO, so i have no idea if the baud rate has anything to do with the actual usb transmission speed.

but i may be wrong about every point here, and have no problem adding a grid refresh thread!

catfact commented 3 years ago

You know you're probably right. This is super easy to check...

catfact commented 3 years ago

If it's just blocking long enough for DMA then there is no point "optimizing"

(I saw a script where g.refresh was threaded into the screen redraw func and thought, you know, we better be sure of this )

catfact commented 3 years ago

derp right, posix has non-blocking socket I/O and we are using it

    if( (fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0 ) {
        perror("libmonome: could not open monome device");
        return 1;
    }

(under the hood the kernel maintains a FIFO per socket)

so.. the only thing i can see going wrong is requesting refreshes faster than the serial line. (we'd get an error in libmonome saying not enough bytes were written, which would be ignored by matron.)