What is fastest way to convert bool to byte?
Asked Answered
C

9

25

What is fastest way to convert bool to byte?

I want this mapping: False=0, True=1

Note: I don't want to use any if statements or other conditional statements. I don't want the CPU to halt or guess next statement.

Update: For those who want to see the point of this question. This example shows how two if statement are reduced from the code.

byte A = k > 9 ; //If it was possible (k>9) == 0 || 1
c[i * 2] = A * (k + 0x37) - (A - 1) * (k + 0x30);
Conscious answered 12/2, 2011 at 21:58 Comment(38)
Why do you care how fast the conversion is? And what is wrong with 'if' statements? Are you 100% sure that bool->byte conversions are causing a bottleneck in your code?Courageous
When you find yourself asking for "the fastest way", you should stop, think about it for a long while and read a dozen anti-premature-optimization rants ;)Canakin
Do you mean fast performance wise or the fastest way to write it? I would be very suprised if this would make any significant perforance differenceMckamey
Next time provide your code as you first post your question. Don't wait for us to make guesses, then say "no no no I meant THIS". And again, are you sure this is the source of your bottlenecks?Breastwork
@Mikael If you hash 1000gb of information it matters.Conscious
@Breastwork My bad, however if the question is not clear please provide a question so I have a chance to clarify the question.Conscious
"You're doing it wrong". Listen to the wisdom of others. Either explain your reason for wanting to know this, or deal with the unpleasantries that come your way regarding optimization. If you haven't got a good enough reason, well the people here have saved you from a tonne of effort trying to improve something meanlingless.Scanlan
If x ? 1 : 0 is actually too slow for you, then you REALLY need to reexamine what you're trying to do. If a conversion at that level is happening tens of millions of times per second (the only way it'll ever noticeably affect performance), then why aren't you writing it in ASM or something anyway? That's the only way you're ever going to significantly beat x ? 1 : 0 for speed.Trimmer
It's sort of funny that the conditional "needs to be optimized" when there is at least one (possible two) unnecessary multiplies in that ;-) (But at the end of the day ... it. just. doesn't. matter.) Another approach may be to use switch which can be optimized as a single jump -- not sure if C# or the JIT does it. Yet another approach is a LOOKUP TABLE. Whee! :)Meandrous
@Justin I asked this question since I wanted to know if there is any bit operation that dose that, before giving up and jumping in ASM.Conscious
@Josh I try to explain it in the updated question.Conscious
Amir, let this be a lesson to you - when asking a question, especially about programming, always explain your FULL PROBLEM. Otherwise you might end up almost solving your problem and asking the question about just one part of it - the part that doesn't make any sense.Louannlouanna
You don't have to go all the way to ASM. There's C to work with as well. Dunno what the exact performance difference is between C# and C is for this operation (C# is pretty well optimized for stuff like this already), but the overhead from managed code is going to dwarf whatever performance you can squeeze out of it this way.Trimmer
@Vilx Well I expected a more professional treatment. If people can't answer the question, they should not ask Why I need the function at first place. One should be silent and learn.Conscious
@Amir - for the umpteenth time! You're asking the wrong question! No, this conditional cannot be optimized! If your code is too slow, you must seek optimizations elsewhere - and there are plenty to choose from in the snippet you showed us.Louannlouanna
@Amir Rezaei: Well you cannot expect us to be mind readers. Your original question was not clear: all you asked was the fastest way to convert a bool to a byte without any if statements. Either we ask the rationale for such a micro-optimization (which by nature it was going to be), or we make assumptions, some of them wrong.Breastwork
@Amir: It's perfectly valid, and frequently useful, for others to point out that you're barking up the wrong tree. Nobody is trying to treat you unprofessionally; you have a lot of people who are trying to give you advice so that you can a) not waste your time on meaningless optimization, and b) use this site more productively. Many of us are surprised to see this question being asked, and that may come across in our comments. But if you read them from a non-defensive point of view, you'll find that no one has insulted you, only advised you on your problem. That's what Stack Overflow is for.Trimmer
If this is your bottleneck you shouldn't use C#, go for plain old ASM D:Euthanasia
@0xA3 This function is called more than ones!Conscious
@Justin IMO when I asked the first version of question that should be enough. My question was. I should not need to explain why I need this conversion, even if many people thinks it’s point less. That is another question IMO.Conscious
"One should be silent and learn" - downvoting seems to be another option.Ditty
@Amir Rezaei: It's not enough because for general-use purposes that will almost never be a bottleneck, and it reeks of micro-optimization. Yours is a rather special case.Breastwork
@Henk Downvoting did those who couldn't provide with any answers. They could wait and learned. See @ChaosPandion answer.Conscious
@Amir: OK, @Chaos's answer addresses your original question, but has very little to do with your later explanation. So: Do you want to convert bytes to bool or to Hex strings?Ditty
@Amir: Usually the one who's supposed to be silent and learn is the one who asked for help. The rest of us try to give constructive information, and "don't do it this way" is constructive (and very helpful) if it's correct. If someone shows me a better way to do something, I don't care about making the old way work anymore.Trimmer
@Henk I want convert byte to bool.Conscious
@Justin Well you can't be silent when people criticize your question and see it pointless. No one can say that this question if meaningless since no one can evaluate the possible value of this question and thereby this question is not pointless.Conscious
@Amir: the why the totally unrelated ByteToHex() ??? Time for my -1. And voting this NARQ too.Ditty
Now that you have a satisfactory answer to this question, perhaps you could clean it up a bit for us?Fortier
@Robert Do you want me to remove the stroke?Conscious
@Amir: Lots of people have answered or commented on this question, and every one of them, even those who offered answers, has questioned the usefulness of what you're doing and suggested finding a different approach. This includes Jon Skeet, who happens to be probably the most respected user on Stack Overflow. At what point do you consider that they may be right? You seem to be getting defensive when you could be re-examining your assumptions.Trimmer
@Amir: You should leave the strike-out parts. So that Vilx doesn't look like a complete fool. Btw, Vilx's answer probably still is better. Profile.Ditty
@Justin I got defensive when people downvoted the question and thought that it's was irrelevant. I had appreciated if people could have a little more patience. I got bombarded with question and downvoted before I had a chance to explain myself. The question was short and valuable IMO, and I illustrated in my updated question there is a true value in it. In many application a little if statement here and there may not be any problem. But in my case it was. Still I shouldn’t need to convince people why I need it at first place. That is another question.Conscious
@Henk Vilx answer is not answer to the this question. I was forced to provide with "why". I think people who google this question will find @ChaosPandion answer more valuable than the solution to my specific problem. Hence @Vilx solution is answer to another question that wasn’t asked. I had figured out lookup table.Conscious
"@Vilx solution is answer to another question that wasn’t asked" - Yes it was asked, by you, in revision 3. That's why this has become such a mess.Ditty
@Henk What I want to do is not relevant. The only relevant thing here is my original question that in many eyes where meaningless. The example I provided is one possible scenario. Vlix solution isn’t a solution to this generic problem.Conscious
@Amir: Thanks for being patient. All you need to do now is edit the question to match the answer you accepted. We don't need to see the history; the website tracks all of your changes in an edit history anyway. Thanks.Fortier
@Amir Rezaei - I disagree. Both what you seek and why you seek it are crucial to providing the optimal solution. While ChaosPandion's solution is clever and kudos for finding it, I'd crucify any developer that used it in production code without a length and detailed proof that optimizing a byte conversion of a Boolean provided any significant improvement at the significant cost of least astonishment and maintenance. If you are having to optimize bool conversions, then either your approach is wrong or C# is the wrong language.Helmsman
B
38

