<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Mon, Apr 3, 2017 at 3:45 PM, Chad Versace <span dir="ltr"><<a href="mailto:chadversary@chromium.org" target="_blank">chadversary@chromium.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">On Mon 03 Apr 2017, Jason Ekstrand wrote:<br>
> On Mon, Apr 3, 2017 at 12:31 PM, Chad Versace <<a href="mailto:chadversary@chromium.org">chadversary@chromium.org</a>><br>
> wrote:<br>
><br>
> > On Fri 31 Mar 2017, Chad Versace wrote:<br>
> > > On Wed 15 Mar 2017, Jason Ekstrand wrote:<br>
> > > > This cache allows us to easily ensure that we have a unique anv_bo for<br>
> > > > each gem handle.  We'll need this in order to support multiple-import<br>
> > of<br>
> > > > memory objects and semaphores.<br>
> > > ><br>
> > > > v2 (Jason Ekstrand):<br>
> > > >  - Reject BO imports if the size doesn't match the prime fd size as<br>
> > > >    reported by lseek().<br>
> > > ><br>
> > > > v3 (Jason Ekstrand):<br>
> > > >  - Fix reference counting around cache_release (Chris Willson)<br>
> > > >  - Move the mutex_unlock() later in cache_release<br>
> > > > ---<br>
> > > >  src/intel/vulkan/anv_<wbr>allocator.c | 261 ++++++++++++++++++++++++++++++<br>
> > +++++++++<br>
> > > >  src/intel/vulkan/anv_private.h   |  26 ++++<br>
> > > >  2 files changed, 287 insertions(+)<br>
> > ><br>
> > ><br>
> > > > +static uint32_t<br>
> > > > +hash_uint32_t(const void *key)<br>
> > > > +{<br>
> > > > +   return (uint32_t)(uintptr_t)key;<br>
> > > > +}<br>
> > ><br>
> > > This hash function does not appear hashy.<br>
> > ><br>
> > > If I correctly understand the details of Mesa's struct hash_table,<br>
> > > choosing the identify function for the hash function causes unwanted<br>
> > > clustering when inserting consecutive gem handles.  Since the kernel does<br>
> > > allocate gem handles consecutively, the problem is real.<br>
> > ><br>
> > > For proof, consider the following:<br>
> > ><br>
> > >    - Suppose a long-running process (maybe the compositor) has thrashed<br>
> > on the<br>
> > >      hash table long enough that its bucket count<br>
> > >      is ht->size = hash_sizes[7].size = 283. Suppose a spike of<br>
> > >      compositor activity raises the hash table's density to about 0.5.<br>
> > >      And suppose the hash table buckets are filled with the consecutive<br>
> > gem<br>
> > >      handles<br>
> > ><br>
> > >      {0, 0, 0, 0, 4, 5, 6, 7, 8, 9, ..., 127, 128, 0, 0, 0, ..., 0 }<br>
> > ><br>
> > >      The exact density is (128 - 4 + 1) / 283 = 0.4417.<br>
> > ><br>
> > >    - Next, some other in-process activity (maybe OpenGL) generated<br>
> > >      a lot of gem handles after Vulkan's most recently imported<br>
> > >      gem handle, 128.<br>
> ><br>
> > This point in the example---the reason why the gem handles in the<br>
> > anv_bo_cache skip from 128 to 287---is bogus in Vulkan. The problem *is*<br>
> > real for multiple in-process OpenGL contexts derived from the same<br>
> > EGLDisplay, using EGL_EXT_image_dma_buf_import, because each context<br>
> > shares the same intel_screen, and therefore the same drm device fd. But<br>
> > in Vulkan, each VkDevice opens its own drm device id. So, bogus example.<br>
> ><br>
> > BUT, that leads to a new question...<br>
> ><br>
> > Since each VkDevice has a unique drm device fd, and since the kernel<br>
> > allocates gem handles consecutively on the fd, and since struct<br>
> > hash_table only grows and never shrinks, and since patch 8/18 inserts<br>
> > every VkDeviceMemory into the cache... I believe no collisions are<br>
> > possible in anv_bo_cache.<br>
> ><br>
><br>
> Does this fall under the category of unbreakable kernel ABI or is it just a<br>
> side-effect of the implementation?  If not, then I'm reluctant to trust it.<br>
<br>
</div></div>I'm not certain. But krh, sitting beside me, says "It's ABI at this point.<br>
The kernel uses a 'idr' structure which guarantees that behavior".<span class=""><br></span></blockquote><div><br></div><div>If we think we can rely on it, I'm happy to make it into a flat array but it'll basically be a simplified hash table at that point.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> > If there are no collisions, then the hash table is only adding overhead,<br>
><br>
> Sure, but a no-collision hash table is pretty cheap...<br>
><br>
> > and we should use a direct-addressing lookup table. The bo cache should<br>
> > look like this:<br>
> ><br>
> >     struct anv_bo_cache {<br>
> >        /* The array indices are gem handles. Null entries are legal. */<br>
> >        struct anv_bo **bos;<br>
> ><br>
> >        /* Length of the array. Because the array can have holes, this<br>
> >         * is *not* the number of gem handles in the array.<br>
> >         */<br>
> >        size_t len;<br>
> ><br>
> >        pthread_mutex_t mutex;<br>
> >     };<br>
> ><br>
> >     struct anv_bo *<br>
> >     anv_bo_cache_lookup(struct anv_bo_cache *cache, uint32_t gem_handle)<br>
> >     {<br>
> >        struct anv_bo *bo = NULL;<br>
> ><br>
> >        pthread_mutex_lock(&cache-><wbr>mutex);<br>
> ><br>
> >        if (gem_handle < cache->len)<br>
</span>> >           bo = cache->entries[gem_handle];<br>
<span class="">> ><br>
> >        pthread_mutex_unlock(&cache-><wbr>mutex);<br>
> ><br>
> >        return bo;<br>
> ><br>
> >     }<br>
> ><br>
> > BUT, that leads to yet another question...<br>
> ><br>
> > Why is patch 8/18 inserting every VkDeviceMemory into the cache? If<br>
> > I understand things correctly, we only *need* to insert a VkDeviceMemory<br>
> > into the cache if, in vkAllocateMemory, either (1)<br>
> > VkExportMemoryAllocateInfoKHX:<wbr>:handleTypes != 0 or (2)<br>
> > VkMemoryAllocateInfo's pNext chain contains an import structure.<br>
> ><br>
><br>
> Because I'm lazy.  In order to start using the bo cache,<br>
> anv_device_memory::bo needs to be a pointer (well, it's makes the BO cache<br>
> API simpler and more efficient if it's a pointer).  This would mean that we<br>
> would have to allocate an additional chunk of memory or go through some<br>
> other hoops in order to make it work.  At the end of the day, just stuffing<br>
> everything in the cache was simpler and kept us to a single path.<br>
<br>
<br>
</span>Huh? I don't understand how pointers-or-not-pointers affect the decision<br>
to use an array or a hash table.  In the lookup function I wrote above,<br>
the anv_bo_cache holds pointers to anv_bo's, just like the anv_bo_cache<br>
in the actual patch. (I kept it that way so the API would be the same as<br>
in your patch series).<span class=""><br></span></blockquote><div><br></div><div>That was in response to the question about inserting every VkDeviceMemory into the cache.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> > If we insert into the cache only those VkDeviceMemories that are<br>
> > imported or that will be exported, then the bo cache remains small, and<br>
> > we *should* use a hash table.<br>
> ><br>
><br>
> Maybe.  But the client isn't supposed to be allocating hundreds of<br>
> VkDeviceMemory objects.  It's supposed to allocate a few and then<br>
> suballocate from those.  If the client allocates so many memory objects<br>
> that they start hitting hash table performance issues, that's their own<br>
> fault.<br>
><br>
> Also, please remember that vkAllocateMemory is considered to be a *very*<br>
> heavy-weight function in Vulkan.  Compared to an ioctl which allocates<br>
> memory, a hash table insert is trivial.  I'm reasonably happy to make a few<br>
> changes here or there to make it more efficient if any of this proves to be<br>
> a problem.  However, I think we're working way too hard to micro-optimize<br>
> something that takes < 0.001% of runtime.<br>
<br>
</span>True. The difference between time needed for an array lookup and a hash<br>
table lookup is tiny compared to the ioctl. I wasn't suggesting to use<br>
an array for that 0.001% though. I suggested an array because struct<br>
hash_table, as used in this patch, is just a resizable array with an<br>
unneeded vtable. It just felt weird.<br>
<br>
We've discussed all the things :) I'll drop the complaint about<br>
hash-vs-array.<br>
</blockquote></div><br></div></div>