Reputation: 3580
I have this function that will print a 2D text in OpenGL
void Text2D::printText(const BMfont &font, const char *text, const PenList &pen_list);
I have several applications(games) that uses this a lot, but I never passed an empty string to it but now. Most of the the string
I used is the std::string::c_str()
.
And also, I have these variants for null checking (or empty string checking).
if (text[0] == '\0') return; // # 1
if (text == '\0') return; // # 2
if (text == nullptr) return; // # 3
This will be the first instruction to be executed upon calling the function Text2D::printText()
All of those check causes a CPU usage of about 50% except for # 1
. I'm pretty sure it is and this is very odd to me.
What's the difference between those 3? I thought # 1
and # 2
are the same and I thought the # 3
will convert the nullptr
to '\0'
? Why # 2
and # 3
cost too much CPU usage? What is the proper and safe way to check for an empty C-string
?
Upvotes: 0
Views: 378
Reputation: 3504
There is no way (1) (the check alone, not execution after that) can be faster than (2) and (3). It is logical to think that following the pointer would take more cycles than just checking the address value.
Check for example the code generated for x86.
if (text == '\0') return -1; // # 2
01028C6E cmp dword ptr [text],0
01028C72 jne printText+29h (01028C79h)
01028C74 or eax,0FFFFFFFFh
01028C77 jmp printText+4Bh (01028C9Bh)
if (text == nullptr) return 0; // # 3
01028C79 cmp dword ptr [text],0
01028C7D jne printText+33h (01028C83h)
01028C7F xor eax,eax
01028C81 jmp printText+4Bh (01028C9Bh)
if (text[0] == '\0') return 1; // # 1
01028C83 mov eax,1
01028C88 imul eax,eax,0
01028C8B mov ecx,dword ptr [text]
01028C8E movsx edx,byte ptr [ecx+eax]
01028C92 test edx,edx
01028C94 jne printText+4Bh (01028C9Bh)
01028C96 mov eax,1
Note that, you should not dereference the pointer without checking the pointer itself.
Upvotes: 3
Reputation: 106530
Number 1 dereferences the pointer, numbers 2 and 3 do not. Therefore we would expect the first to take longer if the target of the pointer needs to be fetched from memory.
In general, tooling is very bad about telling you what's going on in microbenchmarks like this.
It should be noted that the first one goes into the string and checks if it is empty; the second two do not. Therefore, if this check is around some big complex piece of code, it is likely any speedup you're seeing is the result of skipping that block when the string is empty (not only when the pointer is null), rather than anything to do with this test.
If you want to consider null to be the same as empty, then you need both tests:
if (str == nullptr || str[0] == '\0') {
// String is empty
}
because str[0]
is undefined if str
is nullptr.
Upvotes: 2