Using unsafe code this method is pretty fast. With optimizations enabled its about 30% faster than the conditional operator.

bool input = true;
byte value = *((byte*)(&input)); // 1
Brianabriand answered 12/2, 2011 at 22:14 Comment(7)
This is the unsafe code Jon was talking about, and is the way to go. +1Breastwork
And how does this fit into the byte-to-hex conversion?Ditty
@Henk Holterman: Not at all. Like the question ;) But it's an excellent answer.Roughen
@Henk See the original question and follow the timeline of the question.Conscious
While this is undoubtedly a clever solution, I'd crucify any developer that actually used this in production C# code without a lengthy explanation and proof as to how they came to the conclusion that if statements and byte conversions provide any measurable improvement in speed at the cost of violating least astonishment and maintenance.Helmsman
I believe this answer does the conversion itself quickly, although I doubt that the results are guaranteed. However, I don't think it's the best way to optimize the hashing algorithm... see my edited answer.Genarogendarme
@Jon - If your efforts manage to convince @Amir to switch his accepted answer you'll get no complaints from me, you may even get some light applause.Brianabriand
G
27

How about:

byte x = value ? (byte) 1 : (byte) 0;

If you're talking about the most efficient way of doing it, there may be some tricks you could do with unsafe code... but is this really a bottleneck for you?

