[Development] What's Q_PRIMITIVE_TYPE for?
Giuseppe D'Angelo
giuseppe.dangelo at kdab.com
Wed Nov 11 19:14:26 CET 2020
Hi,
On 11/11/2020 18:14, Thiago Macieira wrote:
> So my recommendation is:
> 1) deprecate Q_PRIMITIVE_TYPE and rename to Q_TRIVIAL_TYPE
> 2)*not* use memset-to-zero construction anywhere
>
> #2 implies changing QPodArrayOps, which does use memset, to use a loop calling
> the default constructor. Two of the four compilers do optimise that into a
> call into memset:https://gcc.godbolt.org/z/Ks3M5h. And there's nothing the
> ICC team likes to work on more than losing on a benchmark.
The problem is that ~100% of our value classes are not trivial, because
we always initialize our data members. So, we need type traits anyhow to
distinguish between primitive/relocatable/complex; and I am against
calling it "Q_TRIVIAL_TYPE" because this property has now nothing to do
with pure triviality.
*Some* trivial types can be initialized via memset(0), but not all of
them, so the set of primitive types (according to our current
definition) and the set trivial types are intersecting (*).
In theory we could just rely on the optimizer to turn
std::uninitialized_value_construct_n into a memset(0). (If you have an
out of line constructor that does 0-bit initialization, and the compiler
doesn't see it and do the transformation, you don't have my sympathies.)
This would, in principle, allow for unifying handling of primitive and
relocatable types:
* Construction: use uninitialized_value_construct
* Primitive: the compiler figures out it's a memset()
* Relocatable: call the default constructor (and possibly the
compiler figures out it's a memset())
* Copy: just use std::uninitialized_copy
* Primitive: the compiler figures out it's a memcpy()
* Relocatable: call the copy constructor (possibly the compiler
figures out it's a memcpy())
* Move: just use std::uninitialized_copy
* Primitive: the compiler figures out it's a memcpy()
* Relocatable: call the move constructor (possibly the compiler
figures out it's a memcpy())
* Relocation (destructive move): use memcpy
* Primitive: we know it works
* Relocatable: we know it works
* Destruction: just use std::destroy
* Primitive: compiler does nothing
* Relocatable: call the destructors (possibly do nothing if trivial)
But as far as I can tell, compilers do not do these transformations as
aggressively as we'd like. So we still have a distinct advantage at
using the trait, at least for the Qt 6 lifetime. Take for instance
QStringView:
> https://gcc.godbolt.org/z/6Taoo4
GCC, ICC, MSVC don't optimize anything. Clang chokes on the
(pointer,int) scenario, but only if the initialization goes through a
constructor. Don't ask me why.
(*) Properly intersecting, in the sense that here are primitive types
that are not trivial (all/most the Q_PRIMITIVE_TYPE in Qt), and trivial
types that are not primitive (the aforementioned class with pointers to
data members).
My 2 c,
--
Giuseppe D'Angelo | giuseppe.dangelo at kdab.com | Senior Software Engineer
KDAB (France) S.A.S., a KDAB Group company
Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com
KDAB - The Qt, C++ and OpenGL Experts
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4329 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.qt-project.org/pipermail/development/attachments/20201111/7ea3d134/attachment.bin>
More information about the Development
mailing list