You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FastLanes is used as a building block for higher level compression algorithms, but not as a standalone
19
20
compression algorithm. The main use of FastLanes is to pack integers. The callers need to do extra work
@@ -39,8 +40,8 @@ In case of odd bitwidths, for example 5 bits, and we want to pack them into a 64
39
40
1) either we pack 12 integers and waste 4 bits, or
40
41
2) we split the 13th integer and handle the overflow
41
42
42
-
Both options are not ideal. The beauty of the FastLanes approach is that we can pack odd width integers when we
43
-
have enough of them. In the compression context we tend to have many integers so the numbers are not an issue.
43
+
None of the options are ideal. The beauty of the FastLanes approach is that we can pack odd width integers when we
44
+
have 'enough' of them. In the compression context we tend to have many integers so the numbers are not an issue.
44
45
45
46
The second advantage of the FastLanes approach is that it takes advantage of the 'enough' numbers to pack, and
46
47
developed a set of instructions that allows to transpose the data with SIMD instructions. This is a huge
@@ -61,10 +62,10 @@ NULL values, in which case the underlying compression will only store the non-NU
61
62
up with way less than 1000 non-NULL values. Finally, there are situations specific to TimescaleDB where we
62
63
naturally have smaller batch sizes.
63
64
64
-
The problem with targeting a fixed size of 1024 elements when we often have much smaller batches is that it leads
65
-
to a lot of wasted space which will erode the packing advantages of FastLanes. The natural pack boundary of FL1024
66
-
is 128 bytes, so the smallest amount we can truncate from the output is 128 bytes. This causes too much waste
67
-
for smaller batches.
65
+
The problem with targeting a fixed size of 1024 elements (as in the original paper) when we often have much
66
+
smaller batches is that it leads to a lot of wasted space which will erode the packing advantages of FastLanes.
67
+
The natural pack boundary of FL1024 is 128 bytes, so the smallest amount we can truncate from the output is
68
+
128 bytes. This causes too much waste for smaller batches.
68
69
69
70
### Supporting vectorized execution
70
71
@@ -77,8 +78,8 @@ some level of cooperation from the caller. The three areas where it is required
77
78
- the input buffer needs to have space for a certain number of elements, potentially more than the actual number of elements being packed
78
79
- the packed buffer needs to be allocated with a certain size, potentially more than the actual size of the packed data, and the caller needs to use the returned truncated size to determine the result size
79
80
80
-
Imagine that we have 255 integers to pack. It is faster to pad it to 256 elements and use AVX2 instructions
81
-
than use SSE2 to pack 128 and handle the remaining 127 separately. The requirements the library has are
81
+
Imagine that we have 249 integers to pack. It is faster to pad it to 256 elements and use AVX2 instructions
82
+
than use SSE2 to pack 128 and handle the remaining 121 separately. The requirements the library has are
82
83
designed along these lines, so we maximize speed at the expense of some extra buffer capacity.
83
84
84
85
The library provides functions to inform the user about these requirements, and there is a section below, that
@@ -89,7 +90,8 @@ these three parameters:
89
90
- the width of the input elements (T)
90
91
- the actual bit-width of the packed elements (W)
91
92
92
-
Where W is <= T, and N is <= 256. These determine the actual packing method the library uses and the requirements for the buffers.
93
+
Where W is <= T, and N is <= 256. These determine the actual packing method the library uses and the requirements
94
+
for the buffers.
93
95
94
96
'N', 'W' and 'T' are the central concepts in the FastLanes library and they are often referred to by only these
95
97
initials. The number of parallel lanes (S) is also a central concept but it is derived from T and the tier bits,
@@ -114,10 +116,10 @@ The tiered FL packing is used in many places in the compression algorithm, not o
114
116
values but also for smaller integer arrays, for example the new dictionary compression has a smaller array of the
115
117
dictionary entries, or in the PFOR compression the exceptions are stored as small integer arrays. In these cases
116
118
we easily end up storing only a handful of elements and this is why the very small tiers are useful. The small
117
-
tiers don't offer much SIMD performance but they save space significantly.
119
+
tiers don't offer much SIMD performance but they save significant amount of space.
118
120
119
-
The library provides wrappers over the FL tiers in the `fl/fl.h` file and it is strongly encouraged to use
120
-
these instead of calling the specific tiers directly.
121
+
The library provides wrappers over the FL tiers in the `fastlanes/fastlanes.h` file and the internal
The input of the unpack functions must be padded with zeros up to `fl_alloc_bytes`. This
214
+
The input of the unpack functions must be padded with zeros up to `fl_required_bytes`. This
213
215
is because the SIMD instructions will read beyond the actual packed data, up to the allocated size.
214
216
215
217
## Interface contract
@@ -219,13 +221,10 @@ This is a concise recap of the previous sections.
219
221
- the buffer for the input values MUST have at least `fl_input_count` elements (not bytes) and it SHOULD be aligned to `fl_alignment` bytes, and it MUST be aligned naturally
220
222
- the packed buffer (pack output / unpack input) MUST be aligned to `fl_alignment` bytes
221
223
- the output of the unpack function holding the unpacked values SHOULD be aligned to `fl_alignment` bytes, and MUST be aligned naturally
222
-
- the size of the pack/unpack buffer must be `fl_alloc_bytes` bytes
223
-
- the unpack input buffer must be padded with zeros up to `fl_alloc_bytes` bytes
224
-
- `fl_pack` and `fl_pack_ffor` return the packed size in bytes, which is the same as `fl_truncated_bytes`
225
-
- the input size `n` MUST NOT exceed 256, if using the fl/fl.h provided interface, and
226
-
- if you use a specific tiered packer directly (in addition to the above):
227
-
- `n` MUST NOT exceed the tier's limit
228
-
- `w` MUST be <= `T`
224
+
- the size of the pack/unpack buffer must be `fl_required_bytes` bytes
225
+
- the unpack input buffer must be padded with zeros up to `fl_required_bytes` bytes
226
+
- `fl_pack` and `fl_pack_ffor` return the packed size in bytes, which is the same as `fl_result_bytes`
227
+
- the input size `n` MUST NOT exceed 256
229
228
230
229
## Simple packing vs FFOR packing
231
230
@@ -258,10 +257,13 @@ caller's responsibility to store the `base` value, similar to `w` and `n`.
258
257
259
258
## Batch sizing
260
259
261
-
The various FL tiers have a maximum capacity. In case of FL256, it is 256 elements. It is the
262
-
caller's responsibility to manage the inputs and potentially split it such that it fits into
263
-
one of the available FL tiers. This is a deliberate decision, because in practice, using the
264
-
same bitwidth for more than 256 elements is rarely economical because of the outliers.
260
+
The maximum number of elements the FL tiers can handle is 256 elements (FL256 tier). It is the
261
+
caller's responsibility to manage the inputs and potentially split it such that it fits. This
262
+
is a deliberate decision, because in practice, using the same bitwidth for more than 256 elements
263
+
is rarely economical because of the outliers.
264
+
265
+
When passing less than 256 elements, the library chooses the best tier based on performance
266
+
and packing efficiency.
265
267
266
268
## Signed integers and input validation
267
269
@@ -271,8 +273,7 @@ is chosen such that the subtracted values are non-negative. The library does not
271
273
the inputs, so if the caller passes negative values, the behavior is undefined.
272
274
273
275
Similarly, the library doesn't validate the 'W' parameter to be <= T, or N <= 256, so
274
-
it is the caller's responsibility to ensure that these conditions are met. If one
275
-
calls a tiered FL function directly, the constraints for N can be tighter.
276
+
it is the caller's responsibility to ensure that these conditions are met.
276
277
277
278
## The W=0 special case
278
279
@@ -285,11 +286,11 @@ The pack functions in both cases will return 0, and they handle this special cas
285
286
and with that, the functions ignore the buffers and passing NULLs as buffers is safe. The
286
287
unpack functions need a valid result buffer. Also the below holds:
0 commit comments