Vladimir Vukićević — Words
 



Are You Kidding Me?

So it turns out, now that Alice got the awesome nochrome tinderboxes up and running, that the Mac, on raw HTML layout, is about as fast as Windows.  However, on win32 and linux, when you add in the perf hit of drawing the full browser UI (the nochrome boxes just render the HTML in a plain window with no widgetry), is on the order of 5%-10%.  On the Mac, it’s around 300%.  Clearly there is something very wrong going there.

After some sniffing around, using a very reduced testcase (that I was working with for another performance issue) I came across the Mac native theme code.  Disabling native theme entirely (commenting out all the calls) gave back all the performance.  So I start digging into it.  I collect a bunch of really weird data — such as, if I replace a call to HIThemeDrawTrack with a loop calling HIThemeDrawTrack 5, 10, 50 times… the overall time spent in the benchmark doesn’t change from 1 time.  At 100 times though, it changes drastically.  The delta between benchmark time and raw time spent in NSChildView’s drawRect should remain mostly constant with the above changes, one would think.  But it doesn’t.  Going from no native theme rendering to turning it on increases the “unknown” time significantly.

I’m still spending time with Shark (which is, I would argue, Apple’s best developer tool — maybe one of the best developer tools, ever), but the latest is completely baffling and what prompted this post.  I finally got around to doing a shark System Trace, which traces at the user/kernel boundary, recording syscalls, preemptions, etc.  In the syscall log, I noticed a bunch of calls to geteuid, getuid, etc., all being repeated.  Looking at the call stack…

… you guessed it.  HIThemeDrawTrack makes syscalls.  For every single call, it calls geteuid, geteuid, getuid, geteuid, stat64, stat64. That’s 4 geteuid/getuids and 2 stats.  For every single call to the theme function. This is ludicrous.  The stack traces indicate that this is because this function seems to pull out user preferences on each call, and then has to check if the preferences should change because the euid may have changed (?!?)… the getuid call is coming from _CFUserName.  Because, you know, drawing a UI element needs to know the user name.  It’s probably also allocating the CFString to hold it.

I wish this was a joke, but it’s not.  I’m now looking into how to draw this crap faster; the NSCell API may be a better way to go — we’ll see if it has this same damage.


11 Comments to “Are You Kidding Me?”  

  1. 1 Stuart Parmenter

    I can’t believe you are questioning something Steve Jobs designed…

  2. 2 Rob

    This is the classic bad news, good news story with Shark. The bad news is the bug or overhead you found, the good news is how fast you were able to characterize it.

  3. 3 Smokey Ardisson

    I can’t say the whole thing is sane, but calling into preferences to check for changes is “reasonable,” since the scrollbar should change style and behavior when the user changes either the “next page vs. jump-to-here” pref or the arrow-position (which native scrollbars do, but which our themed scrollbars do not do at all–bug 389775–or properly–bug 377439) and the theme and highlight/selection colors (bug 122000).

    That said, it seems like a saner API design would have been to make all of HITheme APIs listen for change notifications and only react then instead of calling to see what’s going on every single call :-P

  4. 4 Colin Barrett

    It gets worse! There *is no NSCell for NSScroller*. There’s a way to direct the rendering of NSScroller into an offscreen buffer (NSView’s displayRectIgnoringOpacity:inContext:), which I looked into, but NSScroller itself is a bit busted. Getting proper metrics out of it was just as bad as HIThemeDrawTrack, and of course broken in different ways.

    For a 300% performance hit and getting rid of ridiculous syscalls, it might be worth figuring NSScroller’s mysteries. It also might be worth filing a radar. Perhaps it’s actually a bug in Apple’s code, ha ha?

  5. 5 An interested party

    While this doesn’t fix your immediate issue with the call being slow, you should still file a bug with Apple on what you’re seeing.

    http://developer.apple.com/bugreporter/

  6. 6 Fred

    Wow, that’s a whole bunch of context switches it’s doing there. No wonder it’s so slow: IIRC, OSX isn’t exactly known for its high performance on that task.

  7. 7 JB

    Well looking in /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/HITheme.h

    * HIThemeDrawTrack()
    *
    * Summary:
    * Draw a themed track item.
    *
    * Discussion:
    * Used to draw any tracked element including sliders and scroll
    * bars.
    *
    * Mac OS X threading:
    * Not thread safe

    I think threads are “where it’s at” — why use old Carbon calls? Does Firefox not want to be Cocoa for any particular reason?

  8. 8 Xin Zhang

    So in OS X, under what condition, a process can change its EUID?

  9. 9 Boris

    JB, the Cocoa thing here is NSScroller. See Colin’s comments on why it’s not being used.

  10. 10 vladimir

    So, using NSScroller can be done, but there doesn’t seem to be any way to programatically set the state of the various buttons. NSScroller has the nice advantage of being able to render into transformed contexts, which HITheme can’t seem to do, but the buttons thing is a showstopper. Anyway, it also does the syscalls, so no real gain there.

    However, to be clear, the syscalls aren’t the performance issue here. They’re just ridiculously unnecessary.

  11. 11 Boris

    It looks like the “don’t block on flushes” thing that went in today reduced the chrome overhead a good bit….