Categories
Firefox Personal

Mum ♥ glow.mozilla.org

My mother has been staying with me and my family this past week. I told her about the Firefox 4 release, and showed her glow.mozilla.org.

Firefox 4 download counter

She was mesmerized:  “There’s one in Japan… oh, one in Sydney… South America’s doing well… several in Russia…”  I’ve pulled my laptop out to show her it several times, and given her download counts multiple times a day: “7.1 million in the first day… 10 million… 22 million… 35.5 million…”  As a result, I’ve gone on to explain to her a lot more about Firefox, its history, competition between browsers, and how I contribute to Firefox.

I give a big thank you and congratulations to the metrics team for presenting some potentially dry statistics in a beautiful, compelling and easy-to-understand manner!

Categories
Firefox Memory consumption MemShrink

MemShrink

Memory consumption is really important in a web browser.  Firefox has some room for improvement on that front, and so Jeff Muizelaar and I are working to start up an effort, called “MemShrink”, to reduce memory consumption in Firefox 5 (and beyond).

We’ve started a wiki page outlining some ideas on ways to improve our tracking of memory consumption.  Please read it and comment.

I’ve also opened bug 640452, which is a tracking bug for memory leaks in Firefox 5, and bug 640457, which is a tracking bug for other memory improvements in Firefox 5.  Please CC yourself if you’re interested.

Update: I just added bug 640791, which is a tracking bug for improvements to memory profiling.

Categories
Correctness Cplusplus Firefox

Limits of reliability

Julian Seward asked me an interesting question a while ago:  “what are the factors that limit Firefox’s reliability?”  (You can use “crash rate” as a reasonable definition of “reliability”.)

He suggested two things:

  1. Firefox depends on external code, such as plug-ins.
  2. Many crashes are hard to reproduce and so don’t get fixed.

For the first, Electrolysis (a.k.a. process separation) is on track to pretty much make it a non-problem.  It’s already in place for Flash, and will eventually be for other plug-ins.  So that’s good.

For the second, I see two main sub-factors.

  1. Firefox is implemented in C++ which is prone to memory-related bugs and data races, both of which can make crash reproduction difficult.  Using a safer language like Rust would make many (all?) of these bugs impossible.  Unfortunately, Rust isn’t production-ready, and rewriting even parts of the browser is a huge undertaking.  So we better get started ASAP 🙂
  2. Second, Firefox has some nasty low-level code like the garbage collector;  bugs in it be very difficult to reproduce.  I don’t see an obvious way to improve this other than the usual:  testing, code review, using simple algorithms, etc.
Categories
about:memory Firefox Massif Memory consumption

A vision for better memory profiling with about:memory

I’ve been doing lots of memory profiling of Firefox.  I’ve mostly used Massif and about:memory for this.  Both have their uses, but also big problems.  I have some ideas for combining their best features into a killer profiling tool.

Massif’s pros and cons

I’ve mostly used Massif.  It’s both invaluable and a total pain.  More specifically, it has the following advantages:

  • Time series.  It shows how memory consumption changes over time, both graphically (for total allocations) and with periodic detailed snapshots.
  • Level of detail.  Each detailed snapshot consists of an allocation tree that shows which parts of the code are responsible for every single byte allocated.

But it has lots of disadvantages.

  • Slow.  There’s somewhere between a 10x and 100x slowdown;  that’s a rough estimate, I haven’t actually measured it.
  • It’s implemented with Valgrind, so it doesn’t work on Windows.
  • It’s not easy to use.  The command-line needed to get good results with Firefox is huge.
  • There’s little control over when snapshots occur.  That could be improved relatively easily with client requests, though they require code modifications so they’re a bit painful.
  • The superblock allocation problem.  You can profile with Massif at the OS page level, or at the heap (malloc) level.  In both cases, the results can be misleading because an allocation request doesn’t always result in a visible allocation occurring.  For example, if profiling at the OS page level, most calls to malloc won’t result in pages being allocated with mmap, because when jemalloc needs pages from the OS it will usually request more than necessary for the current request, and then hand out pieces of those pages itself on subsequent calls to malloc.  If profiling at the heap level, this problem also occurs with custom allocators such as JSArenaPool that are layered on top of malloc.  As a result, many allocation requests don’t get recorded by Massif, and a small number of them are blamed for allocating much more memory than they actually did.  (The whole question of “what level do I measure at?” is one of the trickiest things about memory profiling.)

The result is that using Massif and understanding its output is difficult for anyone who isn’t an expert.

about:memory’s pros and cons

The other tool I sometimes use is about:memory.  It has the following advantages:

  • Lightweight, trivial to view.
  • Full control over when measurements occur.

