tags:

views:

135

answers:

2

I'm currently working in a codebase where IPv4 addresses are represented as pointers to u_int8. The equality operator is implemented like this:

bool Ipv4Address::operator==(const u_int8 * inAddress) const
{
    return (*(u_int32*) this->myBytes == *(u_int32*) inAddress);
}

This is probably the fasted solution, but it causes the GCC compiler warning:

ipv4address.cpp:65: warning: dereferencing type-punned pointer will break strict-aliasing rules

How can I rewrite the comparison correctly without breaking strict-aliasing rules and without losing performance points?

I have considered using either memcmp or this macro:

#define IS_EQUAL(a, b) \
    (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])

I'm thinking that the macro is the fastest solution.

What do you recommend?

Update
I just read the article Squeezing performance out of memcmp usage which explains how the compiler (Visual Studio, but perhaps also GCC) can optimize !memcmp(..) calls.

+10  A: 

I would go for memcmp()

  1. It is more portable
  2. I usually try not to be smarter than my compiler/language. You are trying to compare memory contents and (depending on compiler options too) the implementation of memcmp() should be the most efficient way to do that.

Also think that if your compiler does not inline memcmp() you will suffer the function context switch

Are you sure you need to optimize that hard? Have you already checked that your program spend most of its time doing that type of operations?

Francisco Garcia
Yep, `std::memcmp()` is what the std lib has for comparing arrays of built-ins. +1 from me. If profiling shows it is too slow on a certain architecture, you can always go back and change it. I doubt it, though.
sbi
Being a good <insert language> programmer means knowing how to use the tools provided in the standard toolkit. Also, premature optimization is the root of all evil. I know those sound like textbook responses, but they're both so important and underused, even by the best of us at times, that they are worth repeating... again... and again...
glowcoder
Comparing the IP address `uint8` by `uint8` is also portable. Small amount of comparisons is usually more efficient than a library function call; although only profiling or an assembly language listing will show the proof.
Thomas Matthews
@Thomas: I'd expect `std::memcmp()` to be an intrinsic in any decent implementation.
sbi
+2  A: 

The reason you are getting an error from GCC is that anything that is more than 1 byte in length likes to be aligned to an address that is a multiple of the object size. A 32-bit integer likes to start on 32-bit boundaries. A char variable (signed, unsigned or plain), can be on any byte boundary, such as 3 which does not play nice for 32-bit fetches by a processor.

In your case, for 4 bytes (32-bits), there may be more overhead in calling memcmp than code to actually compare the bytes.

Try this:

bool Ipv4Address::operator==(const u_int8 * inAddress) const
{
    return myBytes[0] == inAddress[0]
        && myBytes[1] == inAddress[1]
        && myBytes[2] == inAddress[2]
        && myBytes[3] == inAddress[3];
}

Look Mom, member function code without using this-> !

As for efficiency, this code can probably be executed in the same time that a call is made to memcpy and the return executed from it (without executing the content of memcpy). This is assuming that memcpy is not inlined. Knowing how compiler libraries are written for generic and large cases, I suspect that this code is still smaller and faster than an inlined version of memcpy. Although the proof is to print an assembly listing of the two versions and compare.

Edit:
Note: declaring the implementation as inline or placing the code in the class declaration, will be better than defining a dangerous macro. It will be safer and contain the same amount of code. I like the inline method version because it is more readable and easier to maintain.

Thomas Matthews