bxb100 / bxb100.github.io

This is my blog
https://blog.tomcat.run
MIT License
1 stars 0 forks source link

SWAR 的理解 #43

Open bxb100 opened 6 months ago

bxb100 commented 6 months ago

原文:https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/

uint32_t  parse_eight_digits_unrolled(uint64_t val) {
  const uint64_t mask = 0x000000FF000000FF;
  const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
  const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
  val -= 0x3030303030303030;
  val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
  val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
  return val;
}

理解的过程中,把数值 1000000ULL 按照 10 进制的形式填到二进制里面,以为是 0100 0000 😢 :(

bxb100 commented 6 months ago

https://github.com/gunnarmorling/1brc#running-the-challenge 那里看到的优化技巧

bxb100 commented 6 months ago

https://github.com/gunnarmorling/1brc/blob/085168a0b3c73b64409afcf58a1f0a67f746a30a/src/main/java/dev/morling/onebrc/CalculateAverage_royvanrijn.java#L140C11-L141

Salt Lake City;10.3 的 bytes:

byte[19] { 83, 97, 108, 116, 32, 76, 97, 107, 101, 32, 67, 105, 116, 121, 59, 49, 48, 46, 51 }

按 Long 64bits 来分割的话,为(这里都是大端,后面使用需要 Long.reverseBytes):

// 83, 97, 108, 116, 32, 76, 97, 107
// 6008202623902835051L hex: 0x53616C74204C616B
// 101, 32, 67, 105, 116, 121, 59, 49
// 7286898317290191665L hex: 0x6520436974793B31
// 48, 46, 51
// 0x302E33L

可以看到

final long match = word ^ 0x3B3B3B3B3B3B3B3BL;
long mask = ((match - 0x0101010101010101L) & ~match) & 0x8080808080808080L;

主要是判断 bytes 里面有没有 ; (0x3B),如果有的话,高位为 1 剩余都为 0

final long partialWord = word & ((mask >> 7) - 1);

这里的技巧是右移 7 位再减 1,等价于高位后 bytes 均为 1,那么这里就是分割 ; 后面 bytes 的值(这里是小端计算!!)

从上面的举例来说,也就是 101, 32, 67, 105, 116, 121, 59, 49 bytes 走这个逻辑:

64bits Long 值为 7286898317290176512L 也就是 0x6520436974790000

mask 值为 0x80000000000000

((mask >> 7) - 1) 值就是 0xFFFFFFFFFFFF

所以最后得到 7286898317290176512Le City\000\000

转换为 string 的代码 这里也挺搞笑的,小端进去,String 按大端转,最后结果是可读的 ;) ```java public byte[] longToBytes(long x) { ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.putLong(x); return buffer.array(); } new String(longToBytes(7286898317290176512L)) ``` 当然后面的代码并没有使用这个方式获取 string,而是直接通过 `UNSAFE.getByte` 直接获取内存中映射的值

后面还有一段代码, 也是一个技巧,由前文可知某一个 byte 是 0x80,其余为 0,也就是说 0 的个数和 byte 的关系是 1:8


final int index = Long.numberOfTrailingZeros(mask) >> 3;
bxb100 commented 6 months ago

推荐书 算法心得