On Tue, 2020-03-17 at 11:26 +0100, Clemens Ladisch wrote: > Andreas Steinmetz wrote: > > On Mon, 2020-03-16 at 13:03 +0100, Clemens Ladisch wrote: > > > Andreas Steinmetz wrote: > > > > the snd_usbmidi_transmit_byte helper had to be converted to > > > > return output notification to allow for the 'repeat' shortcut. > > > > > > Why not simply handle one MIDI byte per port in each > > > iteration? It > > > could be argued that single-byte MIDI commands are likely to be > > > real- > > > time messages and deserve to go first. > > > > Actually the patch does exactly this. As soon as the helper signals > > that a message is complete the next port is processed. The "repeat" > > loop is just necessary to get a complete class compliant message > > for > > transfer as the helper processes one byte at a time. > > Why is it necessary to immedately get a complete class-compliant > message? > Maybe that's a misunderstanding caused by me using 'message' instead of 'event'. We need a complete MIDI event (or up to 3 sysex bytes) to form the class compliant 4 byte USB event. That's what the 'repeat' shortcut is for. And after one such event is collected to the URB the code switches to the next port. > > The range optimization is there to prevent O(2) performance cost if > > possible. > > Do you mean O(n^2)? For what n? > What I do mean is that in case of only one port being active we would do 16 full outer iterations over all ports to collect 1 event. And we need to do this for all events of that port. Even if we use the hint from port->active as a shortcut its always the full set of iterations. Memorizing the first and the last port of the previous loop and then restricting to this set makes a full 16 port run in the first iteration and then only the active port for all subsequent iterations. And yes, in the worst case of a 16x16 interface with ports 1 and 16 being active we're back to processing 16 ports for every iteration - I do, however, see this scenario as being quite theoretical in practice. > > > My original idea for that FIXME was to use round robin until the > > > packet > > > is filled (or all ports are empty), and to store the next port > > > index > > > (where to start for the next packet) in the endpoint. This would > > > be > > > able to distribute the balancing over multiple packets. > > > > The problem with "round robin until the packet is filled" is that > > in > > case of a large wMaxPacketSize there's then a huge > > latency. > > This mechanism does not require using the original packet size. > The point is that we should not restart with the first port in each > packet. I was thinking about that but in practice I do not know any class compliant interface that does have a number of outputs that is not equal to 2^n, i.e. 1, 2, 4, 8, 16. Then wMaxPacketSize is also always a 2^m value (16, 64, 512). With m>=n+2 (I don't believe that there is any class compliant interface for which this is not true) we can always start at the first port while doing fair scheduling (when a new URB is filled all ports get an equal change if they do have events pending). Implementing a restart based on the last packet scheduled for the very rare possibility of the above being not true just adds unnecessary complexity. > > > Regards, > Clemens Regards -- Andreas Steinmetz