4 ms·
I'm going to try to write up an explanation of this -- mostly to get it straight in my own head, and partly in hopes that if I get pieces wrong someone more kno
by mcherm 13y ago
I'm going to try to write up an explanation of this -- mostly to get it straight in my own head, and partly in hopes that if I get pieces wrong someone more knowledgeable will write in to correct me. (Armin Rigo, do you read Hacker News?) Most of this is based on my reading of https://bitbucket.org/pypy/stmgc/raw/c7/c7/README.txt https://bitbucket.org/pypy/stmgc/raw/c7/c7/README.txt.
So this is all based on two core features at the C(/assembly ?) level. One is the ability to apply a per-thread offset to all memory access (using "%gs:", whatever that is); the other is a (Linux-only) way to move where a memory-mapped page of memory points (remap_file_pages()).
The trick is for all data structures to be allocated in shared-memory pages unique to each thread, then re-map them so they're overlapping. Now we have all threads sharing all heap memory structures (just as you would in a traditional threading model), and zero overhead to read or write to a data value.
In a traditional threading model, you would then put in code that checks for a lock before accessing (read OR write) any data field that might be shared (and ever modified). Threads would block until a field was free before acquiring a lock and reading from or writing to it. That's a price paid for each and every access -- and if conflicts between threads are rare it's a high price to pay.
In STM we instead allow threads to modify data in (short) "transactions", then "commit" these transactions at the end; rolling back one or the other when two threads conflict. In Armin's new approach here's how this gets done:
Reading shared data is fine... go ahead and read to your heart's content. When you need to WRITE to a field inside a transaction, you first clone the entire memory page (that's an expensive operation). Then you adjust the per-thread offset so it's pointing to your own memory page and you continue reading and writing freely -- the only overhead is keeping a list of fields modified and read during the transaction.
At the end of the transaction, you pay again. First, you check whether someone changed data you had read: if so then all the work of the transaction needs to be abandoned and you can start over. Then you copy the modified values back to the "shared" location (if another thread was using them then IT may later discover that it needs to start over).
The theory of STM is that when conflicts are rare there will be few rollbacks so you'll mostly just pay the cost of cloning memory, tracking reads and modifications, and copying the values back. This will (hopefully) be less overhead than having to check for a lock before reading or writing any value, so an STM approach will be better than a traditional threads-and-locks approach. The innovation here is using the memory-mapped page and per-thread offset to allow normal access to memory for reads/writes instead of the traditional approach to STM which requires adding extra indirection logic to each and every memory read or write.
So, smart people of Hacker News... how accurate is my summary?
- tel 13y agoOne thing to add is that while it's worth optimizing the expense of all of these operations, that's not the point. It may very well be that locked algorithms will always outperform STM. Instead, the advantage lies in STM providing a much nicer userland experience. It's downright trivial to write some hard concurrency programs using STM. You do have to worry about contention causing too many rollbacks, but by-and-large STM is a drop-in "make concurrency make sense" module. For many examples see Simon Marlow's Concurrency in Haskell book.
- jerf 13y agoAs I understand it, which may be wrong, this is a PyPy level innovation, used by the VM, and among the intentions is simply speeding up Python to work effectively with multiple threads and no GIL. It is unclear how to directly offer it to end-users without making what the end-users are using something fundamentally Not Python.
- tel 13y agoOh, well, if that's the case then I'll happily side with the op without reservation.
- arigo 13y agoYou are half correct and half wrong. We want to also speed up programs that handle largely independent events without using threads, which is a much bigger "market" in Python than allowing existing multithreaded programs to run faster. The idea is to create threads under the hood, and have each one run a complete event in a transaction -- basically, in standard Python terms, forcing the "GIL release" to not occur randomly. This means adding threads and then controlling the transaction boundaries with a new built-in function. This is a change that can be done in the core of the event system only (Twisted, etc.) without affecting the user programs at all.
- deleted 13y ago[deleted]
- 13y ago