EDIT: I just realized that the conditional operator needs those casts for the operands in order to make the overall expression a byte.

EDIT: Having seen your question, there's a much better way of optimizing it IMO. Currently you'll be performing operations you don't need to either way. Try this instead:

c[i << 1] = k > 9 ? k + 0x37 : k + 0x30;

or

c[i << 1] = k + (k > 9 ? 0x37 : 0x30);

(I suspect it doesn't matter which.)

You only need to perform the comparison and then one addition - instead of two additions and two multiplications after the conversion from bool to byte.

EDIT: Having just tried this, due to potentially branch misses, this can still definitely be slower than the unsafe version... or it can be faster. Picking a random value for k in the range [0, 18), this approach takes twice as long as the unsafe code. Picking a random value for k in the range [0, 1000) (i.e. one branch is picked much more often than the other), this approach is faster than the unconditional one. So what's the pattern for your k value?

Here's some benchmark code:

using System;
using System.Diagnostics;

class Test
{
    static void Main()
    {
        Random rng = new Random();
        int[] ks = new int[100000000];
        for (int i = 0; i < ks.Length; i++)
        {
            ks[i] = rng.Next(1000);
        }

        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("Iteration {0}", i);
            long sum = 0;
            Stopwatch sw = Stopwatch.StartNew();
            for (int j = 0; j < ks.Length; j++)
            {
                int k = ks[j];
                unsafe
                {
                    bool input = k > 9;
                    byte A = *((byte*)(&input)); // 1
                    sum += A * (k + 0x37) - (A - 1) * (k + 0x30);
                }
            }
            sw.Stop();
            Console.WriteLine("Unsafe code: {0}; {1}ms",
                              sum, sw.ElapsedMilliseconds);

            sum = 0;
            sw = Stopwatch.StartNew();
            for (int j = 0; j < ks.Length; j++)
            {
                int k = ks[j];
                sum += k > 9 ? k + 0x37 : k + 0x30;
            }
            sw.Stop();
            Console.WriteLine("Conditional: {0}; {1}ms",
                              sum, sw.ElapsedMilliseconds);
        }
    }
}

Note that on my computer this does give the same values for sum, but I'm not at all sure whether it's guaranteed to. I don't know that there's any guarantee of what the in-memory representation of true is... so on some CLRs you could potentially get the wrong answer.

