Hi,

TL;DR: I wrote a crude yet useful JS memory profiler. I told jimb
about it at the work week and am posting the details here in case it's
of interest to anyone.

Earlier this year I made some large memory usage improvements to
pdf.js 
(https://blog.mozilla.org/nnethercote/2014/06/16/an-even-slimmer-pdf-js/).

I started by looking at about:memory, which told me that pdf.js was
using lots of typed arrays. So I manually instrumented every place in
pdf.js where a typed array was allocated -- a couple of hundred
places, few enough to be doable -- with a dump() that printed the
allocation size and identified the allocation site. I then used a
post-processing script to collate and sort these. This was enough to
identify and fix quite a few problems.

But maintaining those dump() calls every time I updated the pdf.js
code was tedious, so I switched to adding fprintf() calls in
SpiderMonkey in all the places where we allocated significant things.

Here's some example output from a pdf.js run:

241288386 counts:
(  1) 63183232 (26.2%, 26.2%): canvas                 934 x 1208
[http://localhost:8888/src/display/canvas.js:719:8] (4513088)
(  2) 15795808 ( 6.5%, 32.7%): canvas                 467 x  604
[http://localhost:8888/web/thumbnail_view.js:234:0] (1128272)
(  3)  3604480 ( 1.5%, 34.2%): buffer-new            ->    32768
[http://localhost:8888/src/core/stream.js:160:20]  (  32768)
(  4)  3444672 ( 1.4%, 35.7%): gc-new     tenured bg-object4
[<internal>]                                       (     64)
(  5)  3338000 ( 1.4%, 37.0%): gc-new     tenured shape
[<internal>]                                       (     40)
(  6)  2945664 ( 1.2%, 38.3%): gc-new     tenured script
[<internal>]                                       (    192)
(  7)  2800512 ( 1.2%, 39.4%): gc-new     nursery bg-object8
[http://localhost:8888/src/core/evaluator.js:2212:14] (     96)
(  8)  2654856 ( 1.1%, 40.5%): elems-grow       2048 ->     4097
[http://localhost:8888/src/core/function.js:53:8]  (  32776)
(  9)  2097152 ( 0.9%, 41.4%): buffer-new            ->  1048576
[http://localhost:8888/src/core/stream.js:160:20]  (1048576)
( 10)  1917048 ( 0.8%, 42.2%): gc-new     tenured string
[<internal>]                                       (     24)

Things to note:

- It only records allocations, not frees, so it shows cumulative
allocations rather than live allocations. This is still very useful
because (a) it captures short-lived allocations, (b) you don't need to
worry about choosing the right time to measure.

- All the recorded allocations combined totalled 241,288,386 bytes.

- Each line indicates what kind of allocation it is, where it
occurred, the cumulative size of all allocations matching that, and
the percentage of all allocations that this represents.

- It measures everything of interest, including objects, slots,
elements, strings, string chars, and shapes. I also ended up including
canvases, because they are significant for pdf.js. You can see them
here.

- The <internal> ones are mostly due to structured cloning, because
pdf.js passes lots of data from a worker to the main thread. In
practice that was typically 10--30% of the total.

- It's effectively using a stack trace depth of 1. 90% of the time
this is good enough.

- Ion-compiled code can allocate GC things. I didn't have a good
solution for handling this, so I just force-disabled Ion compilation.

Here's output from the same run, but with the allocation kinds
filtered out. So if you have multiple allocations from the same line
they get combined. Also, all the <internal> ones get combined into a
single line here.

241288386 counts:
(  1) 63183232 (26.2%, 26.2%):
[http://localhost:8888/src/display/canvas.js:719:8]
(  2) 36305454 (15.0%, 41.2%): [<internal>]
(  3) 15795808 ( 6.5%, 47.8%):
[http://localhost:8888/web/thumbnail_view.js:234:0]
(  4) 11519448 ( 4.8%, 52.6%): [http://localhost:8888/src/core/stream.js:160:20]
(  5)  7414440 ( 3.1%, 55.6%): [http://localhost:8888/src/core/fonts.js:6789:22]
(  6)  5303880 ( 2.2%, 57.8%): [http://localhost:8888/src/core/function.js:53:8]
(  7)  4774136 ( 2.0%, 59.8%):
[http://localhost:8888/src/display/canvas.js:172:8]
(  8)  4415896 ( 1.8%, 61.6%): [http://localhost:8888/src/core/stream.js:471:16]
(  9)  3890869 ( 1.6%, 63.2%): [http://localhost:8888/src/shared/util.js:1570:6]
( 10)  3858452 ( 1.6%, 64.8%): [http://localhost:8888/src/core/image.js:590:23]

Anyway, I'm writing about this because I thought some people might
find it interesting. Although it's crude, it's effective -- almost all
my pdf.js improvements were found with it.

I've made two files available. First is the patch, which applies
against revision 51419e405e61 from August:
njn.valgrind.org/memprof.patch. Second is the post-processing script:
njn.valgrind.org/memprof.py. The script should be used with the -w
option and possibly the -f option, which filters out the allocation
kinds.

Nick
_______________________________________________
dev-tech-js-engine-internals mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals

Reply via email to