> -----Original Message-----
> From: Andone, Bogdan [mailto:bogdan.and...@intel.com]
> Sent: Friday, March 18, 2016 11:58 AM
> To: 'Nikita Popov' <nikita....@gmail.com>
> Cc: internals@lists.php.net
> Subject: RE: [PHP-DEV] Lazy keys comparison during hash lookups
> 
> > -----Original Message-----
> > From: Andone, Bogdan [mailto:bogdan.and...@intel.com]
> > Sent: Wednesday, March 09, 2016 9:33 AM
> > To: 'Nikita Popov'
> > Cc: internals@lists.php.net
> > Subject: RE: [PHP-DEV] Lazy keys comparison during hash lookups
> >
> >
> >
> > > -----Original Message-----
> > > From: Andone, Bogdan [mailto:bogdan.and...@intel.com]
> > > Sent: Tuesday, March 08, 2016 4:19 PM
> > > To: 'Nikita Popov'
> > > Cc: internals@lists.php.net
> > > Subject: RE: [PHP-DEV] Lazy keys comparison during hash lookups
> > >
> > > > From: Nikita Popov [mailto:nikita....@gmail.com]
> > > > Sent: Tuesday, March 08, 2016 3:43 PM
> > > > To: Andone, Bogdan
> > > > Cc: internals@lists.php.net
> > > > Subject: Re: [PHP-DEV] Lazy keys comparison during hash lookups
> > > > >On Tue, Mar 8, 2016 at 2:18 PM, Nikita Popov
> > > > ><nikita....@gmail.com>
> > > wrote:
> > > > >> On Tue, Mar 8, 2016 at 2:01 PM, Andone, Bogdan
> > > > <bogdan.and...@intel.com> wrote:
> > > > >> Hi Guys,
> > > > >>
> > > > >> I would like to propose a small change into the DJBX33A hash
> > > function
> > > > algorithm which will make easier the key matching validations in
> > hash
> > > > lookup functions.
> > > > >>
> > > > >> The change addresses the modulo 8 tailing bytes of the key. For
> > > these
> > > > bytes we can use an 8 bit shift instead of a 5 bit shift; we also
> > need
> > > > to replace the ADD by XOR, in order to avoid byte level overflows.
> > > This
> > > > change ensures the uniqueness of the hash function transformation
> > for
> > > > the tailing bytes: supposing two strings have same partial hash
> > value
> > > > for the first Nx8 bytes, different combinations of tailing
> > characters
> > > > (with the same tail size) will always generate different keys.
> > > > >> We have the following consequences:
> > > > >> If two strings have:
> > > > >> - same hash value,
> > > > >> - same length,
> > > > >> - same bytes for the first Nx8 positions, then they are equal,
> > > > >> and the tailing bytes can be skipped during
> > > > comparison.
> > > > >>
> > > > >> There is a visible performance gain if we apply this approach
> > > > >> as
> > we
> > > > can use a lightweight memcmp() implementation based on longs
> > > comparison
> > > > and completely free of the complexity incurred by tailing bytes.
> > > > For Mediawiki I have a 1.7%  performance gain while Wordpress
> > > > reports
> > 1.2%
> > > > speedup on Haswell-EP.
> > > > >>
> > > > >> Let’s take a small example:
> > > > >> Suppose we have a key=”this_is_a_key_value”.
> > > > >> The hash function for the  first N x 8 byes are computed in the
> > > > original way; suppose “this_is_a_key_va” (16bytes) will return a
> > > partial
> > > > hash value h1; the final hash value will be computed by the
> > following
> > > > sequence:
> > > > >> h = ((h1<<8) ^ h1) ^ ‘l’;
> > > > >> h = ((h<<8) ^ h) ^ ‘u’;
> > > > >> h = ((h<<8) ^ h) ^ ‘e’;
> > > > >> or, in only one operation:
> > > > >> h = (h1<<24) ^ (h1<<16) ^ (h1<<8) ^ h1 ^ (‘l’<<16) ^
> > ((‘l’^‘u’)<<8)
> > > ^
> > > > (‘l’^’u’^‘e’)
> > > > >> We can see that ht=(‘l’<<16) ^ ((‘l’^‘u’)<<8) ^
> > > (‘l’^’u’^‘e’)  cannot
> > > > be obtained by any other 3 characters long tail. The statement is
> > not
> > > > true if we use ADD instead of XOR, as extended ASCII characters
> > might
> > > > generate overflows affecting the LSB of the higher byte in the
> > > > hash value.
> > > > >>
> > > > >> I pushed a pull request here: https://github.com/php/php-
> > > > src/pull/1793. Unfortunately it does not pass the travis tests
> > because
> > > > “htmlspecialchars etc use a generated table that assumes the
> > > > current hash function” as noticed by Nikita.
> > > > >>
> > > > >> Let me know your thoughts on this idea.
> > > > >
> > > > > Hey Bogdan,
> > > > > This looks like an interesting idea! I'm somewhat apprehensive
> > about
> > > > coupling this to a change of the hash function, for two reasons:
> > > > > a) This will make it more problematic if we want to change the
> > hash
> > > > function in the future, e.g. if we want to switch to SipHash.
> > > > > b) The quality of the new hash distribution is not immediately
> > > clear,
> > > > but likely non-trivially weaker.
> > > > > So I'm wondering if we can keep the concept of using a
> > > > > zend_ulong
> > > > aligned memcmp while leaving the hash function alone: The
> > zend_string
> > > > allocation policy already allocates the string data aligned and
> > padded
> > > > to zend_ulong boundaries. If we were to additionally explicitly
> > > > zero
> > > out
> > > > the last byte (to avoid valgrind warnings) we should be able to
> > > compare
> > > > the character data of two zend_strings using a zend_ulong memcmp.
> > This
> > > > would have the additional benefit that it works for normal string
> > > > comparisons (unrelated to hashtables) as well. On the other hand,
> > this
> > > > is only possible for zend_string to zend_string comparisons, not
> > > > for comparisons with static strings.
> > > > > Regards,
> > > > s/zero out the last byte/zero out the last zend_ulong I'd like to
> > > > add another issue with relying on the hash for this
> > which
> > > I
> > > > just remembered: We currently always set the top bit of the hash
> > > > for strings (see
> > > http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_string.h#351),
> > > > in order to ensure that hashes are never zero. This makes the hash
> > > non-
> > > > unique.
> > > > Nikita
> > > Yeah... I missed the top bit set, but I think it could be solved
> > > somehow.
> > Actually setting the top bit is not an issue. The transformation
> > unicity shall be ensured for the tailing bytes only which are shifted
> > to the left maximum 7(on 64bits)/3(on 32bits) positions; so they will
> > never reach the top byte and setting the top bit will not harm in
> > terms of tailing bytes.
> >
> > > Imho the strongest reason against the patch is the dependency
> > > between the hash function and the key validation method but I have
> > > the impression that this is not be the most dirty hack deployed ever
> > > in
> > PHP;
> > > and I don't know very well what is the degree of compromise
> > > acceptable for 1% speedup :-)
> > Otherwise the concerns about the quality of the hash function and the
> > intention to change it with SipHash or something else, I am afraid it
> > will not bring much in terms of collisions reductions; the issue here
> > is the fact that hash bucket selection uses only the last few bits of
> > the hash value. Not sure how much could help here SipHash...
> > Just for defending the technical feasibility of this POC I think that
> > injective transformation for last tailing bytes could be deployed over
> > theoretically any hash function.
> >
> > Thanks,
> > Bogdan
> >
> > > On the other hand, your idea to zero the last zend_ulong in the
> > > zend_string looks nice. There could be an additional potential gain
> > also
> > > from lightweight implementation of memcmp(). I will give it a try to
> > see
> > > if it gets better results than the current proposal.
> > > I assume all zend_string allocation functions are located in
> > > zend_string.h, correct?
> > >
> > > Thanks,
> > > Bogdan
> I worked a little bit of adapting the zend string allocation functions for 
> ensuring
> long size alignment for both Zend and standard allocation, and for zeroing out
> the end of the strings.
> The sad surprise was to discover that there are other >100 situations, in
> extensions mainly, where the zend strings are truncated by directly inserting 
> '0'
> terminators in the middle of strings; all these situations needs to be 
> revised for
> zeroing out the entire memory word enclosing the end of the string.
> It will probably take a while for revising most of these places before having 
> a
> correct real life run for being able to validate the POC.
> Optimistically speaking, supposing POC will show motivating results, we will
> probably need to enforce the Zend interface with respect to string truncation 
> -
> at least a getter/setter pair for ZSTR_LEN where the setter should ensure also
> synchronizing the string termination.
> I suppose it will be an important effort for the community and I am not sure 
> how
> much willingness will be in it. On the other side, the originally proposed 
> solution
> does not have all this drawbacks and it offers 1% speedup by a localized 
> change
> which could be easily reverted in the case we will get better results by a 
> better
> string alignment in future.
> 
> Thanks,
> Bogdan
I finalized the POC for ensuring strings granularity to zend_long size; you can 
find it here: 
https://gist.github.com/bogdanandone/cfde764f64137b57ce2a6f4a9b52e757 
Unfortunately, the effort to zero-out the end of strings generates an overhead 
higher than the gain incurred by simplified zend_memcpy_aligned memcpy() & 
memcmp() implementations - I defined a simpler zend_memcpy_aligned() function 
used in zend_string.h and zend_ulong_memeq() used in zend_hash.c inherited from 
my original place. Overall I see ~1% loss on Worpress on my platform. I didn't 
spend time in fine tuning each place needing changes but I don't expect a real 
difference.
Probably this is not the way to go and I finalize with a last buzz for the 
original patch proposal :)

Thanks,
Bogdan

Reply via email to