jarrodm
Hi all,
I’ve finally achieved ‘Basic’ status on Elixir Forums, so I have a appropriately basic question ![]()
I’m ingesting a mask where each power of 2 aligns to an consecutive index. So, a value of 1, flags the first index, 2 → 2nd, 4-> 3rd, etc. I’m using the default big endianness of the VM, and I’m finding the following:
iex(87)> << 0::1, 1::7 >> = << 1 >>
<<1>>
… whereas I desire something like:
iex(87)> << 1::1, 0::7 >> = some_transform(<< 1 >>)
<<1>>
I’ve mucked around with big and little modifiers on either side of the match operator, but I assume the order of ingest when the bitstring is split is predetermined to be most-significant/lowest-bit to least-significant/highest-bit, low-byte to high-byte, and the input byte will always be in the same bit-order—which all makes sense. I see that there’s :binary encode and decode functions, but once again they only affect byte order, as it should.
I’ve resolved to doing:
(for << bit::1 <- << 1 >> >>, into: [], do: bit)
|> Enum.reverse()
… to reverse the bits so that I can ingest them the way I expect. Though I suspect I’ve missed some obvious easier way, or some erlang function that might be endian-agnostic, and maybe even optimize this away to a trivial assembly operation—am I hoping too much?
Any pointers would be much appreciated
Thanks!
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 8 of 8 Posts
ityonemo
unfortunately, I have no idea where the full list of string bit length decorators is documented. Note that generally you can’t have a variable size at the head of a string, but in this case we can calculate it and go and do it explicitly. But you can’t just drop a variable directly in to the length specifier (laaaame), so you have to explicitly invoke the size() decorator.
jarrodm
This is an interesting solution, though I think it’s more complex and perhaps has worse performance characteristics than simply:
Which also builds a list of bits and reverses it, and perhaps the compiler would have an easier time optimizing it. Ultimately it would be nice to avoid having to use list at all. I imagine reversing bits is something that many processors could do in a single instruction, but at worst case a few instructions without having to use memory beyond the CPU registers. But having a quick look around at BIFs in Erlang, it doesn’t seem that low-level operations like this are in the scope of erlang… which is sad, but no big deal I suppose
jarrodm
Having a bit more of think about your approach, and it inspired me to try something else:
It basically does what I want, and assuming the VM re-uses CPU registers, it might not even need to touch memory.
Sebb
Endianess is not about the bit-order, but about byte order.
In a bitstring the least significant bit is the rightmost and there is nothing you can do about it.
jarrodm
True, but also I believe that the lower order bits are stored in higher bits of memory so to speak (and vice versa for little endian architectures), but at you point out, it usually doesn’t matter semantically as values are usually transferred at the byte level over a network, from a file, etc. Right?
I think when bitpacking (sub-byte level) data, it makes sense for bit strings to fill from the big-end as it will spill into the next byte - so it’s coherent semantically for the default big-endian:
But as far as I know, there’s no way to bit-pack this in little-endian in Erlang, which would result in
<<129, 0>>True, but what I was looking for was a way to reverse the bits. I think the solution we’ve arrived at is fine. The only thing better would be some library or BIFs that could compile to optimized assembly… but that’s probably over kill for what I need anyway.
ityonemo
I believe that for loop is just sugar for Enum.reduce, which will have more overhead than tco recursion solution. You could check with Benchee, but also I realized that creating a new bitstring is not the best idea, don’t know if it’s kept as a reference internally. This is probably optimal, or near-optimal:
Also note that your solution, if your lambdas are effectful, your effects will land in the forward order, not the reverse order. This will do sideeffects in the correct order.
jarrodm
You might be right about that. I’m busy for now, but I’ll do some benchmarks later in the week and post them up
jarrodm
So I ran some tests. I had to modify your code to match my function signature. Specifically you were running a function for each value, whereas mine was simply reversing the bit string. Here are the results:
Random 16-bit bitstrings
Random 8-bit bitstrings
It appears the simplest approach is the fastest, with your second implementation coming in second. I’m surprised that my second implementation is worse than my first, but it probably that erlang doesn’t permit reusing registers to do ‘in-place’ updates to values, but throws everything on the stack - or perhaps there’s some contention between registers. So it looks like the KISS principle wins on the erlang VM.
You can find the
.exsfor the benchmark here:Edit: And please let me know if I’ve made any mistakes with the benchmark