And the disadvantages:

  • Minimal data, mostly just the heap.
  • The values are not as easy to understand as they seem.
  • No time series.

A better about:memory

I want the best of both worlds:  time series, detailed measurements, control when measurements occur, ease-of-use.  Plus a bit more: both OS page and heap level measurements, accurate numbers, global and per-compartment measurements, and good visualizations of both snapshots and time series data.  Basically, I want to re-implement a decent chunk of Massif within about:memory.  What follows is a 4am braindump, apologies for the density, hopefully it’s mostly understandable.

Merge about:memory and Shaver’s nascent about:compartments, because they’re really doing the same thing.

More global measurements:

  • Add current and peak total memory and RSS (resident set size), as measured by the system (eg. /proc/pid/status on Linux).
  • Keep malloc stats as they currently are (eg. malloc/allocated).
  • More individual counts.  Eg. for JS we currently have js/gc-heap, js/string-data, js/mjit-code.  I want to split js/mjit-code into several counts: JaegerMonkey code (inline, out-of-line, ICs) and JaegerMonkey data (JITScripts and IC data).  And also add counts for: TraceMonkey code and data, Shapes, JSFunctions, JSScripts, cx->tempAlloc (which holds JSParseNodes plus other things), JSFunctions, js::Vector, js::HashTable… basically anything that shows up reasonably high in Massif profiles.  We currently have 19 individual counts, I imagine having 50+.  Obviously there’ll be lots of non-JS counters added as well.
  • Allow individual counts to be shown in a standard order, or in order from biggest to smallest.
  • For a lot of these, both the number of bytes allocated and the number of allocations might be useful.

Per-compartment measurements: show all the individual counts, but on a per-compartment basis.

Clearer meanings of measurements:

  • Add links/tooltips/whatever to every measurement with a brief description so that it’s clear what it means.
  • For individual counts, clearly distinguish heap allocations from non-heap allocations (eg. executable code space allocated by the JITs).
  • Have “everything else” measurements for both the heap and the total, found by subtracting individual counts from the overall and heap totals.

Visualization of measurements.  E.g.:

  • Show the heap as proportion of total memory.
  • Show each individual global count as a proportion of the total.
  • Show each compartment’s individual count sum as a proportion of the global individual count sum.
  • All this via pie charts or similar.

Time series:

  • Allow about:memory snapshots to be dumped to file in some manner, probably as JSON.
  • Do it in a way that allows multiple snapshots to be processed easily by an external tool.
  • Basically, Sayre’s membuster on steroids.
  • Furthermore, make that data available to the browser itself as well.

Visualization of time series data:

  • Use the time series data to show a graph in about:memory.
  • Make the graph interactive, eg. allow drilling down to individual counts, going back to previous snapshots.
  • I have a hacky prototype canvas implementation of something like this that reads Massif’s output files.  SVG would probably be better, though.

Diffs of some kind between snapshots would be great.  It would allow you to answer questions like “how much memory is allocated when I open a new tab?”

If telemetry is ever implemented, sending this data back from users would be great, though that’s a lot harder.

Potential difficulties:

  • Not that many, as far as I can tell.  A lot of the infrastructure is already in place.
  • Keeping the counter code up-to-date may be tricky.  If it’s used frequently by many people, that’ll increase the likelihood that it’ll be kept up to date.  Better descriptions will help make it clearer if the counters are counting what they’re supposed to.
  • about:memory will itself use some memory.  It’s unclear how to avoid measuring that.  Maybe putting the more advanced features like the graphical stuff in a separate about:morememory page might mitigate this;  you could take a bunch of snapshots via about:memory and then open up about:morememory.
  • Performance… will more counters be noticeable?  Hopefully not since we already have a bunch of them anyway.
  • Unsophisticated users might file unhelpful “hey, Firefox is using too much memory”.  But sophisticated users might file helpful “hey, Firefox is using too much memory” bugs.

Basically, if this can be made as attractive and useful in reality as it currently is in my imagination, I figure no-one will ever need to use an external memory profiler for Firefox again.

Categories
Cplusplus Firefox

The dangers of -fno-exceptions

When Firefox is built with GCC, the -fno-exceptions option is used, which means that exception-handling is disabled.  I’ve been told that this is because the performance of code that uses exceptions is unacceptable.

Sounds simple, until you realize that libraries such as libstdc++.so are not built with this option.  This means, for example, that the vanilla operator new will throw an exception if it fails, because it’s in libstdc++.so, but Firefox code cannot catch the exception, because -fno-exceptions is specified.  (If you write a try-block, GCC will give you an error.)

