0

I want to use this to compare short strings(4 to 8 chars long) in performance critical code. I was thinking to cast them to integer values and compare those values instead of comparing the strings:

const char* str = "abcdefgh";
uint64_t num = *reinterpret_cast<const uint64_t*>(str);

Is it safe to cast str to uint64_t without checking alignment of the char* pointer? I'm using the code only on ARM and Intel CPUs, 32 and 64 bits. If the behaviour is well defined and the cast is safe, should I expect performance degradation when the pointer is not aligned to 8 bytes?

Do you have any other suggestions to do this in a very fast way?

Mircea Ispas
  • 20,260
  • 32
  • 123
  • 211
  • 1
    If the strings were stored in fixed-size buffers and padded with zero-bytes to the same length, you could use `memcmp()`. Are you sure this is your performance bottleneck? I ask because a textual representation typically only uses 6 bits of an octet, so it could be packed in a more efficient way. – Ulrich Eckhardt Dec 22 '14 at 21:54
  • It might be faster, but this is the wrong tag to use, since it's CPU-specific. – kec Dec 22 '14 at 22:07

4 Answers4

2

This code will yield incorrect behavior, as the contents of bytes following a null terminator are not part of the string. For instance, consider the strings:

char str1[8] = { 't', 'e', 's', 't', 0, 0, 0, 1 };
char str2[8] = { 't', 'e', 's', 't', 0, 0, 0, 2 };

Both strings have the value "test", but comparing them as integers will say that they are different, because one of the bytes following the end of the string is different. Additionally, if a string begins less than eight bytes from the end of an allocated page of memory, trying to read an integer from it will result in a segfault.

Use strcmp() to compare strings. It's already quite fast, and will give correct results.

1

What you are proposing is not portable and in fact violates the C++ strict aliasing rule. It is undefined behavior.

From the standard: section 3.10.10

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

The most reliable, portable method is simply to use strcmp().

Community
  • 1
  • 1
ThomasMcLeod
  • 7,603
  • 4
  • 42
  • 80
  • See http://stackoverflow.com/questions/12612488/aliasing-t-with-char-is-allowed-is-it-also-allowed-the-other-way-around – Mark Ransom Dec 22 '14 at 21:55
0

You can't reliably (see comments below) "cast" a string of characters to an integer like that. You need to use bitwise operators, like so:

Using bitwise operators in C++ to change 4 chars to int

(And, of course, you can only fit four chars into a 32-bit integer.)

But I'm not sure why you're assuming that "atomizing" the strings like this will be faster than just calling strcmp(), which is implemented in Assembly code in most C libraries.

Community
  • 1
  • 1
Dan Korn
  • 1,274
  • 9
  • 14
  • Actually, you can cast a string to an integer like that just fine; the results will just depend on the endianness of your system. (For instance, `"1234"` will come out to 0x31323334 on big-endian systems, but 0x34333231 on little-endian systems.) –  Dec 22 '14 at 21:54
  • You can't cast char pointers to int pointers like that, because it could be that the pointer is not properly aligned. On x86, you will pay a performance penalty, on ARM you get a bus error. However, you could memcpy() the bytes onto an integer, but I doubt that this improves speed... – Ulrich Eckhardt Dec 22 '14 at 21:58
0

Try running the code both ways and see. You may find that they run at the same speed! This kind of test is often memory bandwidth limited, so the number of instructions executed in the loop hardly matter at all.

It's important to note that this only works if the string values have been padded with nulls out to the size of the integer, which you state as a comment to a different answer.

As for whether it's legal C++, see Aliasing `T*` with `char*` is allowed. Is it also allowed the other way around?

Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622