From f8304a59f3b8d4f2620b626af03cdb23af88fded Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 15 Jul 2008 15:21:28 -0700 Subject: [PATCH] Add New Job Old Tricks post --- src/exa/i965/new_job_old_tricks.mdwn | 118 ++++++++++++++++++ .../new_job_old_tricks/vertex_buffers.png | Bin 0 -> 14350 bytes 2 files changed, 118 insertions(+) create mode 100644 src/exa/i965/new_job_old_tricks.mdwn create mode 100644 src/exa/i965/new_job_old_tricks/vertex_buffers.png diff --git a/src/exa/i965/new_job_old_tricks.mdwn b/src/exa/i965/new_job_old_tricks.mdwn new file mode 100644 index 0000000..5b3c99b --- /dev/null +++ b/src/exa/i965/new_job_old_tricks.mdwn @@ -0,0 +1,118 @@ +[[meta title="A new job, but old performance fixes"]] + +[[tag exa performance i965]] + +Many readers have heard already, but it will be news to some that I +recently changed jobs. After just short of 4 years with Red Hat, I've +now taken a job working for Intel, (in its Open-source Technology +Center). It was hard to leave Red Hat---I have only fond memories of +working there, and I will always be grateful to Red Hat for first +helping me launch a career out of working on Free Software. + +Fortunately, as far as my free-software work is concerned, much of it +will be unaffected by the job change. In fact, since I've been looking +at X/2D/Intel driver graphics performance for the last year already, +this job change should only help me do *much* *more* of that. And as +far as [cairo](http://cairographics.org) goes, I'll continue to +maintain it, but I haven't been doing much feature development there +lately anyway. Instead, the most important thing I feel I could do for +cairo now is to continue to improve X 2D performance. And that's an +explicit job requirement in my new position. So I think the job change +will be neutral to positive for anyone interested in my free-software +efforts. + +As my first task at Intel, I took the nice HP 2510p laptop I was given +on the first day, (which has i965 graphics of course), installed Linux +on it, then compiled everything I needed for doing X development. I +would have saved myself some pain if I had used these [build +instructions](http://wiki.x.org/wiki/Development/git). I've since +repeated that exercise with the instructions, and they work quite +well, (though one can save some work by using distribution-provided +development packages for many of the dependencies). + +Also, since I want to do development with +[GEM](http://lwn.net/Articles/283793/), I built the drm-gem branches +of the mesa, drm, and xf86-video-intel modules. That's as simple as +doing "git checkout -b drm-gem origin/drm-gem" after the "git clone" +of those three modules, (building the master branch of the xserver +module is just fine). That seemed to build and run, so I quickly +installed it as the X server I'm running regularly. I figured this +would be great motivation for myself to fix any bugs I +encountered---since they'd impact everything I tried to do. + +Well, it didn't take long to find some performance bugs. Just +switching workspaces was a rather slow experience---I could literally +watch xchat repaint its window with a slow swipe. (Oddly enough, +gnome-terminal and iceweasel could redraw similarly-sized windows much +more quickly.) And it didn't take much investigation to find the +problem since it was something I had [found +before](http://cworth.org/exa/i965/synchronous_composite/), a big, +blocking call to i830WaitSync in every composite operation. My old +favorite, "x11perf -aa10text" was showing only 13,000 glyphs per +second. + +I had done some work to alleviate that before, and Dave Airlie had +continued that until the call was entirely eliminated at one +point. That happened on the old "intel-batchbuffer" branch of the +driver. Recall that in January Eric and I had been disappointed to +[report](http://cworth.org/talks/lca_2008/) that even after a recent +2x improvement, the intel-batchbuffer branch was only at 109,000 +glyphs per second compared to 186,000 for XAA. + +Well, that branch had about a dozen, large, unrelated changes in it, +and poor Eric Anholt had been stuck with the job of cleaning them up +and landing them independently to the master branch, (while also +writing a new memory manager and porting the driver to it). + +So here was one piece that just hadn't been finished yet. The driver +was still just using a single vertex buffer that it allocates +upfront---and a tiny buffer---just big enough for a single rectangle +for a single composite operation. And so the driver was waiting for +each composite operation to finish before reusing the bugger. And the +change to GEM had made this problem even more noticeable. And Eric +even had a partially-working patch to fix this---simply allocating a +much larger vertex buffer and only doing the sync when wrapping around +after filling it up. He had just been too busy with other things to +get back to this patch. So this was one of those times when it's great +to have a fresh new co-worker appear in the next cubicle asking how he +could help. I took tested Eric's patch, broke it up into tiny pieces +to test them independently, and Eric quickly found what was needed to +fix it, (an explicit flush to avoid the hardware caching vertex-buffer +entries that would be filled in on future composite calls). + +So, with that in place the only thing left to decide was how large of +a vertex buffer to allocate upfront. And that gives me an excuse to +put in a performance plot: + +[[img vertex_buffers.png]] + +So the more the better, (obviously), until we get to 256 composite +operations fitting into a single buffer. Then we start losing +performance. So on the drm-gem branch, this takes performance from +13,000 glyphs/second to 100,000 glyphs/second for a 7.7x +speedup. That's a nice improvement for a simple patch, even if the +overall performance isn't astounding yet. It is at least fast enough +that I can now switch workspaces without getting bored. + +So I went ahead and applied these patches to the master branch as +well. Interestingly, without any of the drm-gem branches, and even +with the i830WaitSync call on every composite operation, things were +already much better than in the GEM world. I measured 142,000 +glyphs/second before my patch, and 208,000 glyphs/second after the +patch. So only a 1.5x speedup there, but for the first time ever I'm +actually measuring EXA text rendering that's faster than XAA text +rendering. Hurrah! + +And really, this is still just getting started. The patch I've +described here is still just a bandaid. The real fix is to eliminate +the upfront allocation and reuse of buffers. Instead, now that we have +a real memory manager, (that's the whole point of GEM), we can +allocated buffer objects as needed for vertex buffer, (and for surface +state objects, etc.). That's the work I'll do next and it should let +us finally see some of the benefits of GEM. Or if not, it will point +out some of the remaining issues in GEM and we'll fix those right +up. Either way, performance should just keep getting better and +better. + +Stay tuned for more from me, and look forward to faster performance +from every Intel graphics driver release. diff --git a/src/exa/i965/new_job_old_tricks/vertex_buffers.png b/src/exa/i965/new_job_old_tricks/vertex_buffers.png new file mode 100644 index 0000000000000000000000000000000000000000..3f44cdb4176c2a08c837f86fc1e379e6112b802b GIT binary patch literal 14350 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 gflg_e-?wm=eK|jE-1p1DUOh