This has important consequences:  if you compile your application with -fno-exceptions, you cannot use any standard library functions that might throw exceptionsSpiderMonkey’s C++ coding standard is succinct, perhaps overly so: “No exceptions, so std is hard to use.”

Another fine example of the “so you think you’ll be able to use a subset of C++, eh?” fallacy.  See bug 624878 for a specific manifestation of this problem.  I wonder if there are others case like that in Firefox.

Categories
Correctness Firefox

My best patch of 2010

I was going to write one of those “everything I did last year” posts, but now I don’t feel like it.  Here’s a “one thing I did last year” post instead.

The most important patch I landed in 2010 was probably the one that added LIR type-checking to Nanojit (which was based on an earlier patch from Julian Seward).  At the time of writing, it’s caught at least 14 type errors, most of which could have caused a crash or security problem.

Consistency checks within compilers are wonderful things.

Categories
Firefox JägerMonkey Massif Memory consumption Valgrind

Memory profiling Firefox with Massif, part 2

To follow up from this post: we’ve made some good progress on reducing JaegerMonkey’s memory consumption in Firefox 4, though there’s still a way to go.  Julian Seward will blog about this shortly.  In the meantime, I thought I’d share a particularly useful Massif invocation that Rob Sayre inspired me to concoct:

  valgrind \
  --smc-check=all --trace-children=yes \
  --tool=massif \
  --pages-as-heap=yes --detailed-freq=1000000 \
  --threshold=0.5 \
  --alloc-fn=mmap \
  --alloc-fn=syscall \
  --alloc-fn=pages_map \
  --alloc-fn=chunk_alloc \
  --alloc-fn=arena_run_alloc \
  --alloc-fn=arena_bin_malloc_hard \
  --alloc-fn=malloc \
  --alloc-fn=realloc \
  --alloc-fn='operator new(unsigned long)' \
  --alloc-fn=huge_malloc \
  --alloc-fn=posix_memalign \
  --alloc-fn=moz_xmalloc \
  --alloc-fn=JS_ArenaAllocate \
  --alloc-fn=PL_ArenaAllocate \
  --alloc-fn=NS_Alloc_P \
  --alloc-fn=NS_Realloc_P \
  --alloc-fn='XPConnectGCChunkAllocator::doAlloc()' \
  --alloc-fn='PickChunk(JSRuntime*)' \
  --alloc-fn='RefillFinalizableFreeList(JSContext*, unsigned int)' \
  --alloc-fn=sqlite3MemMalloc \
  --alloc-fn=mallocWithAlarm \
  --alloc-fn=sqlite3Malloc \
  <insert-firefox-command-here>

Good grief!  What a mess.  Don’t blame Massif for this, though;  it’s because Firefox has so many custom memory allocators.

With that invocation, the output of ms_print becomes something that is comprehensible to people other than Massif’s author 🙂  Here’s an extraction of the output which gives a high-level view of Firefox’s memory consumption on 64-bit Linux after loading 20 tabs, each with a random comic from http://www.cad-comic.com/cad/, which is a JavaScript-heavy site:

31.04% (366,878,720B) _dl_map_object_from_fd (dl-load.c:1195)
15.73% (185,998,724B) in 3693 places, all below massif's threshold (00.00%)
15.62% (184,639,488B) pthread_create@@GLIBC_2.2.5 (allocatestack.c:483)
05.68% (67,112,960B) pa_shm_create_rw (in /usr/lib/libpulsecommon-0.9.21.so)
04.35% (51,372,032B) JSC::ExecutablePool::systemAlloc(unsigned long) (ExecutableAllocatorPosix.cpp:43)
03.30% (38,993,920B) js::InitJIT(js::TraceMonitor*) (jstracer.cpp:7644)
03.11% (36,741,120B) js::InitJIT(js::TraceMonitor*) (jstracer.cpp:7643)
02.87% (33,935,360B) js::PropertyTree::newShape(JSContext*, bool) (jspropertytree.cpp:97)
02.84% (33,554,432B) js_NewFunction(JSContext*, JSObject*, int (*)(JSContext*, unsigned int, js::Value*), unsigned int, unsigned int, JSObject*, JSAtom*) (jsgcinlines.h:127)
02.79% (32,923,648B) js::InitJIT(js::TraceMonitor*) (jstracer.cpp:7642)
01.99% (23,555,684B) js::mjit::Compiler::finishThisUp(js::mjit::JITScript**) (jsutil.h:213)
01.69% (19,934,784B) JSScript::NewScript(JSContext*, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned short, unsigned short) (jsutil.h:209)
01.53% (18,067,456B) pcache1Alloc (sqlite3.c:33368)
01.48% (17,457,388B) nsStringBuffer::Alloc(unsigned long) (nsSubstring.cpp:206)
01.31% (15,478,784B) g_mapped_file_new (in /lib/libglib-2.0.so.0.2400.1)
00.89% (10,486,784B) JS_NewObject (jsgcinlines.h:127)
00.71% (8,388,608B) js::StackSpace::init() (jscntxt.cpp:164)
00.68% (8,093,696B) GCGraphBuilder::NoteScriptChild(unsigned int, void*) (mozalloc.h:229)
00.68% (8,024,064B) NewOrRecycledNode(JSTreeContext*) (jsparse.cpp:495)
00.67% (7,974,936B) js::Vector<unsigned short, 32ul, js::ContextAllocPolicy>::growStorageBy(unsigned long) (jsutil.h:217)
00.53% (6,291,456B) js_CloneRegExpObject(JSContext*, JSObject*, JSObject*) (jsgcinlines.h:127)
00.52% (6,190,836B) nsTArray_base<nsTArrayDefaultAllocator>::EnsureCapacity(unsigned int, unsigned int) (nsTArray.h:68)

