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.dang...@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

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

_______________________________________________
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development

Reply via email to