Performance doesn't just include CPU time, but also memory footprint. You might eat a similar amount of processor time, but you're not going to have a similar memory footprint with any kind of GC'd language, and if the GC happens to be a stop the world GC, you're likely to see pauses in the browser that will irritate the user.
Before the inevitable "memory is cheap" argument is made, I'd rather have my client app be a good neighbor. Having a GC'd process on a client munch through memory like it's infinite is just bad manners and makes some bad assumptions. (On the flip side, a GC'd process that tries to be a good neighbor by freeing memory aggressively becomes a bad neighbor in terms of CPU time, which has additional consequences in terms of battery life.) The JVM, for example, tends to only run its GC when it needs to. It is a horrible neighbor in this respect because it just takes all the memory it needs and only runs its GC when it really, really has to. This is one of the reasons it's relatively fast, but as a consequence puts a lot of pressure on the system. In such a scenario it's possible other processes will OOM and be killed, or the system will start swapping memory pages, or other processes will have to run their GC more aggressively (as V8 does in low memory conditions) which puts even more pressure on the system. All this adds up to very selfish apps that bog down a system and can become virtually unusable. These selfish apps are also unsuitable for use in poorer parts of the world where incomes are lower and additional memory is potentially unaffordable.
I think it's probably okay to take the attitude that memory is cheap when you control the environment and you're paying for the memory, but in any other circumstance, shipping software that gluttonously gobbles memory is probably unacceptable and the #1 reason why C++ makes good sense as a language choice for writing a browser.
I found this whole discussion irritating because not a single person mentioned that with the memory demands of a web browser that you might not want GC. You can say all this BS about momentum but in my mind avoiding GC is probably the best and most relevant reason in 2013. Thank you for mentioning it.
Shows how dependent on GC a large part of the community of developers have become - they are incapable of imagining a world without it.
(PS: I am amused when people mention that being 20-50% slower is described as fixing the performance gap.)
I concur - with a lot of browsers lately running on severely memory constrained systems like smartphones/tablets and lower end laptops ... currently I have 3 tabs in chrome using 300+ MB each and about 40 using 50-60 MB.
So memory right now is important. And browsers are using way too much already.
Just as an aside, the reason Chrome is using so much memory is that each tab runs in its own process. This affords some security and stability benefits (one process dies, the rest are fine), but it obviously comes with a major price. I see this as a major architectural flaw in Chrome.
I think the processes are not the main culprit. We are using way more massive DOMs and a lot of js these days. And there hasn't been solid memory optimization efforts for js VMsas far as I know. The focus was purely on speed the last few years.
Chrome's codebase also got more complex so now every new process consumes a lot more memory. It's a way of achieving concurrency, but it's not necessarily the best way in terms of memory footprint.
Not really. Nothing mature. I mean Mozilla is working on Rust for exactly this reason. But the immaturity of that is why we don't have a Rust based browser at the moment.
Otherwise, C++ is really our only choice for a mature, modern language without GC.
There's C. There's Ada. There's Object Pascal. There's Objective-C. If you're a GNOMEhead, Vala can be pressed into service.
I seriously think that anyone who finds themselves reaching for C++ to solve a task should try Ada instead. The semantics of their code will be clearer and their code will be more readable, therefore maintainable.
I didn't downvote you, but, oh holy crap, Ada! Free documentation is non-existent, performance is lacking compared to C++ [-1] (yeah, yeah, benchmarks, etc), the community is minuscule and pretty much limited to stuff like critical real time systems (read: avionics), and the price of the books (the complete, current ones) is a barrier to entry.[0] From what I've read of others' experiences, there aren't many happy Ada developers. This isn't exactly conducive to a successful FOSS project! (On a side note, I do mostly like the language's syntax and its influence on Ruby is very obvious.)
I'm not sure anybody would use Objective-C if it weren't for Apple, any more than people would choose to use JavaScript if it weren't for it being the only language available in the browser.
It's a bit of a chicken and egg problem, but the one thing you really need for a successful OSS project are developers. Do any of the Object Pascal implementations actually have a community?
A codebase as large as a browser written in C would be a horrible, unmaintainable mess IHMO.
The Ada community is actually growing. Ada has a bad rap because back in the day Ada was a DoD requirement, which meant that vendors could ship compilers that conformed to the spec, but otherwise sucked rocks. With the advent of GNAT it's no longer the case that your compiler is bound to suck. I learned Ada primarily with the help of free resources. The ARM is also free.
Granted, the sort of person who is likely to use Ada is not a l33t hax0r, but someone who's been around the block a few times on major engineering projects. Still, GNAT has bindings to the likes of Gtk and OpenGL, a direct result of increasing interest in Ada outside of critical systems engineering.
It's a bit of a chicken and egg problem, but the one thing you really need for a successful OSS project are developers. Do any of the Object Pascal implementations actually have a community?
FreePascal/Lazarus does. It's mainly Europeans (French and Germans primarily) who once did PC development with Turbo Pascal.
I'm not sure anybody would use Objective-C if it weren't for Apple, any more than people would choose to use JavaScript if it weren't for it being the only language available in the browser.
I would (and do). Its Smalltalk-based object system is much easier to wrangle than C++'s.
A codebase as large as a browser written in C would be a horrible, unmaintainable mess IHMO.
Yes, point against C. C++ has the advantage that it can turn even small codebases into horrible, unmaintainable messes. :) This is the reason why I converted an entire game engine to Objective-C.
Then I agree in that Rust is a wonderful language with a lot of potential. But I'm not sure it's yet a practical language to develop a large piece of software in.
Objective C is close. in that it's a superset of C. But it has problems in portability, and it's insistence for dynamism make it not obviously well suited for the same problems. At least not more than just working in C.
Probably referring to reference counting, but that's not exactly a new idea. Still makes for a performance hit using reference counting all over the place.
If using something like shared_ptr in C++0x, it does come with some drawbacks (due to not having a strict ownership) as mentioned by Bjarne Stroustrup[1]:
Please don't thoughtlessly replace pointers with shared_ptrs in an attempt to prevent memory leaks; shared_ptrs are not a panacea nor are they without costs:
-a circular linked structure of shared_ptrs will cause a memory leak (you'll need some logical complication to break the circle, e.g. using a weak_ptr)
-"shared ownership objects" tend to stay "live" for longer than scoped objects (thus causing higher average resource usage)
-shared pointers in a multi-threaded environment can be expensive (because of the need to avoid data races on the use count)
-a destructor for a shared object does not execute at a predictable time, and the algorithms/logic for the update of any shared object is easier to get wrong than for an object that's not shared
There is no a priori reason to think that it isn't horrible in terms of CPU time. Anything that has to do more work just to manage memory is going to consume cycles, and usually a lot of them. In simple utility programs it might not be observable but in larger applications with more objects, or on slower hardware (phones, for example) the cost is palpable.
> We find that an existing modern implementation of reference
counting has an average 30% overhead compared to tracing, and
that in combination, our optimizations are able to completely eliminate that overhead. This brings the performance of reference counting on par with that of a well tuned mark-sweep collector. [-3]
Even the best tuned mark and sweep GCs are a drag on performance.
> Reference counting is expensive - every time you manipulate pointers to an object you need to update and check the reference count. Pointer manipulation is frequent, so this slows your program and bloats the code size of compiled code. [-2]
> "Unfortunately, reference counting is expensive in both time and space". [-1]
Also, the slide entitled "Why reference counting is slow" in some recent lecture notes go into some detail. [0]
you're not going to have a similar memory footprint with any kind of GC'd language
Cost of GC doesn't bother me.
Java's basic overhead for all data structures does.
Class String is a mistake. Better to have string scalars compiled down to char[], avoiding the class wrapper.
Massive overuse of HashMaps by most frameworks is huge drag too. Using typed collections (vs generics) like Trove helps quite a bit.
Our study group had a really good session on optimizing memory usage. Sorry, I can't quickly find the slidedeck we used. I used jamm to profile various strategies. Super fun.
I recently saw something in the newsfeed about JVM and optimal memory usage. Bit packing, word boundaries, ordering, etc. (It's more low level than I typically work, so I didn't bookmark it, sorry.)
I generally work on the JVM and I do enjoy it, but if you're looking for rock-solid 60fps-all-day-every-day, the cost of GC without question should bother you a great deal.
I would never implement a browser in a managed language and I would do everything in my power to avoid gating rendering on the completion of execution of its scripting system for similar reasons.
JVM != GC, although it is one of the best implementations out there.
Rock-solid FPS doesn't preclude GC. What it requires is a separate rendering layer that isn't subject to any GC pauses (or a game-like rendering loop that's always on time). The managed code manipulates the declarative level of the animations, and the low level handles the frame-by-frame details.
On relatively small heap sizes (under a GB) GC on recent PC hardware usually fits into the equivalent of a single frame -- under 30 milliseconds. Concurrent collectors would rarely need anything more than that, and wouldn't stop the world in any case.
The question here is, can you build a stable high performance rendering system on the JVM? If not, what would you have to change to be able to do that?
Correctly constructed you wouldn't have to change much. And JMonkeyEngine shows you can do a lot with what's there right now. There are some excellent discussions on its site on the topic of GC.
> Rock-solid FPS doesn't preclude GC. What it requires is a separate rendering layer that isn't subject to any GC pauses (or a game-like rendering loop that's always on time). The managed code manipulates the declarative level of the animations, and the low level handles the frame-by-frame details.
I don't think this is true. It's considerably more than rendering--it's reactivity. You need to handle audio streaming without pause, be able to process and dispatch input events seamlessly as well (I don't even know how you'd do this), so on and so forth. You'll essentially need to pull everything except actor logic out of the Java layer. Even some of that will be latency-constrained.
If anything, I think Unity is on the right path here: embed a sufficiently optimized virtual machine inside an engine that does all the hard work. But Unity still suffers from serious perf issues with nontrivial scenes. Like I said in another reply to you, object pooling is still heavily used in Unity because of the costs of object creation and destruction.
> On relatively small heap sizes (under a GB) GC on recent PC hardware usually fits into the equivalent of a single frame -- under 30 milliseconds. Concurrent collectors would rarely need anything more than that, and wouldn't stop the world in any case.
Sure, but that's too slow. The human eye can easily, easily, detect a dropped frame. Concurrent collectors (you mean Azul and friends?) are a possibility but we'd need a portable one that's freely licensed. If you've got one in your back pocket...gimme. =)
> The question here is, can you build a stable high performance rendering system on the JVM? If not, what would you have to change to be able to do that?
This is a tricky question. My instant reaction--"you can't"--feels right for the current state of the JVM. I do think something like Azul or another fully concurrent collector helps. But even then the JVM just has its limitations. Like, you're going to blow up your cache iterating a list of whatever your "GLbuffer" analogues are, unless you decompose all your objects. In most games way more of your code ends up being engine code than game logic code and so you're kind of hosing the main reason to use the JVM: a mostly-clean object-based system.
At that point, I'd just write C++ and embed a scripting language. (Recurring theme!)
> And JMonkeyEngine shows you can do a lot with what's there right now.
JMonkeyEngine is actually a major reason why I'm not using the JVM--if they can't get it good enough, I sure won't, right? IMO, you don't get enough from using the JVM (unless that's all you know, which'd just be sort of a shame) to make up for its limited platform reach and its perf issues. What you can do with it, right now, relative to just sucking it up and writing some C++ (and I do mean "sucking it up", I hate writing C++), just isn't enough.
No contest on your position regarding high fps and using a GC language like Java. I definitely don't have your experiences.
I equate VRML browsers with HTML browsers. General purpose, casual use, low cost of content production, etc. Just like with an HTML browser, even if you can make a fast twitch game with VRML, you probably shouldn't, for so many reasons.
My project was a virtual world construction kit based on a modular toy system. Think LegoCAD. At the time, VRML was ideal.
If you said that HTML browsers should be that, I'd totally agree with you, but (unfortunately, IMO) they're not. They're app platforms now, and so performance becomes really critical. Like, stuttering animations make users mad at the browser, not at the (possibly stupid) code within it.
My last project was a now-defunct deformable-terrain world game[1], based on voxel fields and surface nets. We were running into problems with Lua's GC just on our actors, nothing else in the world. With the amount of data we were forced to push around, I can pretty confidently say that an all-managed environment would have killed us (because we tried it first in C# and it did).
I was probably a little too JVM-centric in my post.
What's a "stop the world GC"? I joke, of course, but I really haven't seen anything like that in a long, long time.
You're right about memory, but you're wrong when you propose C++ as a solution. C++ doesn't solve any of the problems you outlined. You can implement a solution to those problems in C++. A C++ program doesn't come with an inherent advantage in memory handling. Many of the same techniques (creating and managing pools of memory) can and are being done in applications (like Cassandra) on the JVM (off-heap memory).
What you're really deciding is whether you want to solve those problems in a VM, or solve them at the application level.
Garbage collectors give you two things -- a reliability backstop, and (when you take pointers away) increased security. The behavior of GC is something that can be tuned (often dynamically) for a particular environment.
Correct me if I'm wrong, but I'm under the impression the JVM has a stop the world GC and uses this by default (although it does have other GCs available). I'm also under the impression that V8 and the Erlang VM, as well as the stock VMs for Ruby and Python, etc at least stop execution during the mark phase! Go has a fairly basic GC and stops execution to do its housekeeping.
Yes, it's easier to write safe applications in a managed language, but it comes at a performance price due to the GC.
It's far easier to write an app in C++ that performs well. There's no overhead of a virtual machine (there goes the safety...) and manual memory management tends to also yield better results in terms of footprint and CPU time when done properly. C++ gives you the tools to implement a fast process with a small memory footprint; managed languages do not unless they're using a null GC! C++ also gives you the tools tools to write a sloppy heap of shit, but then again, Java affords such a facility too!!
Garbage collection simply isn't free. Maintaining the lie that a system has infinite memory comes with a price.
I'm not making the point that GC is evil, and I write code in managed languages frequently, but I am making the point that it's really only appropriate for situations in which you're paying for the hardware, and using a managed language makes good business sense.
I agree with you, but I do want to point out one thing: there exists a happy medium between unsafe, no-overhead manual memory management (C++) and safe, GC'd managed memory (Java). For example, unique pointers in Rust enforce compile-time memory safety, without imposing any runtime overhead whatsoever and without forcing the programmer to manually allocate and free. In other words, there does exist such a thing as safe, no-overhead, automatic memory management, if you're willing to accept the single-ownership restriction of linear types (which in practice doesn't seem very onerous at all).
> What's a "stop the world GC"? I joke, of course, but I really haven't seen anything like that in a long, long time.
Actually, many modern GCs are still "stop the world" including many generational GCs. the alternative generally has overhead elsewhere that hurts performance. GC has a lot of tradeoffs.
> A C++ program doesn't come with an inherent advantage in memory handling. Many of the same techniques (creating and managing pools of memory) can and are being done in applications (like Cassandra) on the JVM (off-heap memory).
Sure, but it sure is a lot easier in C++ since the entire infrastructure doesn't assume GC is operating. I don't even know a way to reliably allocate a temporary object in Java that won't trigger GC I could guess some things that would probably cause the optimizer to allocate it on the stack, but that precludes having the temporary lifetime outlive the stack. (It must however be said, I'm not really a Java programmer)
> Garbage collectors give you two things -- ... and (when you take pointers away) increased security.
As you just said, increased security is from taking pointers away, which is completely orthogonal to GC.
> As you just said, increased security is from taking pointers away, which is completely orthogonal to GC.
That's not what he said. Removing pointer arithmetic [1] is part of getting memory safety, but not the entire story. Avoiding dangling references is another part of memory safety, and GC is one way to accomplish that; it's not orthogonal.
[1] You cannot actually eliminate pointers entirely; you can disguise them, but if you want a dynamic heap and not just static memory, you need to be able to reference memory within the heap. When people talk about eliminating pointers, they generally mean unsafe C-style pointers.
> Avoiding dangling references is another part of memory safety, and GC is one way to accomplish that; it's not orthogonal.
It's the removal of the `free` and `delete` semantics that avoid dangling references. Not GC. While GC posits a good alternative to many of these problems. GC doesn't actually help memory safety at all. Unless you consider memory leaks to be a matter of "memory safety".
Avoiding premature free()s is precisely what garbage collection accomplishes in this context, because you will never have to call free() to avoid running out of memory.
Before the inevitable "memory is cheap" argument is made, I'd rather have my client app be a good neighbor. Having a GC'd process on a client munch through memory like it's infinite is just bad manners and makes some bad assumptions. (On the flip side, a GC'd process that tries to be a good neighbor by freeing memory aggressively becomes a bad neighbor in terms of CPU time, which has additional consequences in terms of battery life.) The JVM, for example, tends to only run its GC when it needs to. It is a horrible neighbor in this respect because it just takes all the memory it needs and only runs its GC when it really, really has to. This is one of the reasons it's relatively fast, but as a consequence puts a lot of pressure on the system. In such a scenario it's possible other processes will OOM and be killed, or the system will start swapping memory pages, or other processes will have to run their GC more aggressively (as V8 does in low memory conditions) which puts even more pressure on the system. All this adds up to very selfish apps that bog down a system and can become virtually unusable. These selfish apps are also unsuitable for use in poorer parts of the world where incomes are lower and additional memory is potentially unaffordable.
I think it's probably okay to take the attitude that memory is cheap when you control the environment and you're paying for the memory, but in any other circumstance, shipping software that gluttonously gobbles memory is probably unacceptable and the #1 reason why C++ makes good sense as a language choice for writing a browser.