* SIMD accelerated crush_do_rule proof of concept
@ 2016-08-29 11:42 Loic Dachary
2016-08-29 13:15 ` Mark Nelson
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Loic Dachary @ 2016-08-29 11:42 UTC (permalink / raw)
To: Ceph Development
Hi,
TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is straightforward and would help with crushmap validation, is there any reason not to do it ?
When resolving a crush rule (crush_do_rule in mapper.c), the straw2 function (bucket_straw2_choose) calls the hashing function (crush_hash32_3) for each item in a bucket and keeps the best match. When a bucket has four items, the hash function can be run using SIMD instructions. Each item value is 32 bits and four can fit in a __m128i.
I tried to inline the hash function when the conditions are right[1] and run a test to measure the difference.
crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0
time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4
rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4
rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000
With SIMD
real 0m10.433s
user 0m10.428s
sys 0m0.000s
Without SIMD
real 0m19.344s
user 0m19.340s
sys 0m0.004s
Callgrind estimated cycles for each crush_do_rule are in the same range:
rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4
kcachegrind crush.callgrind
With SIMD : crush_do_rule is estimated to use 21 205 cycles
Without SIMD : crush_do_rule is estimated to use 53 068 cycles
This proof of concept relies on instructions that are available on all ARM & Intel processors, nothing complicated is going on. It is beneficial to crush maps that have more than four disks per host, more than four hosts per rack etc. It probably is a small win for an OSD or even a client. For crushmap validation it helps significantly since the MON are not able to run crushtool asynchronously and it needs to run within a few seconds (because it blocks the MON).
The implementation is straightforward: it needs sub/xor/lshift/rshift. The only relatively tricky part is runtime / compile time detection of the SIMD instructions for both Intel and ARM processors. Luckily this has already been taken care of when integrating with the jerasure erasure code plugin.
Is there any reason why it would not be good to implement this ?
Cheers
[1] https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef
--
Loïc Dachary, Artisan Logiciel Libre
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 11:42 SIMD accelerated crush_do_rule proof of concept Loic Dachary @ 2016-08-29 13:15 ` Mark Nelson 2016-08-29 13:57 ` Sage Weil 2016-08-29 13:58 ` Loic Dachary 2016-08-29 13:55 ` Sage Weil 2016-08-30 13:24 ` Piotr Dałek 2 siblings, 2 replies; 11+ messages in thread From: Mark Nelson @ 2016-08-29 13:15 UTC (permalink / raw) To: Loic Dachary, Ceph Development Anything we can do to help on the CPU usage front is a win IMHO, though I would be interested in seeing an example where we are spending a lot of time on crush in a real usage scenario? Mark On 08/29/2016 06:42 AM, Loic Dachary wrote: > Hi, > > TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is straightforward and would help with crushmap validation, is there any reason not to do it ? > > When resolving a crush rule (crush_do_rule in mapper.c), the straw2 function (bucket_straw2_choose) calls the hashing function (crush_hash32_3) for each item in a bucket and keeps the best match. When a bucket has four items, the hash function can be run using SIMD instructions. Each item value is 32 bits and four can fit in a __m128i. > > I tried to inline the hash function when the conditions are right[1] and run a test to measure the difference. > > crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 > time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > With SIMD > > real 0m10.433s > user 0m10.428s > sys 0m0.000s > > Without SIMD > > real 0m19.344s > user 0m19.340s > sys 0m0.004s > > Callgrind estimated cycles for each crush_do_rule are in the same range: > > rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 > kcachegrind crush.callgrind > > With SIMD : crush_do_rule is estimated to use 21 205 cycles > Without SIMD : crush_do_rule is estimated to use 53 068 cycles > > This proof of concept relies on instructions that are available on all ARM & Intel processors, nothing complicated is going on. It is beneficial to crush maps that have more than four disks per host, more than four hosts per rack etc. It probably is a small win for an OSD or even a client. For crushmap validation it helps significantly since the MON are not able to run crushtool asynchronously and it needs to run within a few seconds (because it blocks the MON). > > The implementation is straightforward: it needs sub/xor/lshift/rshift. The only relatively tricky part is runtime / compile time detection of the SIMD instructions for both Intel and ARM processors. Luckily this has already been taken care of when integrating with the jerasure erasure code plugin. > > Is there any reason why it would not be good to implement this ? > > Cheers > > [1] https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 13:15 ` Mark Nelson @ 2016-08-29 13:57 ` Sage Weil 2016-08-29 13:58 ` Loic Dachary 1 sibling, 0 replies; 11+ messages in thread From: Sage Weil @ 2016-08-29 13:57 UTC (permalink / raw) To: Mark Nelson; +Cc: Loic Dachary, Ceph Development On Mon, 29 Aug 2016, Mark Nelson wrote: > Anything we can do to help on the CPU usage front is a win IMHO, though I > would be interested in seeing an example where we are spending a lot of time > on crush in a real usage scenario? The monitor prime_pg_temp has to calculate a crush mapping for every PG when the osdmap changes in significant ways. A 2x improvement there is a big help since the work has to be timeboxed and aborted if it runs too long. Same goes for the crushtool test that runs whenever the crush map changes. sage > > Mark > > On 08/29/2016 06:42 AM, Loic Dachary wrote: > > Hi, > > > > TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is > > straightforward and would help with crushmap validation, is there any reason > > not to do it ? > > > > When resolving a crush rule (crush_do_rule in mapper.c), the straw2 function > > (bucket_straw2_choose) calls the hashing function (crush_hash32_3) for each > > item in a bucket and keeps the best match. When a bucket has four items, the > > hash function can be run using SIMD instructions. Each item value is 32 bits > > and four can fit in a __m128i. > > > > I tried to inline the hash function when the conditions are right[1] and run > > a test to measure the difference. > > > > crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter > > straw2 4 root straw2 0 > > time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test > > --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > > rule 0 (replicated_ruleset) num_rep 4 result size == 4: > > 2048000/2048000 > > > > With SIMD > > > > real 0m10.433s > > user 0m10.428s > > sys 0m0.000s > > > > Without SIMD > > > > real 0m19.344s > > user 0m19.340s > > sys 0m0.004s > > > > Callgrind estimated cycles for each crush_do_rule are in the same range: > > > > rm crush.callgrind ; valgrind --tool=callgrind > > --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map > > --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x > > 204800 --num-rep 4 > > kcachegrind crush.callgrind > > > > With SIMD : crush_do_rule is estimated to use 21 205 cycles > > Without SIMD : crush_do_rule is estimated to use 53 068 cycles > > > > This proof of concept relies on instructions that are available on all ARM & > > Intel processors, nothing complicated is going on. It is beneficial to crush > > maps that have more than four disks per host, more than four hosts per rack > > etc. It probably is a small win for an OSD or even a client. For crushmap > > validation it helps significantly since the MON are not able to run > > crushtool asynchronously and it needs to run within a few seconds (because > > it blocks the MON). > > > > The implementation is straightforward: it needs sub/xor/lshift/rshift. The > > only relatively tricky part is runtime / compile time detection of the SIMD > > instructions for both Intel and ARM processors. Luckily this has already > > been taken care of when integrating with the jerasure erasure code plugin. > > > > Is there any reason why it would not be good to implement this ? > > > > Cheers > > > > [1] > > https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef > > > -- > To unsubscribe from this list: send the line "unsubscribe ceph-devel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 13:15 ` Mark Nelson 2016-08-29 13:57 ` Sage Weil @ 2016-08-29 13:58 ` Loic Dachary 1 sibling, 0 replies; 11+ messages in thread From: Loic Dachary @ 2016-08-29 13:58 UTC (permalink / raw) To: Mark Nelson, Ceph Development Hi Mark, On 29/08/2016 15:15, Mark Nelson wrote: > Anything we can do to help on the CPU usage front is a win IMHO, though I would be interested in seeing an example where we are spending a lot of time on crush in a real usage scenario? When the ceph setcrushmap sends a new map to the monitor, it runs crushtool --test[1] and blocks until it's done. When the crushmap is large or the rules are complex it can timeout because the monitor won't let it run for longer than the mon lease time. Cheers [1] https://github.com/ceph/ceph/blob/master/src/crush/CrushTester.cc#L361 > Mark > > On 08/29/2016 06:42 AM, Loic Dachary wrote: >> Hi, >> >> TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is straightforward and would help with crushmap validation, is there any reason not to do it ? >> >> When resolving a crush rule (crush_do_rule in mapper.c), the straw2 function (bucket_straw2_choose) calls the hashing function (crush_hash32_3) for each item in a bucket and keeps the best match. When a bucket has four items, the hash function can be run using SIMD instructions. Each item value is 32 bits and four can fit in a __m128i. >> >> I tried to inline the hash function when the conditions are right[1] and run a test to measure the difference. >> >> crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 >> time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 >> rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 >> rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 >> >> With SIMD >> >> real 0m10.433s >> user 0m10.428s >> sys 0m0.000s >> >> Without SIMD >> >> real 0m19.344s >> user 0m19.340s >> sys 0m0.004s >> >> Callgrind estimated cycles for each crush_do_rule are in the same range: >> >> rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 >> kcachegrind crush.callgrind >> >> With SIMD : crush_do_rule is estimated to use 21 205 cycles >> Without SIMD : crush_do_rule is estimated to use 53 068 cycles >> >> This proof of concept relies on instructions that are available on all ARM & Intel processors, nothing complicated is going on. It is beneficial to crush maps that have more than four disks per host, more than four hosts per rack etc. It probably is a small win for an OSD or even a client. For crushmap validation it helps significantly since the MON are not able to run crushtool asynchronously and it needs to run within a few seconds (because it blocks the MON). >> >> The implementation is straightforward: it needs sub/xor/lshift/rshift. The only relatively tricky part is runtime / compile time detection of the SIMD instructions for both Intel and ARM processors. Luckily this has already been taken care of when integrating with the jerasure erasure code plugin. >> >> Is there any reason why it would not be good to implement this ? >> >> Cheers >> >> [1] https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef >> > -- Loïc Dachary, Artisan Logiciel Libre ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 11:42 SIMD accelerated crush_do_rule proof of concept Loic Dachary 2016-08-29 13:15 ` Mark Nelson @ 2016-08-29 13:55 ` Sage Weil 2016-08-29 14:03 ` Loic Dachary 2016-08-29 14:08 ` Vincent JARDIN 2016-08-30 13:24 ` Piotr Dałek 2 siblings, 2 replies; 11+ messages in thread From: Sage Weil @ 2016-08-29 13:55 UTC (permalink / raw) To: Loic Dachary; +Cc: Ceph Development [-- Attachment #1: Type: TEXT/PLAIN, Size: 3513 bytes --] On Mon, 29 Aug 2016, Loic Dachary wrote: > Hi, > > TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is > straightforward and would help with crushmap validation, is there any > reason not to do it ? > > When resolving a crush rule (crush_do_rule in mapper.c), the straw2 > function (bucket_straw2_choose) calls the hashing function > (crush_hash32_3) for each item in a bucket and keeps the best match. > When a bucket has four items, the hash function can be run using SIMD > instructions. Each item value is 32 bits and four can fit in a __m128i. > > I tried to inline the hash function when the conditions are right[1] and > run a test to measure the difference. > > crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 > time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > With SIMD > > real 0m10.433s > user 0m10.428s > sys 0m0.000s > > Without SIMD > > real 0m19.344s > user 0m19.340s > sys 0m0.004s > > Callgrind estimated cycles for each crush_do_rule are in the same range: > > rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 > kcachegrind crush.callgrind > > With SIMD : crush_do_rule is estimated to use 21 205 cycles > Without SIMD : crush_do_rule is estimated to use 53 068 cycles > > This proof of concept relies on instructions that are available on all > ARM & Intel processors, nothing complicated is going on. It is > beneficial to crush maps that have more than four disks per host, more > than four hosts per rack etc. It probably is a small win for an OSD or > even a client. For crushmap validation it helps significantly since the > MON are not able to run crushtool asynchronously and it needs to run > within a few seconds (because it blocks the MON). > > The implementation is straightforward: it needs sub/xor/lshift/rshift. > The only relatively tricky part is runtime / compile time detection of > the SIMD instructions for both Intel and ARM processors. Luckily this > has already been taken care of when integrating with the jerasure > erasure code plugin. > > Is there any reason why it would not be good to implement this ? This is really cool! I agree that the straw2 O(n) calculation on each node is the place to apply this. To answer your question, the only real risk/problem I see is that we need to keep the perfectly in sync with the non-optimized variant since the result has to be deterministic. The maintenance burden is small, I think, since for that reason the code behavior doesn't really change, but we do need to pretty exhaustively verify that the new implementation matches the old one. Perhaps a set of unit tests that compile both variants and feed it randomly sized and weighted straw2 buckets and feed lots of values through? sage > > Cheers > > [1] https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef > > -- > Loïc Dachary, Artisan Logiciel Libre > -- > To unsubscribe from this list: send the line "unsubscribe ceph-devel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 13:55 ` Sage Weil @ 2016-08-29 14:03 ` Loic Dachary 2016-08-29 15:07 ` Ilya Dryomov 2016-08-29 14:08 ` Vincent JARDIN 1 sibling, 1 reply; 11+ messages in thread From: Loic Dachary @ 2016-08-29 14:03 UTC (permalink / raw) To: Sage Weil; +Cc: Ceph Development Hi Sage, On 29/08/2016 15:55, Sage Weil wrote: > On Mon, 29 Aug 2016, Loic Dachary wrote: >> Hi, >> >> TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is >> straightforward and would help with crushmap validation, is there any >> reason not to do it ? >> >> When resolving a crush rule (crush_do_rule in mapper.c), the straw2 >> function (bucket_straw2_choose) calls the hashing function >> (crush_hash32_3) for each item in a bucket and keeps the best match. >> When a bucket has four items, the hash function can be run using SIMD >> instructions. Each item value is 32 bits and four can fit in a __m128i. >> >> I tried to inline the hash function when the conditions are right[1] and >> run a test to measure the difference. >> >> crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 >> time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 >> rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 >> rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 >> >> With SIMD >> >> real 0m10.433s >> user 0m10.428s >> sys 0m0.000s >> >> Without SIMD >> >> real 0m19.344s >> user 0m19.340s >> sys 0m0.004s >> >> Callgrind estimated cycles for each crush_do_rule are in the same range: >> >> rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 >> kcachegrind crush.callgrind >> >> With SIMD : crush_do_rule is estimated to use 21 205 cycles >> Without SIMD : crush_do_rule is estimated to use 53 068 cycles >> >> This proof of concept relies on instructions that are available on all >> ARM & Intel processors, nothing complicated is going on. It is >> beneficial to crush maps that have more than four disks per host, more >> than four hosts per rack etc. It probably is a small win for an OSD or >> even a client. For crushmap validation it helps significantly since the >> MON are not able to run crushtool asynchronously and it needs to run >> within a few seconds (because it blocks the MON). >> >> The implementation is straightforward: it needs sub/xor/lshift/rshift. >> The only relatively tricky part is runtime / compile time detection of >> the SIMD instructions for both Intel and ARM processors. Luckily this >> has already been taken care of when integrating with the jerasure >> erasure code plugin. >> >> Is there any reason why it would not be good to implement this ? > > This is really cool! I agree that the straw2 O(n) calculation on each > node is the place to apply this. > > To answer your question, the only real risk/problem I see is that we need > to keep the perfectly in sync with the non-optimized variant since the > result has to be deterministic. The maintenance burden is small, I think, > since for that reason the code behavior doesn't really change, but we do > need to pretty exhaustively verify that the new implementation matches the > old one. Perhaps a set of unit tests that compile both variants and feed > it randomly sized and weighted straw2 buckets and feed lots of values > through? Right: the implementation is likely to be simple but it needs serious testing. I'll give it a try. Cheers > sage > >> >> Cheers >> >> [1] https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef >> >> -- >> Loïc Dachary, Artisan Logiciel Libre >> -- >> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html >> -- Loïc Dachary, Artisan Logiciel Libre ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 14:03 ` Loic Dachary @ 2016-08-29 15:07 ` Ilya Dryomov 0 siblings, 0 replies; 11+ messages in thread From: Ilya Dryomov @ 2016-08-29 15:07 UTC (permalink / raw) To: Loic Dachary; +Cc: Sage Weil, Ceph Development On Mon, Aug 29, 2016 at 4:03 PM, Loic Dachary <loic@dachary.org> wrote: > Hi Sage, > > On 29/08/2016 15:55, Sage Weil wrote: >> On Mon, 29 Aug 2016, Loic Dachary wrote: >>> Hi, >>> >>> TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is >>> straightforward and would help with crushmap validation, is there any >>> reason not to do it ? >>> >>> When resolving a crush rule (crush_do_rule in mapper.c), the straw2 >>> function (bucket_straw2_choose) calls the hashing function >>> (crush_hash32_3) for each item in a bucket and keeps the best match. >>> When a bucket has four items, the hash function can be run using SIMD >>> instructions. Each item value is 32 bits and four can fit in a __m128i. >>> >>> I tried to inline the hash function when the conditions are right[1] and >>> run a test to measure the difference. >>> >>> crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 >>> time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 >>> rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 >>> rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 >>> >>> With SIMD >>> >>> real 0m10.433s >>> user 0m10.428s >>> sys 0m0.000s >>> >>> Without SIMD >>> >>> real 0m19.344s >>> user 0m19.340s >>> sys 0m0.004s >>> >>> Callgrind estimated cycles for each crush_do_rule are in the same range: >>> >>> rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 >>> kcachegrind crush.callgrind >>> >>> With SIMD : crush_do_rule is estimated to use 21 205 cycles >>> Without SIMD : crush_do_rule is estimated to use 53 068 cycles >>> >>> This proof of concept relies on instructions that are available on all >>> ARM & Intel processors, nothing complicated is going on. It is >>> beneficial to crush maps that have more than four disks per host, more >>> than four hosts per rack etc. It probably is a small win for an OSD or >>> even a client. For crushmap validation it helps significantly since the >>> MON are not able to run crushtool asynchronously and it needs to run >>> within a few seconds (because it blocks the MON). >>> >>> The implementation is straightforward: it needs sub/xor/lshift/rshift. >>> The only relatively tricky part is runtime / compile time detection of >>> the SIMD instructions for both Intel and ARM processors. Luckily this >>> has already been taken care of when integrating with the jerasure >>> erasure code plugin. >>> >>> Is there any reason why it would not be good to implement this ? >> >> This is really cool! I agree that the straw2 O(n) calculation on each >> node is the place to apply this. >> >> To answer your question, the only real risk/problem I see is that we need >> to keep the perfectly in sync with the non-optimized variant since the >> result has to be deterministic. The maintenance burden is small, I think, >> since for that reason the code behavior doesn't really change, but we do >> need to pretty exhaustively verify that the new implementation matches the >> old one. Perhaps a set of unit tests that compile both variants and feed >> it randomly sized and weighted straw2 buckets and feed lots of values >> through? > > Right: the implementation is likely to be simple but it needs serious testing. I'll give it a try. We should also make sure it's neatly hidden under !__KERNEL__. Since the benefit to the clients is assumed to be minimal it's probably not worth bothering with bringing SSE into the kernel implementation. (However, if it turns out otherwise we can look into it - IIRC raid6 in-kernel library is using SSE, and probably some crypto code too.) Thanks, Ilya ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 13:55 ` Sage Weil 2016-08-29 14:03 ` Loic Dachary @ 2016-08-29 14:08 ` Vincent JARDIN 2016-08-29 14:54 ` Loic Dachary 1 sibling, 1 reply; 11+ messages in thread From: Vincent JARDIN @ 2016-08-29 14:08 UTC (permalink / raw) To: Sage Weil, Loic Dachary; +Cc: Ceph Development Le 29/08/2016 à 15:55, Sage Weil a écrit : > To answer your question, the only real risk/problem I see is that we need > to keep the perfectly in sync with the non-optimized variant I do propose a generic implementation that allows to share SIMD on ARM, Intel and others (Altivec), https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef You can try the following, For instance, #include <stdint.h> #include <immintrin.h> { __v32qi va, vb; va = (__v32qi) { 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 4, 1, 0 }; vb = (__v32qi) { 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; __v32qi res = va ^ vb; } it will produce the optimized Neon or AVX, AVX2 according to each targets. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 14:08 ` Vincent JARDIN @ 2016-08-29 14:54 ` Loic Dachary 0 siblings, 0 replies; 11+ messages in thread From: Loic Dachary @ 2016-08-29 14:54 UTC (permalink / raw) To: Vincent JARDIN; +Cc: Ceph Development Hi Vincent, On 29/08/2016 16:08, Vincent JARDIN wrote: > Le 29/08/2016 à 15:55, Sage Weil a écrit : >> To answer your question, the only real risk/problem I see is that we need >> to keep the perfectly in sync with the non-optimized variant > > I do propose a generic implementation that allows to share SIMD on ARM, Intel and others (Altivec), > > > https://github.com/dachary/ceph/commit/71ae4584d9ed57f70aad718d0ffe206a01e91fef > > You can try the following, > For instance, > #include <stdint.h> > #include <immintrin.h> > { > __v32qi va, vb; > va = (__v32qi) { 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 4, 1, 0 }; > vb = (__v32qi) { 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; > > __v32qi res = va ^ vb; > } > > it will produce the optimized Neon or AVX, AVX2 according to each targets. Generic code that relies on the compiler optimizations is terse, which is nice. But the code is not generic: it needs to be written specifically for the optimizer, which is self defeating. The http://locklessinc.com/articles/vectorize/ article illustrate that in a fun way. Instead of maintaining code with SIMD instructions, you need to understand each optimizer by reading assembly language, which is more complicated. Cheers -- Loïc Dachary, Artisan Logiciel Libre ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-29 11:42 SIMD accelerated crush_do_rule proof of concept Loic Dachary 2016-08-29 13:15 ` Mark Nelson 2016-08-29 13:55 ` Sage Weil @ 2016-08-30 13:24 ` Piotr Dałek 2016-08-30 16:53 ` Sage Weil 2 siblings, 1 reply; 11+ messages in thread From: Piotr Dałek @ 2016-08-30 13:24 UTC (permalink / raw) To: Loic Dachary; +Cc: Ceph Development On Mon, Aug 29, 2016 at 01:42:22PM +0200, Loic Dachary wrote: > Hi, > > TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is straightforward and would help with crushmap validation, is there any reason not to do it ? > > When resolving a crush rule (crush_do_rule in mapper.c), the straw2 function (bucket_straw2_choose) calls the hashing function (crush_hash32_3) for each item in a bucket and keeps the best match. When a bucket has four items, the hash function can be run using SIMD instructions. Each item value is 32 bits and four can fit in a __m128i. > > I tried to inline the hash function when the conditions are right[1] and run a test to measure the difference. > > crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 > time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > With SIMD > > real 0m10.433s > user 0m10.428s > sys 0m0.000s > > Without SIMD > > real 0m19.344s > user 0m19.340s > sys 0m0.004s > > Callgrind estimated cycles for each crush_do_rule are in the same range: > > rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 > kcachegrind crush.callgrind > > With SIMD : crush_do_rule is estimated to use 21 205 cycles > Without SIMD : crush_do_rule is estimated to use 53 068 cycles > > This proof of concept relies on instructions that are available on all ARM & Intel processors, nothing complicated is going on. It is beneficial to crush maps that have more than four disks per host, more than four hosts per rack etc. It probably is a small win for an OSD or even a client. For crushmap validation it helps significantly since the MON are not able to run crushtool asynchronously and it needs to run within a few seconds (because it blocks the MON). > > The implementation is straightforward: it needs sub/xor/lshift/rshift. The only relatively tricky part is runtime / compile time detection of the SIMD instructions for both Intel and ARM processors. Luckily this has already been taken care of when integrating with the jerasure erasure code plugin. > > Is there any reason why it would not be good to implement this ? I like this and I hope you'll be able to get it into master. I was wondering if straw2 can be optimized further and realized that crush_ln() looks quite expensive. I did some basic statistics and it looks like that crush_ln accepts inputs in 0-65535 range, making lookup table solution feasible. I tried it out: Original: [branch@localhost bin]$ time ./crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 real 0m25.635s user 0m25.553s sys 0m0.072s Loic's SIMD: [branch@localhost bin]$ time ./crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 real 0m15.292s user 0m15.227s sys 0m0.056s +Crush LN cache: [branch@localhost bin]$ time ./crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 real 0m11.828s user 0m11.746s sys 0m0.078s There's a drawback, too - 65536 of__u64's take 512KB of ram, so the question is - is it worth it... -- Piotr Dałek branch@predictor.org.pl http://blog.predictor.org.pl ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: SIMD accelerated crush_do_rule proof of concept 2016-08-30 13:24 ` Piotr Dałek @ 2016-08-30 16:53 ` Sage Weil 0 siblings, 0 replies; 11+ messages in thread From: Sage Weil @ 2016-08-30 16:53 UTC (permalink / raw) To: Piotr Dałek; +Cc: Loic Dachary, Ceph Development [-- Attachment #1: Type: TEXT/PLAIN, Size: 6451 bytes --] On Tue, 30 Aug 2016, Piotr Dałek wrote: > On Mon, Aug 29, 2016 at 01:42:22PM +0200, Loic Dachary wrote: > > Hi, > > > > TL;DR: crush_do_rule using SIMD goes twice faster, the implementation is straightforward and would help with crushmap validation, is there any reason not to do it ? > > > > When resolving a crush rule (crush_do_rule in mapper.c), the straw2 function (bucket_straw2_choose) calls the hashing function (crush_hash32_3) for each item in a bucket and keeps the best match. When a bucket has four items, the hash function can be run using SIMD instructions. Each item value is 32 bits and four can fit in a __m128i. > > > > I tried to inline the hash function when the conditions are right[1] and run a test to measure the difference. > > > > crushtool -o /tmp/t.map --num_osds 1024 --build node straw2 8 datacenter straw2 4 root straw2 0 > > time crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > > > With SIMD > > > > real 0m10.433s > > user 0m10.428s > > sys 0m0.000s > > > > Without SIMD > > > > real 0m19.344s > > user 0m19.340s > > sys 0m0.004s > > > > Callgrind estimated cycles for each crush_do_rule are in the same range: > > > > rm crush.callgrind ; valgrind --tool=callgrind --callgrind-out-file=crush.callgrind crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 204800 --num-rep 4 > > kcachegrind crush.callgrind > > > > With SIMD : crush_do_rule is estimated to use 21 205 cycles > > Without SIMD : crush_do_rule is estimated to use 53 068 cycles > > > > This proof of concept relies on instructions that are available on all ARM & Intel processors, nothing complicated is going on. It is beneficial to crush maps that have more than four disks per host, more than four hosts per rack etc. It probably is a small win for an OSD or even a client. For crushmap validation it helps significantly since the MON are not able to run crushtool asynchronously and it needs to run within a few seconds (because it blocks the MON). > > > > The implementation is straightforward: it needs sub/xor/lshift/rshift. The only relatively tricky part is runtime / compile time detection of the SIMD instructions for both Intel and ARM processors. Luckily this has already been taken care of when integrating with the jerasure erasure code plugin. > > > > Is there any reason why it would not be good to implement this ? > > I like this and I hope you'll be able to get it into master. I was wondering > if straw2 can be optimized further and realized that crush_ln() looks quite > expensive. I did some basic statistics and it looks like that crush_ln > accepts inputs in 0-65535 range, making lookup table solution feasible. > I tried it out: > > Original: > > [branch@localhost bin]$ time ./crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > real 0m25.635s > user 0m25.553s > sys 0m0.072s > > Loic's SIMD: > > [branch@localhost bin]$ time ./crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > real 0m15.292s > user 0m15.227s > sys 0m0.056s > > +Crush LN cache: > > [branch@localhost bin]$ time ./crushtool -i /tmp/t.map --show-bad-mappings --show-statistics --test --rule 0 --min-x 1 --max-x 2048000 --num-rep 4 > rule 0 (replicated_ruleset), x = 1..2048000, numrep = 4..4 > rule 0 (replicated_ruleset) num_rep 4 result size == 4: 2048000/2048000 > > real 0m11.828s > user 0m11.746s > sys 0m0.078s > > There's a drawback, too - 65536 of__u64's take 512KB of ram, so the question is > - is it worth it... I think it probably isn't, since we'll miss the CPU cache and have to fetch from DRAM. But we have a pretty long discussion about this here http://www.spinics.net/lists/ceph-devel/msg21635.html and then Xiaoxi at Intel came up with the current implementation (replacing my original lookup table). http://www.spinics.net/lists/ceph-devel/msg22094.html I don't remember the specifics unfortunately... sage ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2016-08-30 16:53 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-08-29 11:42 SIMD accelerated crush_do_rule proof of concept Loic Dachary 2016-08-29 13:15 ` Mark Nelson 2016-08-29 13:57 ` Sage Weil 2016-08-29 13:58 ` Loic Dachary 2016-08-29 13:55 ` Sage Weil 2016-08-29 14:03 ` Loic Dachary 2016-08-29 15:07 ` Ilya Dryomov 2016-08-29 14:08 ` Vincent JARDIN 2016-08-29 14:54 ` Loic Dachary 2016-08-30 13:24 ` Piotr Dałek 2016-08-30 16:53 ` Sage Weil
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.