From b61cc2bdb904d81e12f52b5cfd2a6bb5c9a01b23 Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Tue, 20 Feb 2018 16:34:16 +0100 Subject: [PATCH] feat(address): add a sip addresses cache to increase performance (+50%) --- src/CMakeLists.txt | 1 + src/address/address-p.h | 2 + src/address/address.cpp | 53 +++++++++++++++++- src/containers/lru-cache.h | 107 +++++++++++++++++++++++++++++++++++++ src/core/core.cpp | 3 ++ 5 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/containers/lru-cache.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8736b6ff..d6dd47a28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/session/call-session.h conference/session/media-session.h conference/session/port-config.h + containers/lru-cache.h content/content-manager.h content/content-p.h content/content-type.h diff --git a/src/address/address-p.h b/src/address/address-p.h index a0c10d9a5..64d7d1ccf 100644 --- a/src/address/address-p.h +++ b/src/address/address-p.h @@ -38,6 +38,8 @@ public: } void setInternalAddress (const SalAddress *value); + static void clearSipAddressesCache (); + private: struct AddressCache { std::string scheme; diff --git a/src/address/address.cpp b/src/address/address.cpp index f35893b62..4d263aa73 100644 --- a/src/address/address.cpp +++ b/src/address/address.cpp @@ -23,6 +23,7 @@ #include "address/identity-address.h" #include "c-wrapper/c-wrapper.h" #include "c-wrapper/internal/c-sal.h" +#include "containers/lru-cache.h" #include "logger/logger.h" // ============================================================================= @@ -31,6 +32,49 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE +namespace { + class SalAddressWrap { + public: + explicit SalAddressWrap (SalAddress *salAddress = nullptr) : mSalAddress(salAddress) {} + + SalAddressWrap (const SalAddressWrap &other) : mSalAddress(other.mSalAddress) { + if (mSalAddress) + sal_address_ref(mSalAddress); + } + + SalAddressWrap (SalAddressWrap &&other) : mSalAddress(other.mSalAddress) { + other.mSalAddress = nullptr; + } + + ~SalAddressWrap () { + if (mSalAddress) + sal_address_unref(mSalAddress); + } + + const SalAddress *get () { + return mSalAddress; + } + + private: + SalAddress *mSalAddress; + }; + LruCache addressesCache; +} + +static SalAddress *getSalAddressFromCache (const string &uri) { + SalAddressWrap *wrap = addressesCache[uri]; + if (wrap) + return sal_address_clone(wrap->get()); + + SalAddress *address = sal_address_new(L_STRING_TO_C(uri)); + if (address) { + addressesCache.insert(uri, SalAddressWrap(address)); + return sal_address_clone(address); + } + + return nullptr; +} + // ----------------------------------------------------------------------------- void AddressPrivate::setInternalAddress (const SalAddress *addr) { @@ -39,11 +83,16 @@ void AddressPrivate::setInternalAddress (const SalAddress *addr) { internalAddress = sal_address_clone(addr); } +void AddressPrivate::clearSipAddressesCache () { + addressesCache.clear(); +} + // ----------------------------------------------------------------------------- Address::Address (const string &address) : ClonableObject(*new AddressPrivate) { L_D(); - if (!(d->internalAddress = sal_address_new(L_STRING_TO_C(address)))) { + + if (!(d->internalAddress = getSalAddressFromCache(address))) { lWarning() << "Cannot create Address, bad uri [" << address << "]"; } } @@ -65,7 +114,7 @@ Address::Address (const IdentityAddress &identityAddress) : ClonableObject(*new if (identityAddress.hasGruu()) uri += ";gr=" + identityAddress.getGruu(); - d->internalAddress = sal_address_new(L_STRING_TO_C(uri)); + d->internalAddress = getSalAddressFromCache(uri); } Address::Address (const Address &other) : ClonableObject(*new AddressPrivate) { diff --git a/src/containers/lru-cache.h b/src/containers/lru-cache.h new file mode 100644 index 000000000..5bab2c5f1 --- /dev/null +++ b/src/containers/lru-cache.h @@ -0,0 +1,107 @@ +/* + * lru-cache.h + * Copyright (C) 2010-2018 Belledonne Communications SARL + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _L_LRU_CACHE_H_ +#define _L_LRU_CACHE_H_ + +#include +#include + +#include "linphone/utils/general.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +template +class LruCache { +public: + LruCache (int capacity = DefaultCapacity) : mCapacity(capacity) { + if (capacity < MinCapacity) + capacity = MinCapacity; + } + + int getCapacity () const { + return mCapacity; + } + + int getSize () const { + return int(mKeyToPair.size()); + } + + Value *operator[] (const Key &key) { + auto it = mKeyToPair.find(key); + return it == mKeyToPair.end() ? nullptr : &it->second.second; + } + + const Value *operator[] (const Key &key) const { + auto it = mKeyToPair.find(key); + return it == mKeyToPair.cend() ? nullptr : &it->second.second; + } + + void insert (const Key &key, const Value &value) { + auto it = mKeyToPair.find(key); + if (it != mKeyToPair.end()) + mKeys.erase(it->second.first); + else if (int(mKeyToPair.size()) == mCapacity) { + Key lastKey = mKeys.back(); + mKeys.pop_back(); + mKeyToPair.erase(lastKey); + } + + mKeys.push_front(key); + mKeyToPair.insert({ key, { mKeys.begin(), value } }); + } + + void insert (const Key &key, Value &&value) { + auto it = mKeyToPair.find(key); + if (it != mKeyToPair.end()) + mKeys.erase(it->second.first); + else if (int(mKeyToPair.size()) == mCapacity) { + Key lastKey = mKeys.back(); + mKeys.pop_back(); + mKeyToPair.erase(lastKey); + } + + mKeys.push_front(key); + mKeyToPair.insert({ key, std::make_pair(mKeys.begin(), std::move(value)) }); + } + + void clear () { + mKeyToPair.clear(); + mKeys.clear(); + } + + static constexpr int MinCapacity = 10; + static constexpr int DefaultCapacity = 1000; + +private: + using Pair = std::pair::iterator, Value>; + + const int mCapacity; + + // See: https://stackoverflow.com/questions/16781886/can-we-store-unordered-maptiterator + // Do not store iterator key. + std::list mKeys; + std::unordered_map mKeyToPair; +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_LRU_CACHE_H_ diff --git a/src/core/core.cpp b/src/core/core.cpp index eebb785de..d339db5cf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -20,6 +20,7 @@ #include #include +#include "address/address-p.h" #include "call/call.h" #include "core/core-listener.h" #include "core/core-p.h" @@ -75,6 +76,8 @@ void CorePrivate::uninit () { linphone_core_iterate(L_GET_C_BACK_PTR(q)); ms_usleep(10000); } + + AddressPrivate::clearSipAddressesCache(); } // -----------------------------------------------------------------------------