8 ms·
Linus suggested in the OP that automated reference counting (where the language implementation handles reference counting for the programmer) is preferable to G
by ekiru 15y ago
Linus suggested in the OP that automated reference counting (where the language implementation handles reference counting for the programmer) is preferable to GC.
Python uses such reference counting (although it also has a GC fallback to ensure cyclic structures are collected).
- cdavid 15y agoAnd it is one of the reason why python is slow and difficult to scale on multiple cores (the main difficulty by far of removing the GIL is reference counting).
- kragen 15y agoScaling Python on multiple cores is easy: you just run multiple Python processes, each with its own per-core GIL. You are correct, though, that ref-counting overhead is one of the main reasons Python is slow.
- cdavid 15y agoYou are playing with words here: of course you can run multiple python instances to scale your application on multiple-cores, that's a trivial statement. But I was talking about python the interpreter (more exactly cpython). There are legitimate cases where multi-threading is the natural, elegant solution, and cpython, mostly because of reference counting, prevents that.
- kragen 15y agoCPython prevents you from using shared-state multithreading to scale your application on multiple cores, but it doesn't prevent you from scaling your application on multiple cores. That's not "playing with words". It is indeed unfortunate when the limitations of our platforms force us to contort our code to improve performance, but that is just as true of multithreading as of multi-process programming. The difference between the complexity of the two is small.
- gerner 15y agoThank you for making this point. To my great dismay I see more and more people think that you need concurrent threads in one process in order to scale. It seems as if many programmers believe this as scripture and subsequently have difficulty thinking about distributed systems (not just multi-core, but multi-host).
- SpikeGronim 15y agoThe drawback with multiple processes is that they each have all the compiled bytecode on their heap. When I "import nltk" my heap goes from ~12 to ~36 MB. Add a few more dependencies and you end up wasting a non-trivial amount of RAM on python heaps.
- lobster_johnson 15y agoYou could do the import and then fork the child processes. That should share all the memory used by the nitk module between all the children (for as long as the memory is not modified by anyone, which triggers a copy-on-write allocation of the affected pages).
- baq 15y agomost forks do copy-on-write and every access to a python object meddles with reference count, which is - you guessed it - a write.
- froydnj 15y agoThe context of the copy-on-write win was the bytecode for modules. I don't know that you'd have anybody meddling with the reference counts for that...but I haven't really looked.
- rayiner 15y agoBytecode is stored in function objects which IIRC are reference-counted.
- lobster_johnson 15y agoAside from ad-hoc classes (defined within the scope of a function, say), does it ever make sense to garbage-collect a module or a class? Say you do "import smtplib" in your main file. Now that module is imported -- forever. I don't know the internals of Python well enough, but I bet that the module reference has strong references to its contents, so that even if nobody is actually calling anything in smtplib, it will be there in case someone does. The same should be true about modules importing other modules; they stay visible at the scope-level, so they are permanently loaded. So for those cases it would make sense to keep them separate from global garbage collection. I'm pretty sure that the method tables of all the classes in the system take up considerable space, probably in the order of megabytes for many apps.
- chc 15y agoThis is a bit like saying, "Of course C has full type safety. Just use a Haskell implementation written in C." Both your statement and his are technically accurate, but yours is on a subtly different topic.
- kragen 15y agoHow is it a bit like that? He complained that Python didn't scale to lots of cores because of the GIL; I said his Python didn't scale because of threading, which interacts badly with the GIL. If you use a shared-nothing approach to parallelizing your Python code, you don't run into that problem. It's not as if shared-state threading happens magically — you have to rewrite your code to use it, too.
- ori_b 15y agoPython doesn't do code analysis to determine when it's effectively doing obj.refs++; obj.refs-- repeatedly. This sort of analysis is useful, and if the interpreter had been designed to do any optimization along with jitting, would probably come nearly for free. Reference counting could be far far cheaper than it is in python. (How much cheaper? I don't know - it'd need work to figure it out)
- sb 15y agoRecently, there has been some work on removing redundant reference count operations in the Python interpreter. The following paper describes how it can be done: http://portal.acm.org/citation.cfm?id=1869631.1869633 http://portal.acm.org/citation.cfm?id=1869631.1869633. Regarding the performance impact of reference counting, the following facts are important: - Switching from immediate reference counting to deferred reference counting (L.P. Deutsch and D.G. Bobrow, 1976 [1]) eliminates about 90pct of all reference count operations in Smalltalk (Berkeley Smalltalk '82, that is) [2] - A very good account of reference counting can be found in either Dave Ungar's excellent PhD thesis [3] and Dave Ungar and Dave Patterson's in-depth analysis of Smalltalk performance [4]. [1] An efficient, incremental, automatic garbage collector (http://www.cs.umass.edu/~emery/classes/cmpsci691s-fall2004/papers/p522-deutsch.pdf http://www.cs.umass.edu/~emery/classes/cmpsci691s-fall2004/p...) [2] High performance storage reclamation in an object-based memory system (http://techreports.lib.berkeley.edu/accessPages/CSD-84-167.html http://techreports.lib.berkeley.edu/accessPages/CSD-84-167.h...) [3] The Design and Evaluation of A High Performance Smalltalk System (http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/5376.html http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/5376.html) [4] Berkeley Smalltalk: Who knows where the time goes? (Chapter 11 of http://www.iam.unibe.ch/~ducasse/FreeBooks/BitsOfHistory/ http://www.iam.unibe.ch/~ducasse/FreeBooks/BitsOfHistory/)
- rayiner 15y agoThis sort of analysis does not come easily. Say you have: def bar(): return some_constructor() def foo(): b = bar() Here the decrement is in bar() and the increment is in foo(). You have no way to elide the operation without doing inter-procedural analysis, which is hard.
- lukesandberg 15y agois the ref counting slow because it has to be a protected operation? or is it just the amount of them. if it is due to being protected by locks there are lock free ref counting systems (as described in here: http://www.google.com/url?sa=t&source=web&cd=1&ved=0CBUQFjAA&url=http%3A%2F%2Fpdos.csail.mit.edu%2Fpapers%2Flinux%3Aosdi10.pdf&rct=j&q=linux%20sloppy%20counters&ei=ytmxTcGDFory0gGqtdmoBQ&usg=AFQjCNGJwB3MQHkpQlHxcF8Xa0O-AszUdQ&sig2=3EyUPLm71MY5ACsfAR3MHA&cad=rja http://www.google.com/url?sa=t&source=web&cd=1&v...) that essentially allow batch refcounting opterations to accumulate in a per thread counter that are only reconciled occasionally. assuming you design your data structures correctly (no false sharing) you can get very high write performance because all your counters are cached and there is no cache invalidation due to competing writes on other processors. of course it takes more memory so it is only suitable for highly shared objects, but it can make a significant difference in a reference counting system. (the article points to scalability issues in the linux ref counting system and how to resolve them)
- cdavid 15y agoThe problem with most methods to improve ref counting speed is that it generally breaks existing C extensions. For that reason alone, I don't expect cpython to significantly change its way of doing things in that area for a long time.
- kragen 15y agoThat's awesome! I knew there were faster approaches to ref-counting, but I didn't know about that one, "sloppy counters". The most common approach to speeding up ref-counting is to ref-count bigger objects — modules rather than individual variables, say. The simplest way to speed up ref-counting transparently is to statically analyze the code and remove redundant increment and decrement operations. This can be tricky in practice, and I haven't heard of anyone actually doing it.
- lukesandberg 15y agoIt seems to me that the best way to do this would be to put the analysis into a tracing JIT. since the compiler knows exactly what will happen in a long code sequence removing redundant incs/decs would be fairly trivial.
- jrockway 15y agoC and Java apps with high contention don't scale well onto multiple cores, either. The key to speed is to not share state, which Python can do fine. It's called fork.
- cdavid 15y agoSometimes you cannot easily not share state. The fact that you can use multiple processes to scale is not an interesting statement when comparing python to other languages because it is true for every language out there. I think the GIL has made people in the python community too defensive: the GIL does not prevent from building scalable architectures in many cases, but it still sucks, and it would be better without. That's a limitation (and a tradeoff because it made development and integration with C easier). And there are scalable architectures based on threads (example: http://www.mailinator.com/tymaPaulMultithreaded.pdf http://www.mailinator.com/tymaPaulMultithreaded.pdf) - "thread suck" has became a meme which slightly bothers me in general. Not a panacea, but a good solution when applicable.
- rtaycher 15y agoI thought the key was not managing mutable state. Sharing lots of immutable data can probably shave a lot of performance over ipc.