QHash: optimize value(key) and key(value) callers

... by not injecting potentially-expensive temporary objects into the
caller's stack frame.

Default arguments are a convenient way to avoid overloads, but if the
defaulted argument isn't a Trivial Type, and the common use case is
not to pass the extra argument explicitly, the construction of the
temporary can dominate the call's runtime.

Since QHash is generic code, we don't know whether T or Key are
expensive or cheap to construct, so use overloading instead of default
arguments to avoid injecting needless code into call sites.

[ChangeLog][QtCore][Potentially Source-Incompatible
Changes][QHash/QMultiHash] The value(key) and key(value) functions are
now overloaded on presence of the defaultValue (was: defaulted
argument) to avoid injecting temporary objects into the caller's stack
frame.

Task-number: QTBUG-98117
Change-Id: I80fdd5436f3de3e4bbe20242fe45916aef62ff0c
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Marc Mutz 2021-11-09 11:04:46 +01:00
parent 14b0e2fb1e
commit 7fe5611365
2 changed files with 99 additions and 25 deletions

View File

@ -1865,14 +1865,15 @@ size_t qHash(long double key, size_t seed) noexcept
\sa count(), QMultiHash::contains()
*/
/*! \fn template <class Key, class T> T QHash<Key, T>::value(const Key &key, const T &defaultValue = T()) const
/*! \fn template <class Key, class T> T QHash<Key, T>::value(const Key &key) const
\fn template <class Key, class T> T QHash<Key, T>::value(const Key &key, const T &defaultValue) const
\overload
Returns the value associated with the \a key.
If the hash contains no item with the \a key, the function
returns \a defaultValue, which is a \l{default-constructed value} if the
parameter has not been specified.
returns \a defaultValue, or a \l{default-constructed value} if this
parameter has not been supplied.
*/
/*! \fn template <class Key, class T> T &QHash<Key, T>::operator[](const Key &key)
@ -1935,11 +1936,13 @@ size_t qHash(long double key, size_t seed) noexcept
*/
/*!
\fn template <class Key, class T> Key QHash<Key, T>::key(const T &value, const Key &defaultKey = Key()) const
\fn template <class Key, class T> Key QHash<Key, T>::key(const T &value) const
\fn template <class Key, class T> Key QHash<Key, T>::key(const T &value, const Key &defaultKey) const
\since 4.3
Returns the first key mapped to \a value, or \a defaultKey if the
hash contains no item mapped to \a value.
Returns the first key mapped to \a value. If the hash contains no item
mapped to \a value, returns \a defaultKey, or a \l{default-constructed
value}{default-constructed key} if this parameter has not been supplied.
This function can be slow (\l{linear time}), because QHash's
internal data structure is optimized for fast lookup by key, not
@ -2899,14 +2902,14 @@ size_t qHash(long double key, size_t seed) noexcept
\sa keys(), values()
*/
/*! \fn template <class Key, class T> T QMultiHash<Key, T>::value(const Key &key, const T &defaultValue = T()) const
\overload
/*! \fn template <class Key, class T> T QMultiHash<Key, T>::value(const Key &key) const
\fn template <class Key, class T> T QMultiHash<Key, T>::value(const Key &key, const T &defaultValue) const
Returns the value associated with the \a key.
If the hash contains no item with the \a key, the function
returns \a defaultValue, which is a \l{default-constructed value} if the
parameter has not been specified.
returns \a defaultValue, or a \l{default-constructed value} if this
parameter has not been supplied.
If there are multiple
items for the \a key in the hash, the value of the most recently
@ -3054,11 +3057,13 @@ size_t qHash(long double key, size_t seed) noexcept
*/
/*!
\fn template <class Key, class T> Key QMultiHash<Key, T>::key(const T &value, const Key &defaultKey = Key()) const
\fn template <class Key, class T> Key QMultiHash<Key, T>::key(const T &value) const
\fn template <class Key, class T> Key QMultiHash<Key, T>::key(const T &value, const Key &defaultKey) const
\since 4.3
Returns the first key mapped to \a value, or \a defaultKey if the
hash contains no item mapped to \a value.
Returns the first key mapped to \a value. If the hash contains no item
mapped to \a value, returns \a defaultKey, or a \l{default-constructed
value}{default-constructed key} if this parameter has not been supplied.
This function can be slow (\l{linear time}), because QMultiHash's
internal data structure is optimized for fast lookup by key, not

View File

@ -926,28 +926,64 @@ public:
return contains(key) ? 1 : 0;
}
Key key(const T &value, const Key &defaultKey = Key()) const noexcept
private:
const Key *keyImpl(const T &value) const noexcept
{
if (d) {
const_iterator i = begin();
while (i != end()) {
if (i.value() == value)
return i.key();
return &i.key();
++i;
}
}
return defaultKey;
return nullptr;
}
T value(const Key &key, const T &defaultValue = T()) const noexcept
public:
Key key(const T &value) const noexcept
{
if (auto *k = keyImpl(value))
return *k;
else
return Key();
}
Key key(const T &value, const Key &defaultKey) const noexcept
{
if (auto *k = keyImpl(value))
return *k;
else
return defaultKey;
}
private:
T *valueImpl(const Key &key) const noexcept
{
if (d) {
Node *n = d->findNode(key);
if (n)
return n->value;
return &n->value;
}
return defaultValue;
return nullptr;
}
public:
T value(const Key &key) const noexcept
{
if (T *v = valueImpl(key))
return *v;
else
return T();
}
T value(const Key &key, const T &defaultValue) const noexcept
{
if (T *v = valueImpl(key))
return *v;
else
return defaultValue;
}
T &operator[](const Key &key)
{
detach();
@ -1427,30 +1463,63 @@ public:
return d->findNode(key) != nullptr;
}
Key key(const T &value, const Key &defaultKey = Key()) const noexcept
private:
const Key *keyImpl(const T &value) const noexcept
{
if (d) {
auto i = d->begin();
while (i != d->end()) {
Chain *e = i.node()->value;
if (e->contains(value))
return i.node()->key;
return &i.node()->key;
++i;
}
}
return defaultKey;
return nullptr;
}
T value(const Key &key, const T &defaultValue = T()) const noexcept
public:
Key key(const T &value) const noexcept
{
if (auto *k = keyImpl(value))
return *k;
else
return Key();
}
Key key(const T &value, const Key &defaultKey) const noexcept
{
if (auto *k = keyImpl(value))
return *k;
else
return defaultKey;
}
private:
T *valueImpl(const Key &key) const noexcept
{
if (d) {
Node *n = d->findNode(key);
if (n) {
Q_ASSERT(n->value);
return n->value->value;
return &n->value->value;
}
}
return defaultValue;
return nullptr;
}
public:
T value(const Key &key) const noexcept
{
if (auto *v = valueImpl(key))
return *v;
else
return T();
}
T value(const Key &key, const T &defaultValue) const noexcept
{
if (auto *v = valueImpl(key))
return *v;
else
return defaultValue;
}
T &operator[](const Key &key)