I need to frequently add and remove elements to and from a large number of sorted lists.
The lists are bound in size, and relatively small -- around 100 elements or less, with each element being on the order of 32 bytes or thereabouts.
If inserting into a full list, the last element can be discarded.
I've done some simple profiling, and found that storing these lists in arrays and using memmove to move everything after the insertion point backwards works surprisingly well; better even than using a linked list of a fixed size and linking the tail into the insertion point. (Never underestimate the power of spatial locality, I guess.)
But I think I can do even better. I suspect that most of my operations will be near the top of the lists; that means I'm going to memmove the vast majority of the list every time I insert or remove. So what if I were to implement this as a sort of ring buffer, and if an operation is closer to the top than the bottom, I shift the topmost items backwards, with the old tail getting overwritten by the head? This should theoretically involve cheaper calls to memmove.
But I'm completely brain farting on implementing this elegantly. The list can now wrap around, with the head being at position k and the tail being at position (k-1)%n if the list is full. So there's the possibility of doing three operations (k is the head, m is the insertion point, n is the max list size).
- memmove elements k through n-1 back one
- memcpy element 0 to location n-1
- memmove elements 1 through m-1 back one
I don't know if that'll be faster than one bigger memmove, but we'll see.
Anyway, I just have a gut feeling that there's a very clever and clean way to implement this through modulo arithmetic and ternary operators. But I can't think of a way to do this without multiple nested "if" statements.
- If we're inserting or removing.
- If we're closer to the front or the back.
- If the list "wraps around" the end of the array.
- If the item being inserted is in the same segment as the head, or if it needs to go in the wrapped around portion.
- If the head is at element 0.
I'm sure that too much branching will doom any improvements I make with smaller memmoves. Is there a clean solution here that I just am not seeing?