From 0997826de8c315c155deaebd96f15471d234ff14 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 23 Jun 2023 23:02:44 -0500 Subject: [PATCH 1/5] GH-1251 Use deque instead of unordered_set to process contracts in order. Prioritize eosio.*. --- .../chain/webassembly/eos-vm-oc/code_cache.hpp | 4 ++-- libraries/chain/wasm_interface.cpp | 2 +- .../webassembly/runtimes/eos-vm-oc/code_cache.cpp | 14 ++++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp index fe7ff49788..1584c96406 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp @@ -80,7 +80,7 @@ class code_cache_base { //these are really only useful to the async code cache, but keep them here so //free_code can be shared - std::unordered_set _queued_compiles; + deque _queued_compiles; std::unordered_map _outstanding_compiles_and_poison; size_t _free_bytes_eviction_threshold; @@ -101,7 +101,7 @@ class code_cache_async : public code_cache_base { //If code is in cache: returns pointer & bumps to front of MRU list //If code is not in cache, and not blacklisted, and not currently compiling: return nullptr and kick off compile //otherwise: return nullptr - const code_descriptor* const get_descriptor_for_code(const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure); + const code_descriptor* const get_descriptor_for_code(const account_name& receiver, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure); private: std::thread _monitor_reply_thread; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index c7aeb46f8e..ba7e268579 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -94,7 +94,7 @@ namespace eosio { namespace chain { const chain::eosvmoc::code_descriptor* cd = nullptr; chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary; try { - cd = my->eosvmoc->cc.get_descriptor_for_code(code_hash, vm_version, context.control.is_write_window(), failure); + cd = my->eosvmoc->cc.get_descriptor_for_code(context.get_receiver(), code_hash, vm_version, context.control.is_write_window(), failure); } catch(...) { //swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp index a43f8ac932..cab0728030 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -106,7 +107,7 @@ std::tuple code_cache_async::consume_compile_thread_queue() { } -const code_descriptor* const code_cache_async::get_descriptor_for_code(const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure) { +const code_descriptor* const code_cache_async::get_descriptor_for_code(const account_name& receiver, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure) { //if there are any outstanding compiles, process the result queue now //When app is in write window, all tasks are running sequentially and read-only threads //are not running. Safe to update cache entries. @@ -156,13 +157,16 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(const dig it->second = false; return nullptr; } - if(_queued_compiles.find(ct) != _queued_compiles.end()) { + if(std::find(_queued_compiles.cbegin(), _queued_compiles.cend(), ct) != _queued_compiles.end()) { failure = get_cd_failure::temporary; // Compile might not be done yet return nullptr; } if(_outstanding_compiles_and_poison.size() >= _threads) { - _queued_compiles.emplace(ct); + if (receiver.prefix() == chain::config::system_account_name) + _queued_compiles.push_front(ct); + else + _queued_compiles.push_back(ct); failure = get_cd_failure::temporary; // Compile might not be done yet return nullptr; } @@ -383,7 +387,9 @@ void code_cache_base::free_code(const digest_type& code_id, const uint8_t& vm_ve } //if it's in the queued list, erase it - _queued_compiles.erase({code_id, vm_version}); + auto i = std::find(_queued_compiles.cbegin(), _queued_compiles.cend(), code_tuple{code_id, vm_version}); + if (i != _queued_compiles.cend()) + _queued_compiles.erase(i); //however, if it's currently being compiled there is no way to cancel the compile, //so instead set a poison boolean that indicates not to insert the code in to the cache From fec0a7a8794620482ee77e258c9b7c37e9089e19 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Jun 2023 08:03:44 -0500 Subject: [PATCH 2/5] GH-1251 pass by reference --- .../include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp | 4 ++-- libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp index 1584c96406..29d432ebaf 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp @@ -39,7 +39,7 @@ struct config; class code_cache_base { public: - code_cache_base(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db); + code_cache_base(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db); ~code_cache_base(); const int& fd() const { return _cache_fd; } @@ -95,7 +95,7 @@ class code_cache_base { class code_cache_async : public code_cache_base { public: - code_cache_async(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db); + code_cache_async(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db); ~code_cache_async(); //If code is in cache: returns pointer & bumps to front of MRU list diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp index cab0728030..62a08756ce 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp @@ -39,7 +39,7 @@ static constexpr size_t descriptor_ptr_from_file_start = header_offset + offseto static_assert(sizeof(code_cache_header) <= header_size, "code_cache_header too big"); -code_cache_async::code_cache_async(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) : +code_cache_async::code_cache_async(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) : code_cache_base(data_dir, eosvmoc_config, db), _result_queue(eosvmoc_config.threads * 2), _threads(eosvmoc_config.threads) @@ -225,7 +225,7 @@ const code_descriptor* const code_cache_sync::get_descriptor_for_code_sync(const return &*_cache_index.push_front(std::move(std::get(result.result))).first; } -code_cache_base::code_cache_base(const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) : +code_cache_base::code_cache_base(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) : _db(db), _cache_file_path(data_dir/"code_cache.bin") { From 57a1d719e8199efa09569ef3ede9637c1dc394ee Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Jun 2023 13:37:28 -0400 Subject: [PATCH 3/5] GH-1251 Use multiindex, process in order of last used, prioritize eosio.* --- .../chain/webassembly/eos-vm-oc/code_cache.hpp | 18 ++++++++++++++---- .../runtimes/eos-vm-oc/code_cache.cpp | 8 ++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp index 29d432ebaf..ec8c13b9ab 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp @@ -15,7 +15,6 @@ #include -#include namespace std { template<> struct hash { @@ -78,9 +77,20 @@ class code_cache_base { local::datagram_protocol::socket _compile_monitor_write_socket{_ctx}; local::datagram_protocol::socket _compile_monitor_read_socket{_ctx}; - //these are really only useful to the async code cache, but keep them here so - //free_code can be shared - deque _queued_compiles; + //these are really only useful to the async code cache, but keep them here so free_code can be shared + using queued_compilies_t = boost::multi_index_container< + code_tuple, + indexed_by< + sequenced<>, + hashed_unique, + composite_key< code_tuple, + member, + member + > + > + > + >; + queued_compilies_t _queued_compiles; std::unordered_map _outstanding_compiles_and_poison; size_t _free_bytes_eviction_threshold; diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp index 62a08756ce..88fe0f3929 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp @@ -157,7 +157,8 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(const acc it->second = false; return nullptr; } - if(std::find(_queued_compiles.cbegin(), _queued_compiles.cend(), ct) != _queued_compiles.end()) { + if(auto it = _queued_compiles.get().find(boost::make_tuple(std::ref(code_id), vm_version)); it != _queued_compiles.get().end()) { + _queued_compiles.relocate(_queued_compiles.begin(), _queued_compiles.project<0>(it)); failure = get_cd_failure::temporary; // Compile might not be done yet return nullptr; } @@ -387,9 +388,8 @@ void code_cache_base::free_code(const digest_type& code_id, const uint8_t& vm_ve } //if it's in the queued list, erase it - auto i = std::find(_queued_compiles.cbegin(), _queued_compiles.cend(), code_tuple{code_id, vm_version}); - if (i != _queued_compiles.cend()) - _queued_compiles.erase(i); + if(auto i = _queued_compiles.get().find(boost::make_tuple(std::ref(code_id), vm_version)); i != _queued_compiles.get().end()) + _queued_compiles.get().erase(i); //however, if it's currently being compiled there is no way to cancel the compile, //so instead set a poison boolean that indicates not to insert the code in to the cache From 9cdcef8dcb81e095ec780cb07ad8d20ca19857df Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Jun 2023 14:36:08 -0400 Subject: [PATCH 4/5] GH-1251 Remove MRU, just go with FIFO --- libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp index 88fe0f3929..40d94f6e35 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp @@ -158,7 +158,6 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(const acc return nullptr; } if(auto it = _queued_compiles.get().find(boost::make_tuple(std::ref(code_id), vm_version)); it != _queued_compiles.get().end()) { - _queued_compiles.relocate(_queued_compiles.begin(), _queued_compiles.project<0>(it)); failure = get_cd_failure::temporary; // Compile might not be done yet return nullptr; } From f51599cbfa630755496eb3d5fc687cca90ddbcbf Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Jun 2023 15:19:06 -0400 Subject: [PATCH 5/5] GH-1251 Remove chain::config use from eos-vm-oc --- .../include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp | 2 +- libraries/chain/wasm_interface.cpp | 3 ++- .../chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp index ec8c13b9ab..d753a9dcaa 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp @@ -111,7 +111,7 @@ class code_cache_async : public code_cache_base { //If code is in cache: returns pointer & bumps to front of MRU list //If code is not in cache, and not blacklisted, and not currently compiling: return nullptr and kick off compile //otherwise: return nullptr - const code_descriptor* const get_descriptor_for_code(const account_name& receiver, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure); + const code_descriptor* const get_descriptor_for_code(bool high_priority, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure); private: std::thread _monitor_reply_thread; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index ba7e268579..3cedae97eb 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -94,7 +94,8 @@ namespace eosio { namespace chain { const chain::eosvmoc::code_descriptor* cd = nullptr; chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary; try { - cd = my->eosvmoc->cc.get_descriptor_for_code(context.get_receiver(), code_hash, vm_version, context.control.is_write_window(), failure); + const bool high_priority = context.get_receiver().prefix() == chain::config::system_account_name; + cd = my->eosvmoc->cc.get_descriptor_for_code(high_priority, code_hash, vm_version, context.control.is_write_window(), failure); } catch(...) { //swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp index 40d94f6e35..46dd95ba25 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -107,7 +106,7 @@ std::tuple code_cache_async::consume_compile_thread_queue() { } -const code_descriptor* const code_cache_async::get_descriptor_for_code(const account_name& receiver, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure) { +const code_descriptor* const code_cache_async::get_descriptor_for_code(bool high_priority, const digest_type& code_id, const uint8_t& vm_version, bool is_write_window, get_cd_failure& failure) { //if there are any outstanding compiles, process the result queue now //When app is in write window, all tasks are running sequentially and read-only threads //are not running. Safe to update cache entries. @@ -163,7 +162,7 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(const acc } if(_outstanding_compiles_and_poison.size() >= _threads) { - if (receiver.prefix() == chain::config::system_account_name) + if (high_priority) _queued_compiles.push_front(ct); else _queued_compiles.push_back(ct);