The bigger problem with it is you can't just load/store a 128 bit value without this instruction. An SSE 128bit load/store is turned into two 64 bit operations under the hood. So to actually get atomic loads and stores you need to use this very expensive compare exchange operation. That really limits the number of algorithms it's useful for.
If you can do that, for most algorithms it doesn't matter much if you do two 64 bit loads to get the values. If you are out of sync your compare and swap is going to fail anyway, which is likely to be rare.
Maybe my experience is not representative, but it's something to be aware of when designing algorithms with it.
There are many better ways to do something like that. Anything that writes a buffer, then makes it available atomically would work much better, or you could just use the compare and swap.
There is always going to be a limit of what is atomic. At some point you have to work with a data structure meant for larger data. The cut off has to be somewhere.
Whatever else was part of your 128 bit struct could probably be part of the record instead. You could use a 64 bit address and a 64 bit version number. Looking up could be done by atomically reading the 64 bit version number only. Writing a new record would then require the compare and swap.
There are other ways of doing that though, I'm not sure the trade off was the best one in the end.
If you were worried about cache and indirection, the indices could be aligned to the start of a 64 byte or 128 byte boundary, since 64 bytes is the cache line size but (Intel) cpus will load two cache lines at a time. Because of this, loading in your 64 bit/8 byte index should mean that the 120 bytes after it are going to be cached.
Looks like I need to resort to ASM for visual c++. Might do.