diff --git a/erpcgen/src/Generator.cpp b/erpcgen/src/Generator.cpp index 1feae7975..e0579a3b3 100644 --- a/erpcgen/src/Generator.cpp +++ b/erpcgen/src/Generator.cpp @@ -471,6 +471,10 @@ data_list Generator::makeGroupInterfacesTemplateData(Group *group) } } + // Generate compile-time hash table for this interface + data_map hashTableInfo = generateHashTableData(iface, functions); + ifaceInfo["hashTable"] = hashTableInfo; + interfaces.push_back(ifaceInfo); } @@ -759,3 +763,132 @@ void Generator::getCallbacksTemplateData(Group *group, const Interface *iface, d } } } + +data_map Generator::generateHashTableData(Interface *iface, const data_list &functions) +{ + data_map hashTableInfo; + + // Get function count and calculate optimal hash table size + uint32_t functionCount = static_cast(functions.size()); + uint32_t hashTableSize = ERPC_HASH_TABLE_MIN_SIZE; + + // Find next power of 2 that gives target load factor + while (hashTableSize * ERPC_HASH_TABLE_TARGET_LOAD_FACTOR < functionCount && + hashTableSize < ERPC_HASH_TABLE_MAX_SIZE) + { + hashTableSize *= 2; + } + + // Check if we hit the size limit and report performance impact + double loadFactor = static_cast(functionCount) / hashTableSize; + if (hashTableSize >= ERPC_HASH_TABLE_MAX_SIZE && loadFactor > ERPC_HASH_TABLE_TARGET_LOAD_FACTOR) { + Log::warning("Interface '%s': Hash table size limit reached (%d functions in %d-entry table)\n" + "Load factor: %.3f, Expected probes: %.1f\n" + "Consider splitting interface for better performance.\n", + iface->getName().c_str(), functionCount, hashTableSize, + loadFactor, 0.5 * (1 + 1/(1-loadFactor))); + } + + Log::info("Hash table for '%s': size=%d, functions=%d, load_factor=%.3f\n", + iface->getName().c_str(), hashTableSize, functionCount, loadFactor); + + hashTableInfo["size"] = make_data(hashTableSize); + hashTableInfo["functionCount"] = make_data(functionCount); + hashTableInfo["loadFactor"] = make_data(static_cast(functionCount) / hashTableSize); + + // Hash function implementation (Knuth multiplicative method) + auto hash_function_id = [hashTableSize](uint32_t id) -> uint32_t { + uint32_t hash = id; + hash = ((hash >> 16) ^ hash) * 0x45d9f3bU; + hash = ((hash >> 16) ^ hash) * 0x45d9f3bU; + hash = (hash >> 16) ^ hash; + return hash & (hashTableSize - 1); + }; + + // Initialize hash table array + vector hashTable(hashTableSize); + for (uint32_t i = 0; i < hashTableSize; i++) + { + data_map entry; + entry["isEmpty"] = make_data(true); + entry["index"] = make_data(i); + entry["id"] = make_data(0U); + entry["name"] = make_data(string("")); + entry["comment"] = make_data(string("Empty")); + entry["probeDistance"] = make_data(0); + hashTable[i] = entry; + } + + // Place functions in hash table using linear probing + uint32_t collisions = 0; + uint32_t maxProbe = 0; + + for (size_t i = 0; i < functions.size(); ++i) + { + data_ptr funcPtr = functions[i]; + assert(dynamic_cast(funcPtr.get().get())); + DataMap *functionData = dynamic_cast(funcPtr.get().get()); + + // Extract function ID and name from template data + uint32_t functionId = static_cast(stoul(functionData->getmap()["id"]->getvalue())); + string functionName = functionData->getmap()["name"]->getvalue(); + + // Calculate hash position + uint32_t hashPos = hash_function_id(functionId); + uint32_t probeDistance = 0; + + // Linear probing collision resolution + while (!hashTable[hashPos]["isEmpty"]->getvalue().empty() && + hashTable[hashPos]["isEmpty"]->getvalue() != "true") + { + hashPos = (hashPos + 1) % hashTableSize; + probeDistance++; + if (probeDistance > 0 && probeDistance == 1) + { + collisions++; + } + } + + // Insert function into hash table + data_map entry; + entry["isEmpty"] = make_data(false); + entry["index"] = make_data(hashPos); + entry["id"] = make_data(functionId); + entry["name"] = make_data(functionName); + entry["probeDistance"] = make_data(probeDistance); + + if (probeDistance == 0) + { + entry["comment"] = make_data(string("Direct hash")); + } + else + { + entry["comment"] = make_data(string("Collision resolved (probe +") + to_string(probeDistance) + ")"); + } + + hashTable[hashPos] = entry; + + if (probeDistance > maxProbe) + { + maxProbe = probeDistance; + } + } + + // Convert hash table to template data + data_list hashTableEntries; + for (const auto& entry : hashTable) + { + hashTableEntries.push_back(entry); + } + + // Statistics + hashTableInfo["entries"] = hashTableEntries; + hashTableInfo["collisions"] = make_data(collisions); + hashTableInfo["maxProbe"] = make_data(maxProbe); + hashTableInfo["primaryHitRate"] = make_data(static_cast(functionCount - collisions) / functionCount * 100.0); + + Log::info("Generated hash table for interface %s: size=%d, functions=%d, collisions=%d, maxProbe=%d\n", + iface->getName().c_str(), hashTableSize, functionCount, collisions, maxProbe); + + return hashTableInfo; +} diff --git a/erpcgen/src/Generator.hpp b/erpcgen/src/Generator.hpp index 18dfb5a03..accda1e8a 100644 --- a/erpcgen/src/Generator.hpp +++ b/erpcgen/src/Generator.hpp @@ -33,6 +33,22 @@ #include #include +//////////////////////////////////////////////////////////////////////////////// +// Hash Table Configuration +//////////////////////////////////////////////////////////////////////////////// + +/*! @brief Maximum hash table size limit (power of 2) */ +#define ERPC_HASH_TABLE_MAX_SIZE 4096 + +/*! @brief Target load factor for optimal performance */ +#define ERPC_HASH_TABLE_TARGET_LOAD_FACTOR 0.65 + +/*! @brief Warning threshold for load factor */ +#define ERPC_HASH_TABLE_WARNING_LOAD_FACTOR 0.75 + +/*! @brief Minimum hash table size */ +#define ERPC_HASH_TABLE_MIN_SIZE 8 + //////////////////////////////////////////////////////////////////////////////// // Classes //////////////////////////////////////////////////////////////////////////////// @@ -407,6 +423,18 @@ class Generator */ void getCallbacksTemplateData(Group *group, const Interface *iface, cpptempl::data_list &callbackTypesInt, cpptempl::data_list &callbackTypesExt, cpptempl::data_list &callbackTypesAll); + + /*! + * @brief Generate compile-time hash table data for efficient function dispatch. + * + * This function generates hash table data similar to your symbols implementation, + * creating a pre-computed static array for O(1) function lookup. + * + * @param[in] iface Interface to generate hash table for + * @param[in] functions List of functions in the interface + * @return Template data for hash table generation + */ + cpptempl::data_map generateHashTableData(Interface *iface, const cpptempl::data_list &functions); }; } // namespace erpcgen diff --git a/erpcgen/src/templates/cpp_interface_header.template b/erpcgen/src/templates/cpp_interface_header.template index 27c528343..1135647f8 100644 --- a/erpcgen/src/templates/cpp_interface_header.template +++ b/erpcgen/src/templates/cpp_interface_header.template @@ -32,9 +32,9 @@ class {$iface.interfaceClassName} {% endfor %} {%endif %} - static const uint8_t m_serviceId = {$iface.id}; + static inline const uint32_t m_serviceId = {$iface.id}; {% for fn in iface.functions %} - static const uint8_t {$getClassFunctionIdName(fn)} = {$fn.id}; + static inline const uint32_t {$getClassFunctionIdName(fn)} = {$fn.id}; {% endfor -- fn %} virtual ~{$iface.interfaceClassName}(void); diff --git a/erpcgen/src/templates/cpp_server_header.template b/erpcgen/src/templates/cpp_server_header.template index adc9bece9..da2b83b42 100644 --- a/erpcgen/src/templates/cpp_server_header.template +++ b/erpcgen/src/templates/cpp_server_header.template @@ -34,6 +34,35 @@ public: virtual erpc_status_t handleInvocation(uint32_t methodId, uint32_t sequence, erpc::Codec * codec, erpc::MessageBufferFactory *messageFactory, erpc::Transport * transport); private: + /*! @brief Function pointer type for shim functions */ + typedef erpc_status_t ({$iface.serviceClassName}::*ShimFunction)(erpc::{$codecClass} * codec, erpc::MessageBufferFactory *messageFactory, erpc::Transport * transport, uint32_t sequence); + + /*! @brief Compile-time hash table entry structure */ + struct FunctionEntry { + uint32_t id; + ShimFunction func; + const char* name; // For debugging + }; + + /*! @brief Hash table size (power of 2, load factor ~0.65) */ + static constexpr uint32_t HASH_TABLE_SIZE = {$iface.hashTable.size}; + static constexpr uint32_t FUNCTION_COUNT = {$iface.hashTable.functionCount}; + + /*! @brief Compile-time hash function (Knuth multiplicative method) */ + static constexpr uint32_t hash_function_id(uint32_t id) { + uint32_t hash = id; + hash = ((hash >> 16) ^ hash) * 0x45d9f3bU; + hash = ((hash >> 16) ^ hash) * 0x45d9f3bU; + hash = (hash >> 16) ^ hash; + return hash & (HASH_TABLE_SIZE - 1); + } + + /*! @brief Get the pre-computed hash table */ + static const FunctionEntry* getFunctionTable(); + + /*! @brief Find function by ID using compile-time hash table */ + ShimFunction findFunction(uint32_t methodId) const; + {$iface.interfaceClassName} *m_handler; {% for fn in iface.functions %} /*! @brief Server shim for {$fn.name} of {$iface.name} interface. */ diff --git a/erpcgen/src/templates/cpp_server_source.template b/erpcgen/src/templates/cpp_server_source.template index 8667e2bdb..136b039d7 100644 --- a/erpcgen/src/templates/cpp_server_source.template +++ b/erpcgen/src/templates/cpp_server_source.template @@ -206,6 +206,54 @@ static erpc_status_t {$iface.interfaceClassName}_{$cb.name}_shim({$iface.interfa { } +// Get the pre-computed compile-time hash table +const {$iface.serviceClassName}::FunctionEntry* {$iface.serviceClassName}::getFunctionTable() +{ + /* + * Compile-time computed hash table with collision resolution + * Hash table size: {$iface.hashTable.size} + * Function count: {$iface.hashTable.functionCount} + * Load factor: {$iface.hashTable.loadFactor} + * Collisions resolved: {$iface.hashTable.collisions} + * Maximum probe distance: {$iface.hashTable.maxProbe} + * Primary hit rate: {$iface.hashTable.primaryHitRate}% + */ + static const FunctionEntry s_functionTable[HASH_TABLE_SIZE] = { +{% for entry in iface.hashTable.entries %} +{% if entry.isEmpty %} + /* Index {$entry.index} - {$entry.comment} */ + {0U, nullptr, nullptr}, +{% else %} + /* Index {$entry.index} - {$entry.comment} */ + {{$entry.id}U, &{$iface.serviceClassName}::{$entry.name}_shim, "{$entry.name}"}, +{% endif %} +{% endfor -- entry %} + }; + return s_functionTable; +} + +// Find function by ID using compile-time hash table with linear probing +{$iface.serviceClassName}::ShimFunction {$iface.serviceClassName}::findFunction(uint32_t methodId) const +{ + const FunctionEntry* table = getFunctionTable(); + uint32_t hash_pos = hash_function_id(methodId); + uint32_t original_pos = hash_pos; + + // Linear probing search (same as your symbols implementation) + do { + if (table[hash_pos].id == methodId && table[hash_pos].func != nullptr) { + return table[hash_pos].func; + } + if (table[hash_pos].id == 0) { // Empty slot + break; + } + // Move to next slot + hash_pos = (hash_pos + 1) & (HASH_TABLE_SIZE - 1); + } while (hash_pos != original_pos); + + return nullptr; // Not found +} + // return service interface handler. {$iface.interfaceClassName}* {$iface.serviceClassName}::getHandler(void) { @@ -215,28 +263,22 @@ static erpc_status_t {$iface.interfaceClassName}_{$cb.name}_shim({$iface.interfa // Call the correct server shim based on method unique ID. erpc_status_t {$iface.serviceClassName}::handleInvocation(uint32_t methodId, uint32_t sequence, Codec * codec, MessageBufferFactory *messageFactory, Transport * transport) { - erpc_status_t erpcStatus; {% if codecClass != "Codec" %} {$codecClass} *_codec = static_cast<{$codecClass} *>(codec); -{% endif %} - switch (methodId) +{% endif %} + + // O(1) compile-time hash table lookup for function dispatch + ShimFunction shimFunc = findFunction(methodId); + if (shimFunc != nullptr) { -{% for fn in iface.functions %} - case {$iface.interfaceClassName}::{$getClassFunctionIdName(fn)}: - { - erpcStatus = {$fn.name}_shim({%if codecClass == "Codec" %}codec{% else %}_codec{% endif %}, messageFactory, transport, sequence); - break; - } - -{% endfor -- fn %} - default: - { - erpcStatus = kErpcStatus_InvalidArgument; - break; - } + // Call the found shim function using function pointer + return (this->*shimFunc)({%if codecClass == "Codec" %}codec{% else %}_codec{% endif %}, messageFactory, transport, sequence); + } + else + { + // Method ID not found + return kErpcStatus_InvalidArgument; } - - return erpcStatus; } {% for fn in iface.functions %}