'We are no longer as optimistic about removing the GIL completely. '
code.google.com
code.google.com
Is the JVM the answer to life, the universe and everything ?
Java suffers from JSR approval hell just because it tries to stadardize everything. This is the requirement to perform well in the enterprise world, along with the programmers certification mechanism and inclusion of the top industry players in the standards approval.
If your language is just a hackers' toy (Clojure and Scala are exactly that) then why do you care about JSRs in the first place? The JSRs don't mention JVM usually, only the libraries or Java language extensions.
That would be golden.
Hopefully this will get easier in Java 7 with the modularity work in Project Jigsaw: http://openjdk.java.net/projects/jigsaw/
But one motivation for using a JVM based language in the first place is that so many libraries are available on that platform. Unfortunately, many of them suffer from excessive complexity and some of that complexity is due to the JCP.
edit: sure downmod me. It's crazy talk! How could programmers do without threads and concurrency issues and all of the other blocking problems. Hardware should handle multiple cores. Not programmers.
Computers don't work that way. Unless you have many CPUs, nothing happens at the same time.
Rewrite your 'long running task' to do things bit by bit. By effectively doing your own timeslicing, you remove the need for any locking or concurrency issues. Once you get into the habbit of programming like this, you wouldn't believe how much easier things are.
FWIW this is how Mibbit backend works - thousands of connections handled in a single thread.
Javascript doesn't have threads (thankfully). There is no need for threads. They look like magic, but they cause more issues than they solve IMHO. The mapping of 'work' onto physical CPUs should be done silently by the hardware IMHO (If you have more than one CPU).
An alternate (better) model would be to simply have a single thread with a main loop, have async networking, and UI updates periodically in the same thread.
while(true) {
networking.check(); // Check if any sockets are ready for read/write/connect
ui.update(); // Update the UI a bit if needed
}
The only case this would be a terrible idea is if you don't have control of all the code, or need to interface to things that may block/crash/etc.Everyone doing something doesn't make them right. But when all major instances of an application are implemented differently than you think is best, perhaps you don't understand the problem as well as you think you do. Chrome, I think, is the best browser architecture, and it looks like IE and Firefox will adapt something similar. I think they use separate processes to manage tabs instead of threads, but it's still parallel.
That's a silly argument. The following can't block or crash.
while(true) {
jsRuntime.executeInstruction();
// Other stuff.
}Why would there be a performance hit in doing js instructions one by one though ;) A loop isn't expensive.
Obviously you wouldn't update the UI every time you execute a js instruction. That would be insane. I just put 1 js instruction in the loop to have the minimum unit, in case anything else needs to be updated very quickly at the same time - eg some animation etc
I'm really not sure how useful it is to cache-optimise a browser. You need to be able to fit a significant amount of logic into 32K in order to take advantage of cache. When I used to do realtime image processing (AR), I could get an order of magnitude speedup by just getting my logic to fit into 32K; image processing code can literally go from 3fps to 30fps just by tightening code to the point where it can fit in L1 cache. I don't know if it's possible to fit significant amounts of rendering logic or js logic into 32K, but if it is, then dedicating a core to each of those functions could give a significant speedup.
If you then use that data in the cache for a while, then the cost of the cache miss will be amortized. But what you're proposing is going back and forth quickly between the JavaScript VM and the rest of the browser code. The browser code will also need to bring its data into the cache, which will kick out the JavaScript VM's data.
Since you're proposing that the JavaScript VM should do a very small amount of work at each time, and it will likely need to bring all of its data back into the cache each time, you will see a lot of CPU stalls.
Timeslicing isn't hard. It's even simpler if you're the one writing the code.
If you are in the horrible position of having to interface with blocking/crashing code though, great shame :(
Not always true. There are multiple levels of parallelism in modern CPUs and cores is only one of these, eg instruction-level parallelism, hyperthreading etc
Anyway, theres more to concurrent programming than doing multiple things at once in parallel - theres also the use of threads to turn a synchronous blocking call into an asynchronous one, like you said.
If you reckon you can solve it, you should definitely be able to get rich from it.
I'll let the scientists who actually need CPU power figure that one out.
Most of the websites people here are working on could be run on a 386 and still have cycles to spare.
Writing everything in continuation-passing style is like writing your own filesystem or parser state machine. It's a little more efficient per core, it's complicated enough that the challenge of getting it right might be gratifying, but it's rarely the best use of our time when we're surrounded with ridiculously powerful hardware. Even netbooks have multiple cores now.
This is a different issue: multitasking of processes in the same CPU. This is an area that needs to be done by the OS, because you can't guarantee that other processes will be cooperative.
Inside your own process, however, you can do whatever you want by diving tasks in small chunks, without the need for threads.
The only place where I think we need threads is when dealing with libraries that we can't control. For example, UI libraries, networking libraries, math libraries, etc.
I have many CPUs.
Native threads do this timeslicing for you; that is why they were invented (so that programmers didnt have to mess around with crazy micro-management of that they were doing).
Can you imagine writing Chrome or FireFox using a single thread (it would be hard, and probably unusable)? :)
Threads serve an excellent purpose
Firefox is single threaded.
Chrome runs each tab in its own process- also not multithreading.
Incorrect. See http://mxr.mozilla.org/firefox/search?string=thread
But they don't solve the fundamental problem of how to organize your code to take advantage of this. It is really easy to create a threaded application that has lower performance than a single threaded application that divides work by chunks. And, after all research in this area, there is no clear way to use threads without spending a lot of time to make sure that they work.
Most processors in the future will be parallel architectures of some kind. Even my current laptop is a multicore.
Clearly, someone will find applications where this power is needed (graphics, simulation, robotics), but there is no way that MS Word will run in parallel in more than a few processors. The biggest change in multicore is in enabling new applications, not in changing the way current applications are developed.
For one, languages like Erlang, Haskell and Clojure already make concurrent programming reasonably approachable and second, if people were to switch to using proper dataflow languages* then parallelization is implicit and automagically done for the programmer.
* What I really want is a proper dataflow/imperative hybrid that lets me choose the right tool for the job...
You might as well not be running an OS and have Mibbit run straight on the hardware. All it's doing is handling network connections and some other data processing right? And don't look at me like I'm crazy, your OS is using valuable CPU time!
My understanding is that he/she wanted to have the hardware handle concurrency automatically. Unfortunately, I don't see any way to get around having software dealing with concurrency.
There are two reasons that the discussion goes beyond what you suggest, I think. One is detailed in http://www.tbray.org/ongoing/When/200x/2009/09/27/Concur-dot... where the wide finder project is a way to explore relatively easy ways to split a log-searching task into effective threads on a multi-core machine.
This is really a hard problem, as evidenced by Tim's long series of articles detailing various forays into clojure and other languages.
Your comment "Hardware should handle multiple cores" reflects the opposite of what I think chip manufacturers are thinking. They run into the performance barrier, so they build a chip with more CPUs on it and hand the problem off to the compiler team and the rest of the software world.
I would take it another step further in challenging hardware manufacturers to look at the broader problem. There was an article recently that noticed that for Lisp, the effective performance gain over a decade or two went up by 50 where for C-family programs it went up by several orders of magnitude. To me this implies that hardware isn't going in the direction that supports higher-level computing.
Remember when the 360 instruction set came out? The 7094 people looked at it with some sense of dissapointment. And where are the nice instruction sets as evidenced by the PDP-10 and family?
Perhaps this implies smarter cores so that we don't have to do so many of them.
But in today's world, it seems that the languages that work well with multiple threads have a language construct that is required to make it work--libraries don't do the trick. The clean channels of GO and the constructs in Clojure point the way. Maybe the GIL-fix approach is truly doomed.
So I agree with your closing sentiment.
One interpreter per-thread means all side-effects have to be migrated to the other threads to keep a consistent view of memory: guess what you will need to do that? yep, a global lock (except this time it's across all interpreters, instead of just one.)
For scientific computing purposes, you can often accomplish this with multiple processes and a numpy array allocated by shmget/shmat. But I'm not sure how to share complex objects in this way.
But I think that's how the Queue object in Python 2.6 works. The Queue instance is locked, you seem to be free to do whatever within the threads that are consuming the queue.
The reason I'm not sure is that having a single object being locked seems to contradict the GIL concept...
I like perl's ithread setup a lot. You explicitly need to mark data as shared between threads, otherwise all variables/objects are local to the thread. Things like queues, for example, are implemented as shared @arrays with a locking primitive around the manipulations, mostly hidden behind the Queue API (Queue->enqueue and Queue->dequeue).
The interpreter code is still shared among all the threads, but each has thread local storage separate from the shared arena. I've found that explicitly needing to mark data as shared to be a big boon to development, I think it has helped reduce the number of the bugs related to shared state.
I can't produce a significantly long start up time when perl copies a 240meg+ local state using http://gist.github.com/258016. Much larger, and the overhead pushes my 4gig machine into swap (so perl's not very efficient with its data overhead, it seems). It is slower than if the structure is shared (you can uncomment a line to compare) between the threads, which shows that perl is copying the state to the other interpreters, but this just says you need to use the same model with perl threads that you'd use when you do multiprocessing with fork if you're going to have long lived child processes that don't exec: spawn threads early before your heap balloons to avoid all that spawn-time copying (which is a good practice anyway, even when COW is used for fork). Every model has its trade offs, and it's good to know those trade offs going in, rather than treating the flavor of the day as a silver bullet.
And even if perl's implementation isn't stable/good, it doesn't mean the model of explicit sharing is a bad one. And it most definitely doesn't mean that using a separate interpreter in each thread with global sharing is bad at all.
vfork() and clone() can almost give you what you want, but Linux threading is using something different (NPTL) nowadays.
Even with NPTL, both processes and threads have task_structs.
I'd like it if someone could show me how the garbage collector is related to removing the GIL.
While I'm not familiar with the specifics of Python's GC, a mark&sweep phase is usually added to reference counting so that if there's garbage which contains references to itself but has no external references, it will eventually be collected. (_Garbage Collection_ by Richard Jones and Rafael Lins is an excellent resource on GC details, btw. There's also a decent overview in the O'Reilly OCaml book (http://caml.inria.fr/pub/docs/oreilly-book/html/book-ora082.... )). In other words, it plugs the worst memory leaks caused by reference counting.
How to do multiprocessor / multithread GC well is still an area of active research. In the mean time, one simpler solution is to have several independent VM states, each running in their own thread (or process), and communicating via message passing. Lua makes this easy, but its VM is considerably lighter than Python's.
(sorry if that's a dumb question)
1. Start at some well-known root object. Mark this object reachable.
2. For each object that is reachable
2a. For each object that this reachable object has a reference to
2b. Mark this object reachable if it's not already.
3. If any object was marked reachable in 2b, repeat step 2.4. Garbage collect every object not marked reachable.
To see an important difference between this kind of strategy ("mark and sweep") and reference counting, consider the classic example of two objects, each one of which has a link to the other, but are not visible to anything else in the system.
But with that said, you can get all the gory details from wikipedia: http://en.wikipedia.org/wiki/Garbage_collection_(computer_sc...
http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning...
I'm no GC expert, but these get you good, fast, and mostly concurrent GC (in very high-pressure situations where one is running out of memory, then I think both fall back to a serialized stop-the-world last-ditch-effort).
If you read the section on the concurrent collector on the link you posted you can see it says "During each major collection cycle, the concurrent collector will pause all the application threads for a brief period at the beginning of the collection and again toward the middle of the collection. The second pause tends to be the longer of the two pauses and multiple threads are used to do the collection work during that pause"
Finally, a point which is not often brought but is crucial in my opinion is about C extension: the GIL makes C extensions much easier to write. That's one big reason for python success in the first place.
I not sure dropping the GIL is the best solution. Something like Erlang message passing sounds like a better model. I'm not sure if that's a good fit either though since it works best with functional style programming.
Processes don't share pointers, they only share BLOBs. Threads do share pointers and that's why they are more suitable to in memory data analysis/manipulation.
(Not that your general point is wrong)
The way most web applications are structured, for instance.
I was under the impression that these Google code wiki pages were kept under source control so you should be able to view histories etc. but I can't see any obvious link.
Found it, the change to this section was done 33 hours ago, a diff can be found here:
http://code.google.com/p/unladen-swallow/source/detail?spec=...