[ https://issues.apache.org/jira/browse/GEODE-8213?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17125993#comment-17125993 ]
ASF GitHub Bot commented on GEODE-8213: --------------------------------------- pivotal-jbarrett commented on a change in pull request #611: URL: https://github.com/apache/geode-native/pull/611#discussion_r435327033 ########## File path: cppcache/src/SerializationRegistry.hpp ########## @@ -73,16 +72,14 @@ using internal::DataSerializableInternal; using internal::DataSerializablePrimitive; class TheTypeMap { - std::unordered_map<internal::DSCode, TypeFactoryMethod> + std::shared_ptr<std::unordered_map<internal::DSCode, TypeFactoryMethod>> Review comment: I am pretty sure you can't perform `std::atomic` operations on a `std::shared_ptr` and expect to get the lifecycle management of `std::shared_ptr`. If you use `std::atomic` to swap out the contents behind this value nothing will update the ref counter and release the memory of the pointed to heap. ########## File path: cppcache/src/SerializationRegistry.cpp ########## @@ -355,45 +363,39 @@ std::shared_ptr<Serializable> SerializationRegistry::GetEnum( } void TheTypeMap::clear() { - std::lock_guard<util::concurrent::spinlock_mutex> guard( - m_dataSerializableMapLock); - m_dataSerializableMap.clear(); + auto tempDataSerializableMap = + std::make_shared<std::unordered_map<int32_t, TypeFactoryMethod>>(); + std::atomic_store(&m_dataSerializableMap, tempDataSerializableMap); - std::lock_guard<util::concurrent::spinlock_mutex> guard2( - m_dataSerializableFixedIdMapLock); - m_dataSerializableFixedIdMap.clear(); + auto tempDataSerializableFixedId = std::make_shared< + std::unordered_map<internal::DSFid, TypeFactoryMethod>>(); + std::atomic_store(&m_dataSerializableFixedIdMap, tempDataSerializableFixedId); - std::lock_guard<util::concurrent::spinlock_mutex> guard3( - m_pdxSerializableMapLock); - m_pdxSerializableMap.clear(); + auto tempPdxSerializableMap = + std::make_shared<std::unordered_map<std::string, TypeFactoryMethodPdx>>(); + std::atomic_store(&m_pdxSerializableMap, tempPdxSerializableMap); } void TheTypeMap::findDataSerializable(int32_t id, TypeFactoryMethod& func) const { - std::lock_guard<util::concurrent::spinlock_mutex> guard( - m_dataSerializableMapLock); - const auto& found = m_dataSerializableMap.find(id); - if (found != m_dataSerializableMap.end()) { + const auto& found = m_dataSerializableMap->find(id); + if (found != m_dataSerializableMap->end()) { Review comment: This is not thread safe. Between calls to `find` and `end` the map currently pointed to by `m_dataSerializableMap` can change. ```C++ auto dataSerializableMap = std::atomic_load(m_dataSerializableMap); const auto& found = dataSerializableMap->find(id); if (found != dataSerializableMap->end()) { ... ``` There are more of these time of access behaviors in this file. ########## File path: cppcache/src/SerializationRegistry.cpp ########## @@ -409,44 +411,56 @@ void TheTypeMap::bindDataSerializable(TypeFactoryMethod func, int32_t id) { "TheTypeMap::bind: Serialization type not implemented."); } - std::lock_guard<util::concurrent::spinlock_mutex> guard( - m_dataSerializableMapLock); - const auto& result = m_dataSerializableMap.emplace(id, func); + auto tempDataSerializableMap = + std::make_shared<std::unordered_map<int32_t, TypeFactoryMethod>>( + *m_dataSerializableMap); + const auto& result = tempDataSerializableMap->emplace(id, func); if (!result.second) { LOGERROR("A class with ID %d is already registered.", id); throw IllegalStateException("A class with given ID is already registered."); } + std::atomic_store(&m_dataSerializableMap, tempDataSerializableMap); Review comment: There is a race condition here that we need to consider. If two threads attempt to update this map they will believe to have successfully done so but only one will truly win. For example, type 1 and 2 are added concurrently, the map with either contain type 1 or type 2 but not both. The thread that tried to add the type that didn't get into the map will not error. This type of copy on write map behavior needs a compare and swap storage. You fetch the current map pointer, clone and add your contents to the map, then if and only if the current map pointer matches the one you fetched previously to you replace it with your new map, otherwise you try again. ```C++ while(true) { const auto currentDataSerializableMap = std::atomic_load(m_ tempDataSerializableMap); auto newDataSerializableMap = clone(currentDataSerializableMap); const auto& result = newDataSerializableMap->emplace(id, func); if (!std:: compare_exchange_strong(&m_dataSerializableMap, currentDataSerializableMap, newDataSerializableMap)) continue; if (!result.second) { ... } break; } ``` Similar goes for any removal or updating operations. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org > C++ native client performance bottleneck in access to serialization registry > ---------------------------------------------------------------------------- > > Key: GEODE-8213 > URL: https://issues.apache.org/jira/browse/GEODE-8213 > Project: Geode > Issue Type: Improvement > Components: native client > Reporter: Alberto Gomez > Assignee: Alberto Gomez > Priority: Major > Fix For: 1.14.0 > > > It's been observed that when the number of threads used in a Geode client > using PdxSerialization is greater than 8, there is an important drop in > performance. > Analysing the client process behavior with perf, it has been observed a very > high CPU consumption by a spinlock > (apache::geode::util::concurrent::spinlock_mutex::lock) used when accessing > the serialization registry . -- This message was sent by Atlassian Jira (v8.3.4#803005)