[Development] Deprecation/removal model going into Qt 6
Kevin Kofler
kevin.kofler at chello.at
Mon Jun 3 13:34:27 CEST 2019
Giuseppe D'Angelo via Development wrote:
> Il 03/06/19 00:08, Kevin Kofler ha scritto:
>> What you call "obsolete functionality" is functionality that existing
>> code relies on and rightfully expects to remain there.
>
> Rightfully? By what right exactly?
APIs in libraries are meant to be used. I consider it an entirely reasonable
expectation by developers using the APIs that they will not be removed under
them because the library developers consider them "obsolete". Imagine the
chaos if Intel or AMD decided to remove some random "obsolete" x86
instructions from their CPUs! x86 has kept backwards compatibility with
every single instruction for more than 30 years. This is the standard
software libraries should be held to, too.
>> I'd rather get fewer (or even no) new features than losing existing ones.
>
> How is this even an argument? Qt will need to evolve and acquire
> features to remain competitive. Again, development bandwidth is finite:
> either the overall quality decreases or some things have to get dropped.
Qt has long reached a point where it can be considered complete. Its main
selling point is portability to many different platforms rather than some
specific feature. Additional features don't necessarily need to be in the
main Qt library, but can be in community-developed addons such as KDE
Frameworks or such as the many third-party Qt-based Free Software libraries
out there. (They can also be in Qt-Company-developed Qt Solutions if there
is manpower left for that.)
Qt has also become larger and larger over time (despite the removal of APIs
considered obsolete). Just compare the size of the Qt 3 tarball with the
size of the Qt 5 monolithic tarball. This is not the result of keeping old
APIs around, but of feature creep.
So I disagree with the assertion that Qt needs more features to remain
competitive.
>> See also Boudewijn Rempt's blog post on the subject:
>> https://valdyas.org/fading/hacking/happy-porting/
>
> I agree with the principle (API breaks are painful), but I strongly
> disagree with the idea that no API breaks can ever possibly happen. And
> the specific example is a terrible one to make a point as the resulting
> API break is trivial to work around (I defined such breakages
> "scriptable").
The Q_FOREACH to ranged for change is not as easy to port to as people
think, because there are at least 2 pitfalls when porting to ranged for:
1. you have to add qAsConst or equivalent or you will be deep-copying your
implicitly-shared CoW container,
2. code that was changing the container during the iteration, which worked
just fine with Q_FOREACH (because the iteration would still be over the
original unchanged container), will now crash without warning. Even
qAsConst will not help you get a warning or error for it, because it only
constifies the reference for the ranged for itself and not for the code
within it.
This is effectively deprecating a safe construct for an unsafe one.
>> An array of pointers is the most efficient data structure in practice
>> (operations are at most O(n)), dropping it in favor of an O(mn) data
>> structure (where m = sizeof(T)) such as QVector is a pessimization. And
>> QList also has the prepend optimization that makes most prepends even
>> O(1) rather than O(n). I don't see why almost everybody hates it.
>
> As written, the above makes no sense, as it looks like you're comparing
> apples and oranges: time complexities against space complexities.
I'm speaking exclusively of time complexities. The space only matters when
it goes into the formula for the time, which is the case for QVector.
QList::insert and QList::removeAt have O(n) time complexity.
QVector::insert and QVector::removeAt have O(mn) time complexity.
QList::prepend has O(1) amortized and O(n) worst case time complexity.
QVector::prepend has O(mn) (always!) time complexity.
If you are dealing with a large class or struct, e.g. 800 bytes, then the
QVector operations are 100 times slower than the QList ones!
> The fact is: once one removes the big-O factors and deals with actual
> numbers and real world hardware, QVector becomes much better than QList
> as a _general purpose_ sequential container. Emphasis on the general
> purpose, please.
For me, the best general purpose container is the one that makes it hardest
to run into big performance bottlenecks. The point being that it should be
GENERAL purpose, i.e., work efficiently for as many use cases as possible,
even if it requires compromises for some common ones. So an O(n) container
is better than an O(mn) one, even if it is often marginally slower. And a
container with prepend optimization is better than one without it.
I used array-of-pointer data structures almost exclusively even in plain C
code and before Qt even introduced QList as it stands now. I find QList's
API that hides the pointer dereferences, the allocations to hold the value
copies, etc. (i.e., all the tedious parts of array-of-pointer
implementation) from me extremely useful and will be sad to see it go away.
> Hence, we want Qt to move away from QList (and encourage users to do the
> same). The point of this thread, once more, was asking how to do that as
> painlessly as possible.
And my answer is that the only painless way is to just not do that to begin
with. No matter how you remove or change QList, it will break lots and lots
of existing code and take away a useful API from new code.
>> For the "unnecessary" part, because Qt has been working fine without
>> QString SSO for years.
>
> Nice try: https://en.wikipedia.org/wiki/Appeal_to_tradition
This is not an appeal to tradition, but to practical experience. QString is
working well in thousands of software packages now. Of course, that doesn't
imply that it is not possible to do better, in theory. But is this
optimization worth the trouble of breaking the ABI? (Keep in mind that my
argument is that Qt 6 should either be source&binary-compatible with Qt 5
(with only the CMake build system as the change justifying the major
version) or not exist at all (i.e., be called 5.13 instead). Of course, if
you are breaking the ABI anyway, then one ABI breakage more or less won't
matter.)
> Second, string classes in all major C++ libraries and frameworks are
> deploying SSO *because* it is a performance win.
SSO is a clear performance win for std::string because std::string is NOT
CoW (in fact, g++'s implementation used to be, but it was dropped because
some obscure "clarification" in C++11 is interpreted as forbidding it). So
you have to deep-copy anyway, and SSO saves the allocation. But SSO bypasses
CoW, so is only a win on the current architectures with ridiculously slow
atomics and fast bulk copies. If atomics ever get optimized in a new CPU
architecture, you'll wish the old QString back.
>> For the "probably also counterproductive" part:
>> * Because there are surely architectures or environments where copying
>> 256 bytes (or whatever the SSO max length actually is)
>
> This is a straw man argument, specifically an exaggeration.
>
> At some QtCS Thiago was talking about 23-24 QChars, i.e. 48 bytes, plus
> a couple of pointers or so, to bring it to 64 bytes (~ a cacheline).
"or whatever the SSO max length actually is". So 64 bytes it is. Still 8
times more than before. This is only faster because current architectures
suck at atomics.
>> * Because the total memory use for an array of QString will likely be
>> higher, due to the padding (space reserved for SSO)?
>
> ... or much, much, much lower because you don't have to allocate every
> string's payload separately.
This depends on the efficiency of your platform's malloc/new, and on your
reservation strategy (do you optimize for memory use and allocate the exact
string size initially or do you immediately reserve space for fast append?).
Of course, if your platform allocator always pads to 64+ bytes, then no
matter what you do, the memory use of non-SSO QString will be the higher
one. This is platform-dependent.
> This thread was about managing API breaks. Adding SSO to QString is not
> meant to be an API break (*). Please stop derailing the thread.
>
> (*) Emphasis on _meant_, because obviously it yields observable side
> effects.
This thread was also about managing ABI breaks, which QString SSO definitely
is.
Kevin Kofler
More information about the Development
mailing list