The total is 1,182,094,880 bytes.

  • 31.04% is from _dl_map_object_from_fd.  This corresponds to code and data segments, mostly from libraries.
  • 15.73% is from allocation points small enough that they fell below the threshold (0.5%) that I used for this run.
  • 15.62% is from pthread_create, i.e. thread stacks.  Hopefully most of this space also won’t be mapped in.
  • 5.68% is from pa_shm_create_rwBug 617852 is open about this.  It won’t be fixed until after Firefox 4.0, but that’s not so bad because /proc/pid/smaps tells me that hardly any of it is mapped into physical memory.
  • That leaves 31.93% of big, heap-ish allocations.  It’s pretty obvious that for this workload, the JS engine is being greedy, accounting for 26.42% of that 31.83%.  One piece of good news is that the three js::InitJIT() entries, which together account for 9.2%, will be greatly improved by bug 623428;  I’m hoping to reduce them by a factor of 10 or more.

If anyone wants Massif’s full output, I’ll be happy to give it to them.  The full output contains full stack traces, which can be useful.

Some conclusions.

  • I’m still worred about our memory consumption, and I intend to keep pushing on it, both before Firefox 4.0 is released and afterwards.
  • Massif takes a bit of getting used to, particularly when you are profiling a huge, messy program like Firefox.  But it’s the only space profiler I know of that gives information that is detailed enough to be really useful in reducing memory consumption.  Without it, we wouldn’t have made much progress on reducing Firefox 4.0’s space consumption.  I’d love for other people to run it, it works on Linux and Mac (not Windows, unfortunately).  I’m happy to help anyone who wants to try it via IRC or email.  For all the improvements done lately, I’ve only looked at a single workload on a single machine!  There’s much more analysis to be done.
  • If anyone knows of other decent memory profilers that can handle programs as complex as Firefox, I’d love to hear about it.  In particular, note that if you only measure the heap (malloc et al) you’re only getting part of the story;  this is again because we have multiple allocators which bypass malloc and use mmap/VirtualAlloc directly.
  • I wonder if we need better memory benchmarks.  I’d like to have some that are as easy to run as, say, SunSpider.  Better telemetry would also be great.
Categories
Firefox JägerMonkey Performance Tracemonkey

Multi-faceted JavaScript speed improvements

Firefox 4.0 beta 7’s release announcement was accompanied by the following graphs that show great improvements in JavaScript speed:

Fx4b7 JavaScript speed-ups

Impressive!  The graphs claim speed-ups of 3x, 3x and 5x;  by my calculations the more precise numbers are 3.49x, 2.94x and 5.24x.

The Sunspider and V8bench results are no surprise to anyone who knows about JägerMonkey and has been following AWFY, but the excellent Kraken results really surprised me.  Why?

  • Sunspider and V8bench have been around for ages.  They are the benchmarks most commonly used (for better or worse) to gauge JavaScript performance and so they have been the major drivers of performance improvements.  To put it more bluntly, like all the other browser vendors, we tune for these benchmarks a lot. In contrast, Kraken was only released on September 14th, and so we’ve done very little tuning for it yet.
  • Unlike Sunspider and V8bench, Kraken contains a lot of computationally intensive code such as image and audio processing. These benchmarks are dominated by tight loops containing numerous array accesses.  As a result, they trace really well, and so even 4b7 spends most of its Kraken time (I’d estimate 90%+) in code generated by TraceMonkey, the trace JIT.

