strager.net

C++ std::string::empty

There are a few common ways check if a C++ std::string is empty (i.e. containing no characters):

Let's compare some implementations of std::string.

Assembly showdown

Code generated for different std::string implementations and compilers
Library Compiler s.empty() s.size() == 0 s == std::string() s == ""
libstdc++ GCC 9.3 x86_64 -O2
is_string_empty:
  cmp QWORD PTR [rdi+8], 0
  sete al
  ret
.LC0:
  .string ""
is_string_empty:
  sub rsp, 8
  mov esi, OFFSET FLAT:.LC0
  call std::string::compare
  test eax, eax
  sete al
  add rsp, 8
  ret
Clang 10.0.0 x86_64 -O2
is_string_empty:
  cmp QWORD PTR [rdi+8], 0
  sete al
  ret
is_string_empty:
  push rax
  mov esi, OFFSET .L.str
  call std::string::compare
  test eax, eax
  sete al
  pop rcx
  ret
.L.str:
  .zero 1
Library Compiler s.empty() s.size() == 0 s == std::string() s == ""
libc++ Clang 10.0.0 x86_64 -O2
is_string_empty:
  movzx eax, BYTE PTR [rdi]
  test al, 1
  je .LBB0_1
  mov rax, [rdi+8]
  jmp .LBB0_3
.LBB0_1:
  shr rax
.LBB0_3:
  test rax, rax
  sete al
  ret
is_string_empty:
  push rax
  movzx eax, BYTE PTR [rdi]
  test al, 1
  je .LBB0_4
  mov rax, [rdi+8]
  test rax, rax
  je .LBB0_5
.LBB0_2:
  xor eax, eax
  pop rcx
  ret
.LBB0_4:
  shr rax
  test rax, rax
  jne .LBB0_2
.LBB0_5:
  mov ecx, OFFSET .L.str
  xor esi, esi
  mov rdx, -1
  xor r8d, r8d
  call std::string::compare
  test eax, eax
  sete al
  pop rcx
  ret
  mov rdi, rax
  call __clang_call_terminate
Clang 10.0.0 x86_64 -O2 -fno-exceptions
is_string_empty:
  push rax
  movzx eax, BYTE PTR [rdi]
  test al, 1
  je .LBB0_4
  mov rax, [rdi+8]
  test rax, rax
  je .LBB0_5
.LBB0_2:
  xor eax, eax
  pop rcx
  ret
.LBB0_4:
  shr rax
  test rax, rax
  jne .LBB0_2
.LBB0_5:
  mov ecx, OFFSET .L.str
  xor esi, esi
  mov rdx, -1
  xor r8d, r8d
  call std::string::compare
  test eax, eax
  sete al
  pop rcx
  ret
.L.str:
  .zero 1
Library Compiler s.empty() s.size() == 0 s == std::string() s == ""
STL MSVC v19.24 x64 /O2t
is_string_empty:
  cmp QWORD PTR [rcx+16], 0
  sete al
  ret 0
$T1 = 8
__$ArrayPad$ = 40
s$ = 64
is_string_empty:
  sub rsp, 56
  mov rax, __security_cookie
  xor rax, rsp
  mov __$ArrayPad$[rsp], rax
  cmp QWORD PTR [rcx+16], 0
  mov QWORD PTR $T1[rsp+16], 0
  mov QWORD PTR $T1[rsp+24], 15
  mov BYTE PTR $T1[rsp], 0
  jne SHORT $LN29@is_string_
  mov al, 1
  mov rcx, __$ArrayPad$[rsp]
  xor rcx, rsp
  call __security_check_cookie
  add rsp, 56
  ret 0
$LN29@is_string_:
  xor al, al
  mov rcx, __$ArrayPad$[rsp]
  xor rcx, rsp
  call __security_check_cookie
  add rsp, 56
  ret 0
is_string_empty:
  cmp QWORD PTR [rcx+16], 0
  jne SHORT $LN16@is_string_
  mov al, 1
  ret 0
$LN16@is_string_:
  xor al, al
  ret 0
MSVC v19.24 x64 /O2t /GS-
$T1 = 0
s$ = 48
is_string_empty:
$LN63:
  sub rsp, 40
  cmp QWORD PTR [rcx+16], 0
  mov QWORD PTR $T1[rsp+16], 0
  mov QWORD PTR $T1[rsp+24], 15
  mov BYTE PTR $T1[rsp], 0
  jne SHORT $LN29@empty_by_c
  mov al, 1
  add rsp, 40
  ret 0
$LN29@empty_by_c:
  xor al, al
  add rsp, 40
  ret 0
Library Compiler s.empty() s.size() == 0 s == std::string() s == ""

Observations and analysis

Summary of algorithms for different std::string implementations
std::string library s.empty() s.size() == 0 s == std::string() s == ""
libstdc++ load size and test call std::string::compare (not inlined)
libc++ load and test SSO discriminator; branch and test size load and test SSO discriminator; branch and test size; std::string::compare (not inlined)
STL load size and test load size and test; dead stores to stack; branch load size and test; branch