However, I would point out that on my laptop, this loop of 100 million operations only takes around 300ms (and that's including adding to the sum and the initial array access, which may well be taking significant time, particularly due to cache misses)... are you really sure this is the bottleneck? How are you hoping to obtain data to hash so fast that this becomes the problem?

EDIT: I've just added another loop to see a "base case":

for (int j = 0; j < ks.Length; j++)
{
    int k = ks[j];
    sum += k + 0x30;
}

That takes about half the time... so only half the time is actually spent in the hash-specific code. Are you really, really sure this is a crucial bit of code to optimize at the cost of readability and potentially correctness?

Genarogendarme answered 12/2, 2011 at 22:0 Comment(14)
@Jon it is a bottleneck for me. The hash function is doing two conditional operations here. The array length is in terabyte order.Conscious
@Amir - first, it's not a hash function, it's a conversion function. Second, what machine are you using that has so much RAM, that not only a terabyte-size byte array can fit into it, but also an UTF-16 encoded hex version of it (which takes up 4 times as much RAM as the original array). And on top of that you still make that new string() down there, which copies the result char array! I won't even bother explaining the Array.Length property, which is 32-bit.Louannlouanna
@Vils 1. This function is called multiple times. In the end, the total length will be in terabyte order. 2. This function is called from a "hash" function.Conscious
@Amir - then perhaps I should really suggest moving all the relevant code (this function, hash function, and maybe more) to a C/C++ DLL and P/Invoking it. Maybe even do some ASM. But I fear that this may be beyond you - I know that I would be hard pressed to take this road. Anyways, if you really want to go all the way, here's an article that will help you: lwn.net/Articles/250967 Mind you it's quite lengthy and hard-core. But when you're fighting for microseconds, this knowledge is essential. (P.S. Maybe you can rewrite the hash function so that it doesn't need this function?)Louannlouanna
@Amir: Please read my updated answer. You were trying to optimize the wrong part, IMO.Genarogendarme
@Jon Thank you for your help! Is "k + (k > 9 ? 0x37 : 0x30)" faster than "A * (k + 0x37) - (A - 1) * (k + 0x30)" where A=0||1, Here is some background: igoro.com/archive/…Conscious
@Amir: I haven't tested it... but if you're convinced that this is a bottleneck in your code, then presumably you already have plenty of benchmarks set up, right? So test it yourself.Genarogendarme
@Amir: I've just tested it... and it depends on the values of k that you're using. See my edit.Genarogendarme
@Jon I optimized my code and reduced the sum to: "sum += ((byte)(&input)) * 0x07 + k + 0x30;" this is now 18-20% faster. What conversion brings is that it converts the original expression to a mathematical expression which can be reduced. The code is not readable as with branch but some comment in the code will help.Conscious
@Amir: And it also may not be correct any more... how certain are you that true always, always ends up as 1 when converted this way? It still seems unlikely that this is really the bottleneck... it processes data insanely quickly; where are you getting the data from that doesn't make IO the bottleneck?Genarogendarme
@Jon Data comes from Raid 0, SSD (PCI-Express). I don't really know the true performance. But I heard that they run at 2-4GB/S.Conscious
@Amir: Sustained? You really need to get the whole pipeline in place before you can work out where you actually need to spend time optimizing. For example, you may be able to do the hashing in parallel... but you won't be able to tell whether that's worth it until you've got some real data.Genarogendarme
@Jon Yes I agree! But I didn't realize that a bool to byte conversion would so hard in C#. It felt natural to have a conversion since it’s helpful in cases that I show in my example.Conscious
@Amir: It's not hard... it's extremely easy with the conditional operator. It's very rarely useful to do this sort of conversion IME, which is presumably why there isn't more direct support.Genarogendarme
A
12

How about

