Hi Jerome,<br><br>Concerning rewriting mesa etc. I am not a fan of complete rewriting. 
It&#39;s like throwing years of hard work and ideas and testing and fixing 
away. Sometimes this can get really crazy, resulting in endless rewrites
 with nothing being stable or usable for end users. I think I have seen this tendency in you. If you think there are any performance issues in the current code, please consider fixing them 
there, one by one.<br><br>Another thing you can do for your testing is to measure the time spent in radeon_cs_emit and bo_wait, and compare that to the time spent elsewhere in the driver. bo_wait calls can sometimes be optimized away with a smart buffer management (keep in mind that all write-only transfers can be implemented *without* bo_wait, but you first need resource_copy for pure buffers). Concerning radeon_cs_emit, it can be called in another thread so as not to cost you anything on multi-core machines (it&#39;s not so simple, some DRM calls may need to wait until radeon_cs_emit finishes, but you shouldn&#39;t need such a synchronization if vertices/pixels/commands flow only one way i.e. to the GPU).<br>

<br>In r300g, we had the following issue. The driver used to spend too much time in pb_bufmgr_cache, especially in the create and map functions and in the winsys as well. It turned out the real problem was somewhere entirely else: we used u_upload_mgr somewhat naively and that was slowing down the driver a lot. However if you had had a look at the profiler results, you wouldn&#39;t have been able to see any obvious connection between u_upload_mgr and the winsys. Eventually the fix turned out to be pretty simple, but my point is that profiler data can show you the bottleneck, but not the real cause nor will it help you to find the best (or just a good) solution.<br>

<br>Also I think demos/perf/* are quite bad tests if you care about performance in real apps. Running some replays from real games under sysprof or callgrind will give you more interesting results. Phoronix Test Suite comes in handy here as it contains a lot of automatic tests of real games you can tear apart and use. :) But not all of them are framerate-independent, therefore you cannot always expect callgrind to give you meaningful results.<br>

<br>Marek<br><br><div class="gmail_quote">On Fri, Nov 12, 2010 at 8:55 PM, Jerome Glisse <span dir="ltr">&lt;<a href="mailto:j.glisse@gmail.com">j.glisse@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">

Hi,<br>
<br>
I have been doing some benchmarking lately to try to identify<br>
bottleneck in the Mesa/Gallium/R600 driver. I fear results are not<br>
ones i expected. I would have liked GPU being the bottleneck and thus<br>
additions of new features such as texture tiling or hyper-z would<br>
immediatly boost performance. I mostly used &quot;old&quot; GL application such<br>
quake 3 like rendering (openarena, cake, quake3, ut2004, nexuiz are<br>
among the program i used), i am pretty sure in the end most of them<br>
can reach close to maximum performance without use of advanced GPU<br>
optimization such as hyper-z, because newer hardware such as<br>
R600/R700/Evergreen should have enough raw power for that kind of<br>
rendering.<br>
<br>
I have used sysprof (you will find sysprof xml files in above<br>
mentioned archive) to collect timing information on where the CPU is<br>
spending it&#39;s time. To minimize GPU impact and avoid having to compare<br>
with pageflipping games were run in windowed mode at 800x600. I would<br>
also like to point out that given the nature of GL, and the layering<br>
of our implementation of it, one should be prudent with sysprof<br>
results. If the top of the stack is inefficient and calls several<br>
times into the hw driver, the hw driver functions might turn at the<br>
top offender but this doesn&#39;t necessarily mean that they are the one<br>
to blame (though they share responsabilities ;)).<br>
<br>
(b billion, m million, t thousand) (ng no gpu ie no cmd submited to<br>
gpu, np no pipe ie pipe driver call turn into no operation, nb no<br>
kernel buffer bo management ie replaced by malloc, nv47b nv47 nvidia<br>
blob, nv47g nouveau gallium driver)<br>
<br>
I used same configuration (with HD3650, GTS7800 or X1950 radeon with<br>
mesa from nov 11). You can download most of the sysprof/results here<br>
(along with some ddx patch to remove throttling and mesa patch showing<br>
how i commented out the pipe driver part):<br>
<br>
<br>
<br>
* vertexrate --------------------------------------------------------<br>
<br>
verts/second      immediate    glDrawArrays  VBO_glDrawArrays<br>
r600g ngnpnb          16.2m        11700.0m          16600.0m<br>
r600g ngnp            33.5m        11700.0m          17400.0m<br>
r600g ng              29.2m           58.3m           2200.0m<br>
r600f                 33.5m           95.1m            240.4m<br>
r600g                 29.3m          117.3m            203.8m<br>
r600c                 17.2m           42.2m            121.9m<br>
r500g                 17.5m           31.0m            141.5m<br>
r500c                 14.4m           13.5m             12.5m<br>
r500c ng              20.2m           57.5m           1700.0m<br>
nv47g                 14.5m           55.5m            118.7m<br>
nv47b                 65.8m           68.7m            201.2m<br>
<br>
demos/perf/vertexrate shows that the r600g driver is close to fglrx<br>
(sometimes a little bit faster, sometimes a little bit slower). Other<br>
tests I have done make me confident that the vertex path is not the<br>
bottleneck in our pipeline (thought it could still probably be<br>
optimized further). Also it seems that we are suffering from call<br>
overhead (likely TLS or others similar optimization in our GL<br>
dispatching stuff), nvidia is a lot better at facing millions of call.<br>
<br>
Also it seems quite clear that gallium driver out perform classic<br>
driver in DrawArrays case. So from pure vertex rate point of view<br>
gallium is better.<br>
<br>
<br>
* swapbuffer --------------------------------------------------------<br>
<br>
in pixel/second 320x240(s) 320x240(scd) 1200x1024(s) 1200x1024(scd)<br>
r600g ngnpnb          797m         647m        1100m           730m<br>
r600g ngnp            795m         651m        1100m           729m<br>
r600g ng              812m         470m        1100m           729m<br>
r600f                1300m         678m        1500m           980m<br>
r600g                 730m         324m        1100m           365m<br>
r600c                 654m         269m         728m           363m<br>
r500g                1100m         535m        2800m          1500m<br>
r500c                 954m         310m        1800m          1400m<br>
r500c ng             1100m         367m        1800m          1800m<br>
nv47g                1100m          30m        5200m           394m<br>
nv47b                2300m        2000m        3700m          2600m<br>
(s swap, scd swap/clear/draw)<br>
<br>
demos/perf/swapbuffers pretty much shows that even if dri2 is less<br>
efficient at performing buffer copy/swap than fglrx it&#39;s still not the<br>
thing that is slowing us down.<br>
<br>
* drawoverhead ------------------------------------------------------<br>
<br>
draw call/second     draw only     draw nop sc     draw sc<br>
r600g ngnpnb             1600t           1500t        173t<br>
r600g ngnp               1700t           1600t         69t<br>
r600g ng                  220t            220t         46t<br>
r600f                    4100t           3500t       1300t<br>
r600g                     123t            122t         34t<br>
r600c                      73t             71t         60t<br>
r500g                     235t            236t         80t<br>
r500c                     115t            115t         97t<br>
r500c ng                  171t            171t        135t<br>
nv47g                     496t            471t        121t<br>
nv47b                   10600t           9300t       1200t<br>
(nop sc no op state ie state changed but to same value, sc state<br>
change to different value btw each draw call)<br>
<br>
<br>
demos/perf/drawoverhead has the most interesting number r600g goes<br>
from 123t(call/sec) when no state change between draw calls to<br>
34t(call/sec) when a state change between draw calls (the fact that we<br>
only draw 4 vertex at each call is not that important as if we trust<br>
vertexrate we are not impacted by the number of vertices we draw). So<br>
state change divides by 3.6 the raw performance of our stacks. I<br>
wanted to know who was to blame for this.<br>
<br>
In order to find out which part of the stack is underperforming in<br>
front of state changes I slowly disabled layer starting by the bottom<br>
(which is the only way to do this ;o)). Thus i disabled the command<br>
buffer submission to the GPU (r600g-nogpu) and made sure the driver<br>
still believed things where happening. Drawoverhead state change from<br>
123t(call/sec-r600g) to 220t(call/sec-r600g-nogpu). So the GPU is<br>
slowing down things a bit but not that much, also comparing sysprof<br>
shows that we are spending lot of time in cs ioctl.<br>
<br>
Next was to disable the r600g pipe driver, basically turning the<br>
driver into no-op where each call into it is ignored except for<br>
buffer/resource/texture allocations. Drawoverhead state change from<br>
220t(call/sec-r600g-nogpu) to 1700t(call/sec-r600g-nogpu-nopipe).<br>
Obviously the r600g pipe is a CPU intensive task, lot of registers<br>
marshmalling. But the most disturbing fact is that we achieve 24.6<br>
times less draw call per second when there is a state change than when<br>
there is none. Pointing out that the pipe driver is likely not the<br>
only one to blame.<br>
<br>
Last was to see if our memory allocation throught gem/ttm was hurting<br>
us. Yes it does (drawoverhead no state change<br>
1600t(call/sec-r600g-nogpu-nopipe-nobo, drawoverhead state change<br>
173t(call/sec-r600g-nogpu-nopipe-nobo). So when we use malloc for<br>
buffer allocation the performances, between no state change and a<br>
state change, drops only by a factor of 9.4. So obviously GPU buffer<br>
allocation is costing us a lot.<br>
<br>
sysprof shows that it&#39;s the shader constant where we are loosing most<br>
of our bo cpu time. In that path it&#39;s the pb_bufmgr_cache mecanism<br>
that are too blame, we are loosing lot of time in gettimeofday. We are<br>
also loosing lot of time in pb_bufmgr_cache bo allocation path (again<br>
for gettimeofday). Those shows too in nexuiz sysprof but they looks<br>
less offencive there as nexuiz usage pattern also suffer from<br>
bottleneck in others part of the code.<br>
<br>
Drawoverhead outcome is i believe that gallium is severly<br>
under-performing in front of states changes. If i had to guess i would<br>
say that an improvement of factor n in this would gives an improvement<br>
of ~n for the overall (at least for r600g and likely for r300g too).<br>
Does any one works on this ? Or knows what could be done to improve<br>
this ? I didn&#39;t spot any obvious mistake in mesa state tracker. Of<br>
course one could argue that it&#39;s the pipe driver which is slow but i<br>
don&#39;t it&#39;s the only one to blame. Classic driver doesn&#39;t fallover in<br>
drawoverhead test, thought classic driver are lot less performant on<br>
this benchmark so maybe bottleneck in classic is also somewhere in<br>
state world.<br>
<br>
To me gallium need to be improved to be more efficient at changing<br>
only the smallest number of states and try avoid call into the pipe<br>
driver. I am not saying there is nothings to be done in the pipe<br>
driver but there is a limit on what we can do there and gpu driver is<br>
all about avoiding doing things :o)<br>
<br>
Also nvidia nouveau driver shows the same kind of issue on<br>
drawoverhead. I would also like to stress that i am not interested in<br>
making drawoverhead superfast but that i believe drawoverhead exibits<br>
one of the biggest shortcoming of today gallium implementations.<br>
<br>
This made me wonders if it would not be good time to start thinking<br>
about doing a new GL2 pure state tracker for gallium (an idea i stole<br>
from Stephane ;o)).<br>
Drawbacks :<br>
        - loosing/missing fixes/GL spec interpretations grown into mesa  over<br>
all the years<br>
        - Intel is still on classic, GL is big.<br>
Advantages :<br>
        - implemented with efficiency straight from the begining<br>
        - less code than mesa ?<br>
        - faster gallium.<br>
<br>
To mitigate the drawbacks one might start with EGL2 only which should<br>
be a lot simpler to achieve while steal allowing to test some real app<br>
(like quake3 or<br>
doomIII rendering engine).<br>
<br>
* bo allocation -----------------------------------------------------<br>
<br>
Another constant pattern i see in all benchmark i done is that a lot<br>
of bo allocation/destruction happen during draw sequency (for shader &amp;<br>
shader constant mostly but also for temporary vertex buffer). Current<br>
solution we have are under performing (see pb_bufmgr* bottleneck i am<br>
talking about above).<br>
<br>
Another point is how we handle all those bo, it seems that our current<br>
command submission mecanism are severly slow. If you look at sysprof<br>
you will see that despiste us being 3 times slower we are spending<br>
same amount of time in kernel space than closed source driver, this<br>
surely can&#39;t be good.<br>
<br>
To address this i want to play a bit with ttm. For instance try<br>
considering every bo as pinned and use a big gtt space (simplify away<br>
bo validation/allocation to be lock less or at least very quick). This<br>
would help to determine the cost (if any) of our current scheme where<br>
we always try to satisfy userspace request which is often to have bo<br>
into vram. I might also try to change the bo mapping by always using<br>
page and add a call to sync vram copy (such changes obviously need<br>
hack into userspace too otherwise they won&#39;t work properly).<br>
<br>
Also, i think, that we have been bit naive to think that one can<br>
optimize GL stack and make it fast afterward (at least I have been<br>
naive :o)). Efficient/performant GL stack can only be done by<br>
carefully evaluating each step of the way and changing what needs to<br>
be change no matter where in the stack. Which would have mean for us<br>
having unstable kernel API until we are at a point where we see GL<br>
performing reasonably well (I envy nouveau people who are wiser on<br>
this front :o)).<br>
<br>
Sorry for the long mail, but i wanted to explain the reasoning behind<br>
my findings. Maybe i am completely wrong and overlooked something, i<br>
hope not.<br>
<br>
Cheers,<br>
<font color="#888888">Jerome Glisse<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</font></blockquote></div><br>