Added lfs_parity intrinsic
We're using parity a lot more than popc now (actually, now that we don't use CTZ skip-lists, do we use popc at all?), so it makes sense to the compiler's __builtin_parity intrinsic when possible. On some processors parity can be much cheaper than popc. Notably, the 8080 family just includes a parity flag in the set of carry flags that are implicitly updated on most ALU operations. Though I think this approach didn't scale, you don't really see parity flags on most >8-bit architectures... Unfortunately, ARM thumb, our test arch, does not have a popc or parity instruction. I guess because thanks to implicit shifts in most instructions, the tree-reduction solution is surprisingly cheap: ea80 4010 eor.w r0, r0, r0, lsr #16 ea80 2010 eor.w r0, r0, r0, lsr #8 ea80 1010 eor.w r0, r0, r0, lsr #4 ea80 00c0 eor.w r0, r0, r0, lsr #2 ea80 0050 eor.w r0, r0, r0, lsr #1 f000 0001 and.w r0, r0, #1 Both popc and parity benefit from this (GCC 11): code __popcountsi2: 40 __paritysi2: 32 (-20.0%) So, thumb is not an arch where we see much benefit: code stack before: 33908 2824 after: 33924 (+0.0%) 2824 (+0.0%) Not really sure where the +16 bytes come from, we removed several masks, so I guess it's just bool vs in compiler noise? Still, this may be useful for other archs with parity instructions/ hardware.
This commit is contained in:
@@ -2223,7 +2223,7 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
|
||||
// temporary state until we validate a cksum
|
||||
uint32_t cksum_ = cksum;
|
||||
bool parity_ = lfs_popc(cksum) & 1;
|
||||
bool parity_ = lfs_parity(cksum);
|
||||
lfs_size_t off = sizeof(uint32_t);
|
||||
lfs_size_t trunk_ = 0;
|
||||
lfs_size_t trunk__ = 0;
|
||||
@@ -2255,7 +2255,7 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
break;
|
||||
}
|
||||
tag &= 0x7fff;
|
||||
parity_ ^= lfs_popc(cksum_ ^ cksum__) & 1;
|
||||
parity_ ^= lfs_parity(cksum_ ^ cksum__);
|
||||
cksum_ = cksum__;
|
||||
|
||||
// tag goes out of range?
|
||||
@@ -2277,7 +2277,7 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
}
|
||||
return err;
|
||||
}
|
||||
parity_ ^= lfs_popc(cksum_ ^ cksum__) & 1;
|
||||
parity_ ^= lfs_parity(cksum_ ^ cksum__);
|
||||
cksum_ = cksum__;
|
||||
|
||||
// found an ecksum? save for later
|
||||
@@ -2631,7 +2631,7 @@ static int lfsr_rbyd_appendrev(lfs_t *lfs, lfsr_rbyd_t *rbyd, uint32_t rev) {
|
||||
|
||||
// update eoff, xor cksum parity
|
||||
rbyd->eoff
|
||||
+= ((lfs_popc(rbyd->cksum ^ cksum_) & 1)
|
||||
+= ((lfs_size_t)lfs_parity(rbyd->cksum ^ cksum_)
|
||||
<< (8*sizeof(lfs_size_t)-1))
|
||||
+ sizeof(uint32_t);
|
||||
rbyd->cksum = cksum_;
|
||||
@@ -2655,7 +2655,7 @@ static int lfsr_rbyd_appendtag(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
|
||||
// update eoff, xor cksum parity
|
||||
rbyd->eoff
|
||||
+= ((lfs_popc(rbyd->cksum ^ cksum_) & 1)
|
||||
+= ((lfs_size_t)lfs_parity(rbyd->cksum ^ cksum_)
|
||||
<< (8*sizeof(lfs_size_t)-1))
|
||||
+ d;
|
||||
rbyd->cksum = cksum_;
|
||||
@@ -2674,7 +2674,7 @@ static int lfsr_rbyd_appenddata(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
|
||||
// update eoff, xor cksum parity
|
||||
rbyd->eoff
|
||||
+= ((lfs_popc(rbyd->cksum ^ cksum_) & 1)
|
||||
+= ((lfs_size_t)lfs_parity(rbyd->cksum ^ cksum_)
|
||||
<< (8*sizeof(lfs_size_t)-1))
|
||||
+ lfsr_data_size(data);
|
||||
rbyd->cksum = cksum_;
|
||||
@@ -3506,8 +3506,7 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
|
||||
// xor in the tag parity
|
||||
cksum_buf[0] ^= (uint8_t)lfsr_rbyd_parity(rbyd) << 7;
|
||||
// find the new parity
|
||||
bool parity_ = lfsr_rbyd_parity(rbyd)
|
||||
^ (lfs_popc(rbyd->cksum ^ cksum_) & 1);
|
||||
bool parity_ = lfsr_rbyd_parity(rbyd) ^ lfs_parity(rbyd->cksum ^ cksum_);
|
||||
// and intentionally perturb the commit so the next tag appears invalid
|
||||
if ((e >> 7) == parity_) {
|
||||
cksum_buf[1] ^= 0x01;
|
||||
|
||||
@@ -274,6 +274,15 @@ static inline uint32_t lfs_popc(uint32_t a) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns true if there is an odd number of binary ones in a
|
||||
static inline bool lfs_parity(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return __builtin_parity(a);
|
||||
#else
|
||||
return lfs_popc(a) & 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find the sequence comparison of a and b, this is the distance
|
||||
// between a and b ignoring overflow
|
||||
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
||||
|
||||
Reference in New Issue
Block a user