byte x = Convert.ToByte(true);
Astragalus answered 12/2, 2011 at 22:2 Comment(2)
Have you used reflector? That is public static byte ToByte(bool value) { if (!value) { return 0; } return 1; }Conscious
@Vilx-: Well the OP can't have it both ways.Breastwork
L
6
// Warning! Brain-compiled code ahead!
static readonly char[] HexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static string ToHex(this byte[] me)
{
    if ( me == null ) return null;
    int ml = me.Length;
    char[] c = new char[2*ml];

    int cp = 0;
    for (int i = 0; i < ml; i++ )
    {
        c[cp++] = HexChars[me[i]&15];
        c[cp++] = HexChars[me[i]>>4];
    }
    return new string(c);
}
Louannlouanna answered 12/2, 2011 at 22:15 Comment(6)
It would be interesting to see the & and >> into a small lookup vs. dual full lookup tables. In particular if the latter plays havoc with the cache/locality or not.Meandrous
@pst - HexChars is 32 bytes in length. I think it fits inside a cache line perfectly. In worst case it will take 2 cache lines. The only non-locality here is the source/target array, but those are accessed in a linear fashion, so prefetching should work like a charm. In short, I don't think this code can be much cache-optimized, aside maybe from some SSE instructions of which I know nothing about. But that goes beyond C# then.Louannlouanna
Just a question: don't you think it's strange to return a string in a function like this! "terabyte"?Astragalus
@Henk - Maybe. You would indeed have to profile it to see if there is any difference (me, I think the >> operation probably takes, like, 1 cycle, so there wouldn't be any noticable difference).Louannlouanna
@Astragalus Still the question isn't my function. My question is a part of the job to optimize this function.Conscious
@Astragalus - I only fixed the OP's function. He mentioned the "terabyte" later. And then explained that it results from calling this function in a loop. :PLouannlouanna
M
6

The following is a simple benchmark to compare the three options:

    Int32 j = 0;
    bool b = true;

    for (int n = 0; n < 5; n++) {
        Stopwatch sw1 = new Stopwatch();
        Stopwatch sw2 = new Stopwatch();
        Stopwatch sw3 = new Stopwatch();
        sw1.Start();
        for (int i = 100 * 1000 * 1000; i > 0; i--)
            unsafe { j = *(int*)(&b); }
        sw1.Stop();

        sw2.Start();
        for (int i = 100 * 1000 * 1000; i > 0; i--)
            j = b ? 1 : 0;
        sw2.Stop();

        sw3.Start();
        for (int i = 100 * 1000 * 1000; i > 0; i--)
            j = Convert.ToInt32(b);
        sw3.Stop();
        Trace.WriteLine("sw1: " + sw1.ElapsedMilliseconds +
            "  sw2:" + sw2.ElapsedMilliseconds + ", +" + 100 * (sw2.ElapsedMilliseconds - sw1.ElapsedMilliseconds) / sw1.ElapsedMilliseconds + "% relative to sw1" +
            "  sw3:" + sw3.ElapsedMilliseconds + ", +" + 100 * (sw3.ElapsedMilliseconds - sw1.ElapsedMilliseconds) / sw1.ElapsedMilliseconds + "% relative to sw1"
            );
    }

The results:

sw1: 172  sw2:218, +26% relative to sw1  sw3:213, +23% relative to sw1
sw1: 168  sw2:211, +25% relative to sw1  sw3:211, +25% relative to sw1
sw1: 167  sw2:212, +26% relative to sw1  sw3:208, +24% relative to sw1
sw1: 167  sw2:211, +26% relative to sw1  sw3:209, +25% relative to sw1
sw1: 167  sw2:212, +26% relative to sw1  sw3:210, +25% relative to sw1

Conclusion:

The unsafe method is about 25% faster than the other two!

The relative slowness of the "if" version is due to the high cost of branching. The cost of the Convert could have been avoided if Microsoft would do the conversion at compile time..

Multiversity answered 21/12, 2012 at 1:2 Comment(0)
M
3
Convert.ToByte(myBool) 

will give you 0 if myBool is False or 1 if it is True.

Monthly answered 12/2, 2011 at 22:4 Comment(0)
D
2

Hand-written IL:

.method private hidebysig static 
    int32 BoolToInt (
        bool b
    ) cil managed noinlining 
{
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldc.i4.0
    IL_0002: cgt.un
    IL_0004: ret
}

And they are jitted to few x86 codes:
(clrjit.dll version 4.7.3131.0)

test        cl,cl
setne       al
movzx       eax,al
ret

The only problem is that I have found no simple way to inline IL in C#. This answer is done using dnSpy.

Dewar answered 25/7, 2018 at 15:50 Comment(0)
C
0

You can use this struct to do similar to ChaosPandion's solution, but with safe code.

[StructLayout(LayoutKind.Explicit)]
struct BoolByte
{
    [FieldOffset(0)]
    public bool flag;
    [FieldOffset(0)]
    public byte num;
}

...

bool someBool = true;
byte num = new BoolByte() { flag = someBool }.num;

I haven't benchmarked it, so I'm not sure how the speed compares.

[EDIT] Well I ran the benchmark with .NET 3.5 equivalent mono and it looks like this is ~10% slower than a plain if check (on my macbook pro). So forget about this one. I doubt .NET 4+ would make a difference there.

Capsular answered 16/1, 2019 at 8:35 Comment(0)
S
0

Since .NET Core 2.1 you can reinterpret the bool as a byte this way. This is branch-free and should be very fast, as it hardly needs to "do" anything.

Technically, the true value could be any non-zero byte, but in practice, it is 1. That deserves some consideration. If you want absolute certainty, you could look for an efficient, branch-free way to turn a byte into 1 if it is non-zero, or leave it 0 otherwise. (Two approaches come to mind: A) Smear the bits so that either all bits are 0 or all bits are 1, then do & 1 to get either 0 or 1. B) Take 0 - n as an int, which will be either zero or negative. Shift the sign bit so that it becomes the least significant bit, resulting in either 0 or 1.)

Sextan answered 20/11, 2019 at 13:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.