size_bytes unnecessarily optimizes poorly
JosephBialekMsft opened this issue · comments
The span function size_bytes is currently implemented as follows:
constexpr size_type size_bytes() const noexcept
{
Expects(size() < dynamic_extent / sizeof(element_type));
return size() * sizeof(element_type);
}
This check is attempting to ensure that the multiplication doesn't overflow. This check is not actually needed because it is not possible for "size" to become so large that the overflow occurs. The only way this could happen is if the span was somehow representing more than size_t bytes worth of data but there's no room in the virtual address space for this.
It leads to code optimizing poorly. In my current project I am calling size_bytes and it is actually a function call (I imagine because this code is compiled for size and size_bytes is a lot of code).
binary!gsl::span<unsigned char const ,-1>::size_bytes:
00000001`40434650 4883ec28 sub rsp,28h
00000001`40434654 488b01 mov rax,qword ptr [rcx]
00000001`40434657 4883f8ff cmp rax,0FFFFFFFFFFFFFFFFh
00000001`4043465b 7306 jae binary!gsl::span<unsigned char const ,-1>::size_bytes+0x13 (00000001`40434663)
00000001`4043465d 4883c428 add rsp,28h
00000001`40434661 c3 ret
00000001`40434662 cc int 3
00000001`40434663 e864000000 call binary!gsl::details::terminate (00000001`404346cc)
Notice since this is a span of bytes, there is a comparison to check if rax (the size) is bigger than or equal to 0xFFFFFFFF'FFFFFFFF. There is no way a span could be this big, and this check is resulting in a ton of code generation for what should just be loading the size field and returning it.
Hi @JosephBialekMsft
Thanks for noticing this, I will bring it up at the next maintainer's sync.
Dmitry