Message ID | 20160607234506.32160.qmail@ns.sciencehorizons.net (mailing list archive) |
---|---|
State | Accepted, archived |
Delegated to: | Helge Deller |
Headers | show |
Hi George, On 08.06.2016 01:45, George Spelvin wrote: > PA-RISC is interesting; integer multiplies are implemented in the > FPU, so are painful in the kernel. But it tries to be friendly to > shift-and-add sequences for constant multiplies. > > __hash_32 is implemented using the same shift-and-add sequence as > Microblaze, just scheduled for the PA7100. (It's 2-way superscalar > but in-order, like the Pentium.) > > hash_64 was tricky, but a suggestion from Jason Thong allowed a > good solution by breaking up the multiplier. After a lot of manual > optimization, I found a 19-instruction sequence for the multiply that > can be executed in 10 cycles using only 4 temporaries. > > (The PA8xxx can issue 4 instructions per cycle, but 2 must be ALU ops > and 2 must be loads/stores. And the final add can't be paired.) > > An alternative considered, but ultimately not used, was Thomas Wang's > 64-to-32-bit integer hash. At 12 instructions, it's smaller, but they're > all sequentially dependent, so it has longer latency. > > https://web.archive.org/web/2011/http://www.concentric.net/~Ttwang/tech/inthash.htm > http://burtleburtle.net/bob/hash/integer.html > > Signed-off-by: George Spelvin <linux@sciencehorizons.net> > Cc: Helge Deller <deller@gmx.de> > Cc: linux-parisc@vger.kernel.org > --- > No functional change, just cleaned up a lot. This is final if no problems > are found. Thanks, but two minor issues... > arch/parisc/Kconfig | 1 + > arch/parisc/include/asm/hash.h | 146 +++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 147 insertions(+) > create mode 100644 arch/parisc/include/asm/hash.h > > diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig > index 88cfaa8a..8ed2a444 100644 > --- a/arch/parisc/Kconfig > +++ b/arch/parisc/Kconfig > @@ -30,6 +30,7 @@ config PARISC > select TTY # Needed for pdc_cons.c > select HAVE_DEBUG_STACKOVERFLOW > select HAVE_ARCH_AUDITSYSCALL > + select HAVE_ARCH_HASH > select HAVE_ARCH_SECCOMP_FILTER > select ARCH_NO_COHERENT_DMA_MMAP Minor issue: This line (ARCH_NO_COHERENT) is not yet in Linus' tree. I assume you diff'ed against -next or something? Maybe you move the "select HAVE_ARCH_HASH" one or two lines up, or wait until the other patch went upstream? > diff --git a/arch/parisc/include/asm/hash.h b/arch/parisc/include/asm/hash.h > new file mode 100644 > index 00000000..fb992a3b > --- /dev/null > +++ b/arch/parisc/include/asm/hash.h > @@ -0,0 +1,146 @@ > +#ifndef _ASM_HASH_H > +#define _ASM_HASH_H > + > +/* > + * HP-PA only implements integer multiply in the FPU. However, for > + * integer multiplies by constant, it has a number of shift-and-add > + * (but no shift-and-subtract, sigh!) instructions that a compiler > + * can synthesize a code sequence with. > + * > + * Unfortunately, GCC isn't very efficient at using them. For example > + * it uses three instructions for "x *= 21" when only two are needed. > + * But we can find a sequence manually. > + */ > + > +#define HAVE_ARCH__HASH_32 1 > + > +/* > + * This is a multiply by GOLDEN_RATIO_32 = 0x61C88647 optimized for the > + * PA7100 pairing rules. This is an in-order 2-way superscalar processor. > + * Only one instruction in a pair may be a shift (by more than 3 bits), > + * but other than that, simple ALU ops (including shift-and-add by up > + * to 3 bits) may be paired arbitrarily. > + * > + * PA8xxx processors also dual-issue ALU instructions, although with > + * fewer constraints, so this schedule is good for them, too. > + * > + * This 6-step sequence was found by Yevgen Voronenko's implementation > + * of the Hcub algorithm at http://spiral.ece.cmu.edu/mcm/gen.html. > + */ > +static inline u32 __attribute_const__ __hash_32(u32 x) > +{ > + u32 a, b, c; > + > + /* > + * Phase 1: Compute a = (x << 19) + x, > + * b = (x << 9) + a, c = (x << 23) + b. > + */ > + a = x << 19; /* Two shifts can't be paired */ > + b = x << 9; a += x; > + c = x << 23; b += a; > + c += b; > + /* Phase 2: Return (b<<11) + (c<<6) + (a<<3) - c */ > + b <<= 11; > + a += c << 3; b -= c; > + return (a << 3) + b; > +} > + > +#if BITS_PER_LONG == 64 > + > +#define HAVE_ARCH_HASH_64 1 > + > +/* > + * Finding a good shift-and-add chain for GOLDEN_RATIO_64 is tricky, > + * because available software for the purpose chokes on constants this > + * large. (It's mostly designed for compiling FIR filter coefficients > + * into FPGAs.) > + * > + * However, Jason Thong pointed out a work-around. The Hcub software > + * (http://spiral.ece.cmu.edu/mcm/gen.html) is designed for *multiple* > + * constant multiplication, and is good at finding shift-and-add chains > + * which share common terms. > + * > + * Looking at 0x0x61C8864680B583EB in binary: > + * 0110000111001000100001100100011010000000101101011000001111101011 > + * \______________/ \__________/ \_______/ \________/ > + * \____________________________/ \____________________/ > + * you can see the non-zero bits are divided into several well-separated > + * blocks. Hcub can find algorithms for those terms separately, which > + * can then be shifted and added together. > + * > + * Dividing the input into 2, 3 or 4 blocks, Hcub can find solutions > + * with 10, 9 or 8 adds, respectively, making a total of 11 for the > + * whole number. > + * > + * Using just two large blocks, 0xC3910C8D << 31 in the high bits, > + * and 0xB583EB in the low bits, produces as good an algorithm as any, > + * and with one more small shift than alternatives. > + * > + * The high bits are a larger number and more work to compute, as well > + * as needing one extra cycle to shift left 31 bits before the final > + * addition, so they are the critical path for scheduling. The low bits > + * can fit into the scheduling slots left over. > + */ > + > + > +/* > + * This _ASSIGN(dst, src) macro performs "dst = src", but prevents GCC > + * from inferring anything about the value assigned to "dest". > + * > + * This prevents it from mis-optimizing certain sequences. > + * In particular, gcc is annoyingly eager to combine consecutive shifts. > + * Given "x <<= 19; y += x; z += x << 1;", GCC will turn this into > + * "y += x << 19; z += x << 20;" even though the latter sequence needs > + * an additional instruction and temporary register. > + * > + * Because no actual assembly code is generated, this construct is > + * usefully portable across all GCC platforms, and so can be test-compiled > + * on non-PA systems. > + * > + * In two places, additional unused input dependencies are added. This > + * forces GCC's scheduling so it does not rearrange instructions too much. > + * Because the PA-8xxx is out of order, I'm not sure how much this matters, > + * but why make it more difficult for the processor than necessary? > + */ > +#define _ASSIGN(dst, src, ...) asm("" : "=r" (dst) : "0" (src), ##__VA_ARGS__) > + > +/* > + * Multiply by GOLDEN_RATIO_64 = 0x0x61C8864680B583EB using a heavily > + * optimized shift-and-add sequence. > + * > + * Without the final shift, the multiply proper is 19 instructions, > + * 10 cycles and uses only 4 temporaries. Whew! > + * > + * You are not expected to understand this. > + */ > +static __always_inline u32 __attribute_const__ > +hash_64(u64 a, unsigned int bits) > +{ > + u64 b, c, d; > + > + /* > + * Encourage GCC to move a dynamic shift to %sar early, > + * thereby freeing up an additional temporary register. > + */ > + if (!__builtin_constant_p(bits)) > + asm("" : "=q" (bits) : "0" (64 - bits)); > + else > + bits = 64 - bits; > + > + _ASSIGN(b, a*5); c = a << 13; > + b = (b << 2) + a; _ASSIGN(d, a << 17); > + a = b + (a << 1); c += d; > + d = a << 10; _ASSIGN(a, a << 19); > + d = a - d; _ASSIGN(a, a << 4, "X" (d)); > + c += b; a += b; > + d -= c; c += a << 1; > + a += c << 3; _ASSIGN(b, b << 7+31, "X" (c), "X" (d)); This line produces compiler warnings: arch/parisc/include/asm/hash.h: In function ‘hash_64’: arch/parisc/include/asm/hash.h:137:29: warning: suggest parentheses around ‘+’ inside ‘<<’ [-Wparentheses] a += c << 3; _ASSIGN(b, b << 7+31, "X" (c), "X" (d)); ^ You should add () around 7+31. With those changes I sucessfully tested it on a 64bit parisc kernel. So, please add Acked-by: Helge Deller <deller@gmx.de> THANKS! Helge > + a <<= 31; b += d; > + a += b; > + return a >> bits; > +} > +#undef _ASSIGN /* We're a widely-used header file, so don't litter! */ > + > +#endif /* BITS_PER_LONG == 64 */ > + > +#endif /* _ASM_HASH_H */ > -- To unsubscribe from this list: send the line "unsubscribe linux-parisc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Helge Deller wrote: > Minor issue: > This line (ARCH_NO_COHERENT) is not yet in Linus' tree. > I assume you diff'ed against -next or something? > Maybe you move the "select HAVE_ARCH_HASH" one or two lines up, or > wait until the other patch went upstream? No, I'm working against v4.6, and the line is there. Line 34 of "git show v4.6:arch/parisc/Kconfig". You pulled my git tree, so you can see what I'm working on top of. >> + a += c << 3; _ASSIGN(b, b << 7+31, "X" (c), "X" (d)); > > This line produces compiler warnings: Thanks for catching that. > So, please add > Acked-by: Helge Deller <deller@gmx.de> Done and pushed out to git://ftp.sciencehorizons.net/linux.git hash Would you like to take it via the PA-RISC tree? I don't think a "performance fix" like this is 4.7-rc material (I think it's low-risk, so I don't object, but I think Linus would) so it has to wait for 4.8 either way. > THANKS! You're very welcome. I really wanted to improve it for *every* platform, so thank you for your help. I still haven't heard from the Microblaze guys. Getting it down to 10 cycles was a fun micro-optimization challenge. I'm just annoyed I wasted to much time optimizing for a misunderstanding of the PA-8800 pipeline. The large print ("QUAD-ISSUE!") giveth, and the small print taketh waway. -- To unsubscribe from this list: send the line "unsubscribe linux-parisc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 10.06.2016 05:23, George Spelvin wrote: > Helge Deller wrote: >>> + a += c << 3; _ASSIGN(b, b << 7+31, "X" (c), "X" (d)); >> >> This line produces compiler warnings: > > Thanks for catching that. > >> So, please add >> Acked-by: Helge Deller <deller@gmx.de> > > Done and pushed out to git://ftp.sciencehorizons.net/linux.git hash > > Would you like to take it via the PA-RISC tree? Yes, I will take it through the parisc tree. Helge -- To unsubscribe from this list: send the line "unsubscribe linux-parisc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 88cfaa8a..8ed2a444 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -30,6 +30,7 @@ config PARISC select TTY # Needed for pdc_cons.c select HAVE_DEBUG_STACKOVERFLOW select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_HASH select HAVE_ARCH_SECCOMP_FILTER select ARCH_NO_COHERENT_DMA_MMAP diff --git a/arch/parisc/include/asm/hash.h b/arch/parisc/include/asm/hash.h new file mode 100644 index 00000000..fb992a3b --- /dev/null +++ b/arch/parisc/include/asm/hash.h @@ -0,0 +1,146 @@ +#ifndef _ASM_HASH_H +#define _ASM_HASH_H + +/* + * HP-PA only implements integer multiply in the FPU. However, for + * integer multiplies by constant, it has a number of shift-and-add + * (but no shift-and-subtract, sigh!) instructions that a compiler + * can synthesize a code sequence with. + * + * Unfortunately, GCC isn't very efficient at using them. For example + * it uses three instructions for "x *= 21" when only two are needed. + * But we can find a sequence manually. + */ + +#define HAVE_ARCH__HASH_32 1 + +/* + * This is a multiply by GOLDEN_RATIO_32 = 0x61C88647 optimized for the + * PA7100 pairing rules. This is an in-order 2-way superscalar processor. + * Only one instruction in a pair may be a shift (by more than 3 bits), + * but other than that, simple ALU ops (including shift-and-add by up + * to 3 bits) may be paired arbitrarily. + * + * PA8xxx processors also dual-issue ALU instructions, although with + * fewer constraints, so this schedule is good for them, too. + * + * This 6-step sequence was found by Yevgen Voronenko's implementation + * of the Hcub algorithm at http://spiral.ece.cmu.edu/mcm/gen.html. + */ +static inline u32 __attribute_const__ __hash_32(u32 x) +{ + u32 a, b, c; + + /* + * Phase 1: Compute a = (x << 19) + x, + * b = (x << 9) + a, c = (x << 23) + b. + */ + a = x << 19; /* Two shifts can't be paired */ + b = x << 9; a += x; + c = x << 23; b += a; + c += b; + /* Phase 2: Return (b<<11) + (c<<6) + (a<<3) - c */ + b <<= 11; + a += c << 3; b -= c; + return (a << 3) + b; +} + +#if BITS_PER_LONG == 64 + +#define HAVE_ARCH_HASH_64 1 + +/* + * Finding a good shift-and-add chain for GOLDEN_RATIO_64 is tricky, + * because available software for the purpose chokes on constants this + * large. (It's mostly designed for compiling FIR filter coefficients + * into FPGAs.) + * + * However, Jason Thong pointed out a work-around. The Hcub software + * (http://spiral.ece.cmu.edu/mcm/gen.html) is designed for *multiple* + * constant multiplication, and is good at finding shift-and-add chains + * which share common terms. + * + * Looking at 0x0x61C8864680B583EB in binary: + * 0110000111001000100001100100011010000000101101011000001111101011 + * \______________/ \__________/ \_______/ \________/ + * \____________________________/ \____________________/ + * you can see the non-zero bits are divided into several well-separated + * blocks. Hcub can find algorithms for those terms separately, which + * can then be shifted and added together. + * + * Dividing the input into 2, 3 or 4 blocks, Hcub can find solutions + * with 10, 9 or 8 adds, respectively, making a total of 11 for the + * whole number. + * + * Using just two large blocks, 0xC3910C8D << 31 in the high bits, + * and 0xB583EB in the low bits, produces as good an algorithm as any, + * and with one more small shift than alternatives. + * + * The high bits are a larger number and more work to compute, as well + * as needing one extra cycle to shift left 31 bits before the final + * addition, so they are the critical path for scheduling. The low bits + * can fit into the scheduling slots left over. + */ + + +/* + * This _ASSIGN(dst, src) macro performs "dst = src", but prevents GCC + * from inferring anything about the value assigned to "dest". + * + * This prevents it from mis-optimizing certain sequences. + * In particular, gcc is annoyingly eager to combine consecutive shifts. + * Given "x <<= 19; y += x; z += x << 1;", GCC will turn this into + * "y += x << 19; z += x << 20;" even though the latter sequence needs + * an additional instruction and temporary register. + * + * Because no actual assembly code is generated, this construct is + * usefully portable across all GCC platforms, and so can be test-compiled + * on non-PA systems. + * + * In two places, additional unused input dependencies are added. This + * forces GCC's scheduling so it does not rearrange instructions too much. + * Because the PA-8xxx is out of order, I'm not sure how much this matters, + * but why make it more difficult for the processor than necessary? + */ +#define _ASSIGN(dst, src, ...) asm("" : "=r" (dst) : "0" (src), ##__VA_ARGS__) + +/* + * Multiply by GOLDEN_RATIO_64 = 0x0x61C8864680B583EB using a heavily + * optimized shift-and-add sequence. + * + * Without the final shift, the multiply proper is 19 instructions, + * 10 cycles and uses only 4 temporaries. Whew! + * + * You are not expected to understand this. + */ +static __always_inline u32 __attribute_const__ +hash_64(u64 a, unsigned int bits) +{ + u64 b, c, d; + + /* + * Encourage GCC to move a dynamic shift to %sar early, + * thereby freeing up an additional temporary register. + */ + if (!__builtin_constant_p(bits)) + asm("" : "=q" (bits) : "0" (64 - bits)); + else + bits = 64 - bits; + + _ASSIGN(b, a*5); c = a << 13; + b = (b << 2) + a; _ASSIGN(d, a << 17); + a = b + (a << 1); c += d; + d = a << 10; _ASSIGN(a, a << 19); + d = a - d; _ASSIGN(a, a << 4, "X" (d)); + c += b; a += b; + d -= c; c += a << 1; + a += c << 3; _ASSIGN(b, b << 7+31, "X" (c), "X" (d)); + a <<= 31; b += d; + a += b; + return a >> bits; +} +#undef _ASSIGN /* We're a widely-used header file, so don't litter! */ + +#endif /* BITS_PER_LONG == 64 */ + +#endif /* _ASM_HASH_H */
PA-RISC is interesting; integer multiplies are implemented in the FPU, so are painful in the kernel. But it tries to be friendly to shift-and-add sequences for constant multiplies. __hash_32 is implemented using the same shift-and-add sequence as Microblaze, just scheduled for the PA7100. (It's 2-way superscalar but in-order, like the Pentium.) hash_64 was tricky, but a suggestion from Jason Thong allowed a good solution by breaking up the multiplier. After a lot of manual optimization, I found a 19-instruction sequence for the multiply that can be executed in 10 cycles using only 4 temporaries. (The PA8xxx can issue 4 instructions per cycle, but 2 must be ALU ops and 2 must be loads/stores. And the final add can't be paired.) An alternative considered, but ultimately not used, was Thomas Wang's 64-to-32-bit integer hash. At 12 instructions, it's smaller, but they're all sequentially dependent, so it has longer latency. https://web.archive.org/web/2011/http://www.concentric.net/~Ttwang/tech/inthash.htm http://burtleburtle.net/bob/hash/integer.html Signed-off-by: George Spelvin <linux@sciencehorizons.net> Cc: Helge Deller <deller@gmx.de> Cc: linux-parisc@vger.kernel.org --- No functional change, just cleaned up a lot. This is final if no problems are found. arch/parisc/Kconfig | 1 + arch/parisc/include/asm/hash.h | 146 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 arch/parisc/include/asm/hash.h