double_compressor.cc
1 // Copyright 2017, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 #include "util/coding/double_compressor.h"
5 
6 #include <cmath>
7 #include <numeric>
8 
9 #include <lz4.h>
10 #include <shuffle.h>
11 
12 #include "base/bits.h"
13 #include "base/endian.h"
14 #include "base/logging.h"
15 #include "base/integral_types.h"
16 #include "util/math/float2decimal.h"
17 
18 
19 namespace util {
20 
21 namespace {
22 
23 static inline unsigned DecimalCost(unsigned dec_len) {
24  if (dec_len >= 16)
25  return 10;
26 
27  return (dec_len + 1) / 2;
28 }
29 
30 constexpr uint8_t kRawBit = 1 << 7;
31 constexpr uint8_t kHasExceptionsBit = 1 << 6;
32 
33 double FromPositive(int64_t significand, int exponent) {
34  while (significand % 10 == 0) {
35  significand /= 10;
36  ++exponent;
37  }
38  DCHECK_LE(Bits::FindMSBSet64NonZero(significand), 52);
39  double res(significand);
40  return res * exp10(exponent);
41 }
42 
43 // TODO: to memoize exp10(exponent).
44 double FromDecimal(int64_t significand, int exponent) {
45  if (significand == 0)
46  return 0.0;
47 
48  if (significand < 0)
49  return -FromPositive(-significand, exponent);
50  else
51  return FromPositive(significand, exponent);
52 }
53 
54 constexpr size_t kShuffleStep = 1024;
55 
56 inline void bitshuffle2(const int64_t* src, size_t count, uint8_t* dest, uint8_t* tmp) {
57  const uint8_t* src_b = reinterpret_cast<const uint8_t*>(src);
58  const uint8_t* src_e = src_b + count * sizeof(uint64_t);
59  uint8_t* ptr = dest;
60  for (; src_b + kShuffleStep <= src_e; src_b += kShuffleStep) {
61  bitshuffle(sizeof(uint64_t), kShuffleStep, src_b, ptr, tmp);
62  ptr+= kShuffleStep;
63  }
64  if (src_b != src_e) {
65  bitshuffle(sizeof(uint64_t), src_e - src_b, src_b, ptr, tmp);
66  }
67 }
68 
69 inline void bitunshuffle2(const uint8_t* src, size_t sz, uint8_t* dest) {
70  uint8_t tmp[kShuffleStep];
71 
72  size_t i = 0;
73  uint8_t* ptr = dest;
74  for (; i + kShuffleStep <= sz; i += kShuffleStep) {
75  bitunshuffle(sizeof(uint64_t), kShuffleStep, src + i, ptr, tmp);
76  ptr+= kShuffleStep;
77  }
78  if (i != sz) {
79  bitunshuffle(sizeof(uint64_t), sz - i, src + i, ptr, tmp);
80  }
81 }
82 
83 } // namespace
84 
86  uint16_t cnt[17];
87  ExpInfo() { std::fill(cnt, cnt + 17, 0); }
88 
89  uint32_t count() const { return std::accumulate(cnt, cnt + 17, uint32_t(0)); }
90  uint32_t Cost(int edelta) const {
91  DCHECK_GE(edelta, 0);
92 
93  uint32_t res = 0;
94  for (unsigned i = 0; i < 17; ++i) {
95  res += cnt[i] * DecimalCost(edelta + i + 1);
96  }
97  return res;
98  }
99 };
100 
101 void DoubleCompressor::DecimalHeader::Serialize(uint8_t flags, uint8_t* dest) {
102  LittleEndian::Store64(dest, min_val); // 8
103  dest += sizeof(uint64_t);
104  LittleEndian::Store16(dest, exponent); //2
105  dest += sizeof(uint16_t);
106 
107  LittleEndian::Store16(dest, lz4_size); //2
108  dest += sizeof(uint16_t);
109 
110  if (flags & kHasExceptionsBit)
111  LittleEndian::Store16(dest, first_exception_index);
112 }
113 
114 uint32_t DoubleCompressor::DecimalHeader::Parse(uint8_t flags, const uint8_t* src) {
115  min_val = LittleEndian::Load64(src); // 8
116  src += sizeof(uint64_t);
117  exponent = LittleEndian::Load16(src); //2
118  src += sizeof(uint16_t);
119 
120  lz4_size = LittleEndian::Load16(src); //2
121  src += sizeof(uint16_t);
122 
123  uint32_t res = 12;
124  if (flags & kHasExceptionsBit) {
125  first_exception_index = LittleEndian::Load16(src);
126  res += 2;
127  }
128  return res;
129 }
130 
131 unsigned DoubleCompressor::NormalizeDecimals(unsigned count, const double* dbl_src) {
132  aux_->header.min_val = kuint64max;
133  aux_->header.first_exception_index = count;
134 
135  unsigned normal_cnt = 0;
136  unsigned prev_exception_index = 0;
137  unsigned exception_index = 0;
138 
139  for (unsigned i = 0; i < count; ++i) {
140  const Decimal& dec = aux_->dec[i];
141 
142  if (dec.CanNormalize(aux_->header.exponent)) {
143  int64_t normal_val = dec.val * base::Power10(dec.exp - aux_->header.exponent);
144  aux_->normalized[i] = normal_val;
145  if (normal_val < aux_->header.min_val)
146  aux_->header.min_val = normal_val;
147  normal_cnt++;
148  } else {
149  aux_->exceptions[exception_index++] = dbl_src[i];
150 
151  if (aux_->header.first_exception_index != count) {
152  aux_->normalized[prev_exception_index] = i - prev_exception_index;
153  } else {
154  aux_->header.first_exception_index = i;
155  }
156  prev_exception_index = i;
157  }
158  }
159 
160  if (exception_index) {
161  aux_->normalized[prev_exception_index] = 0; // close the linked list.
162  }
163 
164  exception_index = aux_->header.first_exception_index;
165 
166  // Rebase the numbers.
167  for (unsigned i = 0; i < count; ++i) {
168  if (i == exception_index) {
169  exception_index += aux_->normalized[i];
170  } else {
171  aux_->normalized[i] -= aux_->header.min_val;
172  }
173  }
174 
175  return normal_cnt;
176 }
177 
178 uint32_t DoubleCompressor::Commit(const double* src, uint32_t count, uint8_t* dest) {
179  if (count <= 16) {
180  return WriteRawDoubles(src, count, dest);
181  }
182 
183  CHECK_LE(count, BLOCK_MAX_LEN);
184  if (!aux_)
185  aux_.reset(new Aux);
186 
187  ExponentMap exp_map;
188 
189  for (unsigned i = 0; i < count; ++i) {
190  Decimal& dec = aux_->dec[i];
191  CHECK(util::dtoa::ToDecimal(src[i], &dec.val, &dec.exp, &dec.dec_len));
192 
193  DCHECK_GT(dec.dec_len, 0);
194  DCHECK_LE(dec.dec_len, 17);
195 
196  // For dec_len we automatically put them into exceptions.
197  if (dec.val != 0 && dec.dec_len < 17) {
198  ++exp_map[dec.exp].cnt[dec.dec_len - 1];
199  }
200  }
201  uint32_t cost = Optimize(exp_map);
202 
203  unsigned normal_cnt = NormalizeDecimals(count, src);
204  if (normal_cnt < count / 2) {
205  return WriteRawDoubles(src, count, dest);
206  }
207  VLOG(1) << "Cost: " << cost << " normalized count: " << normal_cnt;
208 
209  // dest here serves as temporary buffer. aux_->shuffle is the destination.
210  uint8_t* shuffle_buf = reinterpret_cast<uint8_t*>(aux_->dec);
211  bitshuffle2(aux_->normalized, count, shuffle_buf, dest);
212 
213  char* const cdest = reinterpret_cast<char*>(dest);
214  char* next = cdest + 3 + DECIMAL_HEADER_MAX_SIZE - 2;
215  char* end = cdest + CommitMaxSize(count);
216  unsigned exc_count = count - normal_cnt;
217  uint8_t flags = 0;
218  if (exc_count > 0) {
219  next += 2;
220  flags = kHasExceptionsBit;
221  }
222  const unsigned kByteSize = sizeof(uint64_t) * count;
223  int res = LZ4_compress_fast(reinterpret_cast<const char*>(shuffle_buf), next,
224  kByteSize, LZ4_COMPRESSBOUND(kByteSize), 3 /* level */);
225  CHECK_GT(res, 0);
226  if ((res + 8 * exc_count) * 1.1 > kByteSize) {
227  return WriteRawDoubles(src, count, dest);
228  }
229 
230  aux_->header.lz4_size = res;
231  aux_->header.Serialize(flags, dest + 3);
232 
233  next += res;
234  if (exc_count) {
235  shuffle(sizeof(uint64_t), exc_count * sizeof(uint64_t),
236  reinterpret_cast<const uint8_t*>(aux_->exceptions), shuffle_buf);
237 
238  res = LZ4_compress_fast(reinterpret_cast<const char*>(shuffle_buf), next,
239  exc_count * 8, end - next, 5);
240  CHECK_GT(res, 0);
241  next += res;
242  }
243  uint32_t written = next - cdest;
244 
245  CHECK_LE(written, COMPRESS_BLOCK_BOUND);
246  LittleEndian::Store16(dest + 1, written - 3);
247  *dest = flags;
248  return written;
249 }
250 
251 uint32_t DoubleCompressor::Optimize(const ExponentMap& em) {
252  uint32_t best = kuint32max;
253 
254  uint32_t prefix_cnt = 0;
255 
256  // we chose the exponent that produces smallest cost when
257  // normalizing all the decimal numbers to its value.
258  for (auto it = em.begin(); it != em.end(); ++it) {
259  int16_t e_base = it->first;
260  uint32_t current = it->second.Cost(0);
261  auto next = it;
262 
263  for (++next; next != em.end(); ++next) {
264  current += next->second.Cost(next->first - e_base);
265  }
266  current += prefix_cnt * 9;
267  if (current < best) {
268  best = current;
269  aux_->header.exponent = e_base;
270  }
271  prefix_cnt += it->second.count();
272  }
273  return best;
274 }
275 
276 uint32_t DoubleCompressor::WriteRawDoubles(const double* src, uint32_t count, uint8_t* dest) {
277  *dest = kRawBit;
278  uint16_t sz = count * sizeof(double);
279  LittleEndian::Store16(dest + 1, sz);
280  memcpy(dest + 3, src, sz);
281  return sz + 3;
282 }
283 
284 
285 int32_t DoubleDecompressor::Decompress(const uint8_t* src, uint32_t src_len, double* dest) {
286  if (src_len < 3 || LittleEndian::Load16(src + 1) != src_len - 3)
287  return -1;
288 
289  src_len -= 3;
290 
291  uint8_t flags = *src;
292  src += 3;
293 
294  if ((flags & kRawBit) != 0) {
295  CHECK_EQ(0, src_len % sizeof(double));
296  memcpy(dest, src, src_len);
297  return src_len / sizeof(double);
298  }
299  CHECK_GT(src_len, DoubleCompressor::DECIMAL_HEADER_MAX_SIZE);
300 
301  if (!aux_)
302  aux_.reset(new Aux);
303 
304  DoubleCompressor::DecimalHeader dh;
305  uint32 read = dh.Parse(flags, src);
306  src_len -= read;
307 
308  CHECK_LE(dh.lz4_size, src_len);
309  constexpr size_t kMaxSize = DoubleCompressor::BLOCK_MAX_BYTES;
310 
311  src += read;
312  src_len -= dh.lz4_size;
313 
314  int res = LZ4_decompress_safe(reinterpret_cast<const char*>(src),
315  reinterpret_cast<char*>(aux_->z4buf), dh.lz4_size, kMaxSize);
316  CHECK_GT(res, 0);
317  CHECK_EQ(0, res % 8);
318  src += dh.lz4_size;
319 
320  bitunshuffle2(aux_->z4buf, res, reinterpret_cast<uint8_t*>(dest));
321  unsigned count = res / 8;
322 
323  unsigned exception_index = count;
324  unsigned exception_id = 0;
325  if (flags & kHasExceptionsBit) {
326  res = LZ4_decompress_safe(reinterpret_cast<const char*>(src),
327  reinterpret_cast<char*>(aux_->z4buf), src_len,
328  kMaxSize);
329  CHECK_GT(res, 0);
330  CHECK_EQ(0, res % 8);
331  unshuffle(sizeof(double), res, aux_->z4buf, reinterpret_cast<uint8*>(aux_->exceptions));
332  exception_index = dh.first_exception_index;
333  }
334 
335  int64_t* i64 = reinterpret_cast<int64_t*>(dest);
336  for (unsigned i = 0; i < count; ++i) {
337  if (i == exception_index) {
338  unsigned delta = i64[i];
339  dest[i] = aux_->exceptions[exception_id++];
340  exception_index += delta;
341  CHECK_LT(exception_index, count);
342  } else {
343  int64_t f = i64[i] + dh.min_val;
344  dest[i] = FromDecimal(f, dh.exponent);
345  }
346  }
347  return count;
348 }
349 
350 } // namespace util