We can draw two happy conclusions from Kraken’s improvement.

  • Our speed-ups apply widely, not just to Sunspider and V8bench.
  • Our future performance eggs are not all in one basket: the JavaScript team has made and will continue to make great improvements to the non-JägerMonkey parts of the JavaScript engine.

Firefox 4.0 is going to be great release!

Categories
Firefox

Reasons not to worry (part 2)

About a month ago I wrote about the negative reaction Firefox often gets on sites like Slashdot, Reddit and Metafilter, and how I found this reaction dispiriting.

The post received lots of interesting comments.  Interestingly, there was a huge variety of reactions:  suggestions for improving Firefox, explanations for how Firefox had gone wrong, etc, but there was certainly nothing like a consensus of opinion.

This got me thinking about why people would use each of the main five browsers.  My overly short, tongue-in-cheek list looked like this:

  • Firefox: add-ons!
  • Chrome: speed! (and, for the moment, new shiny!)
  • IE: I’m a Windows user and I don’t know how to change my browser.
  • Safari: I’m a Mac user and I don’t know how to change my browser.
  • Opera: Hey, look how quirky I am!

More seriously, on a technical level the five main browsers are converging.  When one of them implements a new compelling feature, the others will get something similar eventually.  Firefox introduced the awesome bar, and now all the browsers track history in a sophisticated way in the address bar.  Chrome pushed the envelope on JS speed, but once Firefox 4.0 and IE9 are released the gap will have mostly closed.  And so on.

(An aside:  Firefox 1.5 and 2.0 had bad memory behaviour, ie. lots of leaks.  That was mostly fixed by Firefox 3.0, but the reputation has stuck, primarily through the word “bloat”.  But “bloat” has various meanings, so any time a new feature is added to Firefox that someone thinks isn’t useful, they’ll cry “bloat!” even though that feature may not affect memory footprint at all.  Cue Twain’s quote: “Give a man a reputation as an early riser, and he can sleep until noon.”)

So if you assume technical convergence (which isn’t entirely true, but it’s not so far off) then Firefox really is special, because it’s the only browser made by a non-profit organisation whose desire to create good software isn’t sullied by commercial interests.  As a single example, consider Firefox Sync, which allows you to synchronize browser history, passwords, etc., between different machines.  The Sync protocol is encrypted, so Mozilla can’t read it.  Furthermore, if you don’t believe that, you can run your own Sync server.  I don’t see Google implementing that in Chrome.  (Indeed, although I have Chrome installed on my laptop I’ve barely used it because I’m uncomfortable wondering exactly what information it’s sending back to Google HQ.  I already have a gmail account, they’ve got enough on me already without knowing my browsing history, thanks very much.)

In other words:  It’s the mission, stupid!

I was reminded of this with my favourite comment on my earlier post, from Ryan:

The add-ons are nice, sure. But to me, Mozilla is about hard working, smart web-wonks undeterred by hairballs of code from netscape, miniscule market share vs. Microsoft or really, reality in general. That’s awesome – and worth celebrating.

In a similar vein, Phil Ringnalda on IRC pointed me at a blog post that ended with this quote:

I believe in keeping the web free and open. I believe in building a better Internet, and helping people take control. These ideas align with those of Mozilla, btw… and it’s one more reason I’m sticking with Firefox as my browser (and Mozilla) instead of abandoning it for Chrome or Safari, or another browser created by a for-profit company interested in controlling my browsing experience. Mozilla was there for us, they saved us from the big bad IE Monster, and helped keep the web open and free, and they’re still doing that.

Words to live by!

Categories
Firefox

Reasons not to worry

The higher-ups at Mozilla like to say that we should focus on building the best browser we can, and not to pay too much attention to our competitors.  This is probably wise when you consider that we are competing against Microsoft, Apple and Google.

But I can’t help reading comment threads relating to browsers on sites like Slashdot, Reddit and Metafilter.  Judging from these threads, few people love Firefox;  a few say things like “I can’t switch because I couldn’t live without Adblock Plus/NoScript/etc.”  Lots of people complain about Firefox being slow/bloated/a memory hog.  Lots of people praise Google Chrome, mostly for its speed.

I realize these communities aren’t reflective of web users overall, but they do represent something of a leading edge.  As do Linux users, which is why I am troubled by Ubuntu’s plan to use Chromium as their default browser.  Also, IE9 looks like it will be a high-quality modern browser;  the sleeping Microsoft giant has finally awoken.

It’s all rather depressing.  So, gentle reader, please tell me why my perception is wrong.  What am I overlooking?