Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add eos-vm-oc-enable auto mode #1322

Merged
merged 25 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9229b34
GH-1251 Use enum for eosvmoc_tierup instead of bool.
heifner Jun 16, 2023
a05077a
GH-1251 Refactor wasm_interface map into a wasm_interface_collection
heifner Jun 19, 2023
c167373
GH-1251 Fix failing tests
heifner Jun 19, 2023
e7f1406
GH-1251 Fix tests to be able to run with default of --eos-vm-oc-enabl…
heifner Jun 19, 2023
34fa470
GH-1251 Add prefix() method to name
heifner Jun 19, 2023
360ffb5
GH-1251 Fix test
heifner Jun 19, 2023
23f6db5
GH-1251 Remove unused
heifner Jun 19, 2023
1cc64a0
GH-1251 Update docs
heifner Jun 19, 2023
025bc22
GH-1251 Add initial implementation of should_use_eos_vm_oc()
heifner Jun 19, 2023
f2123ea
GH-1251 Disable oc on producer when applying blocks
heifner Jun 20, 2023
9faefa2
GH-1251 Increase perf harness genesis to have 150ms max transaction t…
heifner Jun 22, 2023
8fc11f0
GH-1251 Add additional allowed true/false alternatives
heifner Jun 22, 2023
8706738
GH-1251 Add additional comments
heifner Jun 22, 2023
98e3b81
GH-1251 Address peer review comments
heifner Jun 22, 2023
d4ee46b
GH-1251 Add is_applying_block() method
heifner Jun 23, 2023
dc00e24
GH-1251 Add integration test that verifies auto oc tierup
heifner Jun 23, 2023
6008c72
GH-1251 Use eosio.token as eosio.system contract can not tierup in ti…
heifner Jun 23, 2023
d373b1d
GH-1251 No need to link to boost unit_test_framework, using header-only
heifner Jun 28, 2023
da236cb
GH-1251 Update auto description
heifner Jun 28, 2023
975ad13
GH-1251 Support case-insensitive options
heifner Jun 28, 2023
774fd21
GH-1251 Move setting of eosvmoc_tierup outside of loop
heifner Jun 28, 2023
b260447
GH-1251 Remove improper check for vm_type::eos_vm_oc
heifner Jun 28, 2023
a8f20ce
GH-1251 Restore #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED removed in ref…
heifner Jun 28, 2023
7c43e00
GH-1251 Add comment that log statement used in test
heifner Jun 29, 2023
cd744f5
GH-1251 Fix tests to not destroy temp dir before controller which use…
heifner Jun 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 10 additions & 1 deletion docs/01_nodeos/03_plugins/chain_plugin/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,16 @@ Config Options for eosio::chain_plugin:
code cache
--eos-vm-oc-compile-threads arg (=1) Number of threads to use for EOS VM OC
tier-up
--eos-vm-oc-enable Enable EOS VM OC tier-up runtime
--eos-vm-oc-enable arg (=auto) Enable EOS VM OC tier-up runtime
('auto', 'all', 'none').
'auto' - EOS VM OC tier-up is enabled
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
for eosio.* accounts and read-only
trxs.
'all' - EOS VM OC tier-up is enabled
for all contract execution.
'none' - EOS VM OC tier-up is
completely disabled.

--enable-account-queries arg (=0) enable queries to find accounts by
various metadata.
--max-nonprivileged-inline-action-size arg (=4096)
Expand Down
15 changes: 15 additions & 0 deletions libraries/chain/apply_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1094,4 +1094,19 @@ action_name apply_context::get_sender() const {
return action_name();
}

// Context | OC?
//-------------------------------------------------------------------------------
// Building block | baseline, OC for eosio.*
// Applying block | OC unless a producer, OC for eosio.* including producers
// Speculative API trx | baseline, OC for eosio.*
// Speculative P2P trx | baseline, OC for eosio.*
// Compute trx | baseline, OC for eosio.*
// Read only trx | OC
bool apply_context::should_use_eos_vm_oc()const {
return trx_context.is_read_only()
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
|| receiver.prefix() == config::system_account_name // "eosio"_n, all cases use OC
|| (trx_context.explicit_billed_cpu_time && !control.is_producer_node()); // validating/applying block
}


} } /// eosio::chain
77 changes: 23 additions & 54 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <eosio/chain/thread_utils.hpp>
#include <eosio/chain/platform_timer.hpp>
#include <eosio/chain/deep_mind.hpp>
#include <eosio/chain/wasm_interface_collection.hpp>

#include <chainbase/chainbase.hpp>
#include <eosio/vm/allocator.hpp>
Expand Down Expand Up @@ -239,6 +240,7 @@ struct controller_impl {
controller::config conf;
const chain_id_type chain_id; // read by thread_pool threads, value will not be changed
bool replaying = false;
bool producer_node = false; // true if node is configured as a block producer
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
db_read_mode read_mode = db_read_mode::HEAD;
bool in_trx_requiring_checks = false; ///< if true, checks that are normally skipped on replay (e.g. auth checks) cannot be skipped
std::optional<fc::microseconds> subjective_cpu_leeway;
Expand All @@ -249,14 +251,11 @@ struct controller_impl {
deep_mind_handler* deep_mind_logger = nullptr;
bool okay_to_print_integrity_hash_on_stop = false;

std::thread::id main_thread_id;
thread_local static platform_timer timer; // a copy for main thread and each read-only thread
#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED)
thread_local static vm::wasm_allocator wasm_alloc; // a copy for main thread and each read-only thread
#endif
wasm_interface wasmif; // used by main thread and all threads for EOSVMOC
std::mutex threaded_wasmifs_mtx;
std::unordered_map<std::thread::id, std::unique_ptr<wasm_interface>> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit
wasm_interface_collection wasm_if_collect;
app_window_type app_window = app_window_type::write;

typedef pair<scope_name,action_name> handler_key;
Expand Down Expand Up @@ -315,8 +314,7 @@ struct controller_impl {
chain_id( chain_id ),
read_mode( cfg.read_mode ),
thread_pool(),
main_thread_id( std::this_thread::get_id() ),
wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() )
wasm_if_collect( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() )
{
fork_db.open( [this]( block_timestamp_type timestamp,
const flat_set<digest_type>& cur_features,
Expand All @@ -342,12 +340,7 @@ struct controller_impl {
set_activation_handler<builtin_protocol_feature_t::crypto_primitives>();

self.irreversible_block.connect([this](const block_state_ptr& bsp) {
// producer_plugin has already asserted irreversible_block signal is
// called in write window
wasmif.current_lib(bsp->block_num);
for (auto& w: threaded_wasmifs) {
w.second->current_lib(bsp->block_num);
}
wasm_if_collect.current_lib(bsp->block_num);
});


Expand Down Expand Up @@ -2679,31 +2672,6 @@ struct controller_impl {
return (blog.first_block_num() != 0) ? blog.first_block_num() : fork_db.root()->block_num;
}

#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
bool is_eos_vm_oc_enabled() const {
return ( conf.eosvmoc_tierup || conf.wasm_runtime == wasm_interface::vm_type::eos_vm_oc );
}
#endif

// only called from non-main threads (read-only trx execution threads)
// when producer_plugin starts them
void init_thread_local_data() {
EOS_ASSERT( !is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread");
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
if ( is_eos_vm_oc_enabled() )
// EOSVMOC needs further initialization of its thread local data
wasmif.init_thread_local_data();
else
#endif
{
std::lock_guard g(threaded_wasmifs_mtx);
// Non-EOSVMOC needs a wasmif per thread
threaded_wasmifs[std::this_thread::get_id()] = std::make_unique<wasm_interface>( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty());
}
}

bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); };

void set_to_write_window() {
app_window = app_window_type::write;
}
Expand All @@ -2714,25 +2682,20 @@ struct controller_impl {
return app_window == app_window_type::write;
}

bool is_eos_vm_oc_enabled() const {
return wasm_if_collect.is_eos_vm_oc_enabled();
}

void init_thread_local_data() {
wasm_if_collect.init_thread_local_data(db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty());
}

wasm_interface& get_wasm_interface() {
if ( is_on_main_thread()
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|| is_eos_vm_oc_enabled()
#endif
)
return wasmif;
else
return *threaded_wasmifs[std::this_thread::get_id()];
return wasm_if_collect.get_wasm_interface();
}

void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) {
// The caller of this function apply_eosio_setcode has already asserted that
// the transaction is not a read-only trx, which implies we are
// in write window. Safe to call threaded_wasmifs's code_block_num_last_used
wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
for (auto& w: threaded_wasmifs) {
w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
}
wasm_if_collect.code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
}

block_state_ptr fork_db_head() const;
Expand Down Expand Up @@ -3610,11 +3573,9 @@ vm::wasm_allocator& controller::get_wasm_allocator() {
}
#endif

#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
bool controller::is_eos_vm_oc_enabled() const {
return my->is_eos_vm_oc_enabled();
}
#endif

std::optional<uint64_t> controller::convert_exception_to_error_code( const fc::exception& e ) {
const chain_exception* e_ptr = dynamic_cast<const chain_exception*>( &e );
Expand Down Expand Up @@ -3717,6 +3678,14 @@ void controller::replace_account_keys( name account, name permission, const publ
rlm.verify_account_ram_usage(account);
}

void controller::set_producer_node(bool is_producer_node) {
my->producer_node = is_producer_node;
}

bool controller::is_producer_node()const {
return my->producer_node;
}

void controller::set_db_read_only_mode() {
mutable_db().set_read_only_mode();
}
Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/include/eosio/chain/apply_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,8 @@ class apply_context {

action_name get_sender() const;

bool should_use_eos_vm_oc()const;

/// Fields:
public:

Expand Down
7 changes: 5 additions & 2 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ namespace eosio { namespace chain {

wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime;
eosvmoc::config eosvmoc_config;
bool eosvmoc_tierup = false;
wasm_interface::vm_oc_enable eosvmoc_tierup = wasm_interface::vm_oc_enable::oc_auto;

db_read_mode read_mode = db_read_mode::HEAD;
validation_mode block_validation_mode = validation_mode::FULL;
Expand Down Expand Up @@ -321,8 +321,8 @@ namespace eosio { namespace chain {

#if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED)
vm::wasm_allocator& get_wasm_allocator();
bool is_eos_vm_oc_enabled() const;
#endif
bool is_eos_vm_oc_enabled() const;

static std::optional<uint64_t> convert_exception_to_error_code( const fc::exception& e );

Expand Down Expand Up @@ -355,6 +355,9 @@ namespace eosio { namespace chain {
void replace_producer_keys( const public_key_type& key );
void replace_account_keys( name account, name permission, const public_key_type& key );

void set_producer_node(bool is_producer_node);
bool is_producer_node()const;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of sad about this change. Until now, the core of leap had no need to know if it was a producer or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, I guess depending how much of a purist you want to be here, maybe you could make controller take a std::function<bool(apply_context)> tier_up_with_oc, either as a ctor parameter or a config or something. That way chain_plugin can decide how to set this up based on the various configs. And libtester just always passes in a return false; for example, I guess.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that seems a bit too far.


void set_db_read_only_mode();
void unset_db_read_only_mode();
void init_thread_local_data();
Expand Down
35 changes: 35 additions & 0 deletions libraries/chain/include/eosio/chain/name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,41 @@ namespace eosio::chain {
friend constexpr bool operator != ( const name& a, uint64_t b ) { return a.value != b; }

constexpr explicit operator bool()const { return value != 0; }

/**
* Returns the prefix.
* for exmaple:
* "eosio.any" -> "eosio"
* "eosio" -> "eosio"
*/
constexpr name prefix() const {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation copied from CDT

uint64_t result = value;
bool not_dot_character_seen = false;
uint64_t mask = 0xFull;

// Get characters one-by-one in name in order from right to left
for (int32_t offset = 0; offset <= 59;) {
auto c = (value >> offset) & mask;

if (!c) { // if this character is a dot
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
if (not_dot_character_seen) { // we found the rightmost dot character
result = (value >> offset) << offset;
break;
}
} else {
not_dot_character_seen = true;
}

if (offset == 0) {
offset += 4;
mask = 0x1Full;
} else {
offset += 5;
}
}

return name{ result };
}
};

// Each char of the string is encoded into 5-bit chunk and left-shifted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ namespace eosio { namespace chain {
speculative_executed_adjusted_max_transaction_time // prev_billed_cpu_time_us > 0
};
tx_cpu_usage_exceeded_reason tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit;
fc::microseconds tx_cpu_usage_amount;
};

} }
19 changes: 17 additions & 2 deletions libraries/chain/include/eosio/chain/wasm_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ namespace eosio { namespace chain {
}
}

wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
enum class vm_oc_enable {
oc_auto,
oc_all,
oc_none
};

wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
~wasm_interface();

// initialize exec per thread
Expand Down Expand Up @@ -70,13 +76,22 @@ namespace eosio { namespace chain {
const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, apply_context& context)> substitute_apply;
private:
unique_ptr<struct wasm_interface_impl> my;
vm_type vm;
};

} } // eosio::chain

namespace eosio{ namespace chain {
std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime);
inline std::ostream& operator<<(std::ostream& os, wasm_interface::vm_oc_enable t) {
if (t == wasm_interface::vm_oc_enable::oc_auto) {
os << "auto";
} else if (t == wasm_interface::vm_oc_enable::oc_all) {
os << "all";
} else if (t == wasm_interface::vm_oc_enable::oc_none) {
os << "none";
}
return os;
}
}}

FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (eos_vm)(eos_vm_jit)(eos_vm_oc) )
78 changes: 78 additions & 0 deletions libraries/chain/include/eosio/chain/wasm_interface_collection.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once
#include <eosio/chain/wasm_interface.hpp>

namespace eosio::chain {

/**
* @class wasm_interface_collection manages the active wasm_interface to use for execution.
*/
class wasm_interface_collection {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no functionality change here, just a refactor out of controller.

public:
wasm_interface_collection(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup,
const chainbase::database& d, const std::filesystem::path& data_dir,
const eosvmoc::config& eosvmoc_config, bool profile)
: main_thread_id(std::this_thread::get_id())
, wasm_runtime(vm)
, eosvmoc_tierup(eosvmoc_tierup)
, wasmif(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile)
{}

wasm_interface& get_wasm_interface() {
if (is_on_main_thread() || is_eos_vm_oc_enabled()) {
return wasmif;
}
return *threaded_wasmifs[std::this_thread::get_id()];
}


// update current lib of all wasm interfaces
void current_lib(const uint32_t lib) {
// producer_plugin has already asserted irreversible_block signal is called in write window
wasmif.current_lib(lib);
for (auto& w: threaded_wasmifs) {
w.second->current_lib(lib);
}
}

// only called from non-main threads (read-only trx execution threads) when producer_plugin starts them
void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir,
const eosvmoc::config& eosvmoc_config, bool profile) {
EOS_ASSERT(!is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread");
if (is_eos_vm_oc_enabled()) {
// EOSVMOC needs further initialization of its thread local data
wasmif.init_thread_local_data();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lost an #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED on these couple lines during the refactor

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if most of the #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED could be removed to simplify differences. For now, I'll just revert back to having them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored #ifdefs

} else {
std::lock_guard g(threaded_wasmifs_mtx);
// Non-EOSVMOC needs a wasmif per thread
threaded_wasmifs[std::this_thread::get_id()] = std::make_unique<wasm_interface>(wasm_runtime, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile);
}
}

bool is_eos_vm_oc_enabled() const {
return ((eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) || wasm_runtime == wasm_interface::vm_type::eos_vm_oc);
}

void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) {
// The caller of this function apply_eosio_setcode has already asserted that
// the transaction is not a read-only trx, which implies we are
// in write window. Safe to call threaded_wasmifs's code_block_num_last_used
wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
for (auto& w: threaded_wasmifs) {
w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
}
}

private:
bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); };

private:
const std::thread::id main_thread_id;
const wasm_interface::vm_type wasm_runtime;
const wasm_interface::vm_oc_enable eosvmoc_tierup;

wasm_interface wasmif; // used by main thread and all threads for EOSVMOC
std::mutex threaded_wasmifs_mtx;
std::unordered_map<std::thread::id, std::unique_ptr<wasm_interface>> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit
};

} // eosio::chain
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this refactoring. It hides those details from the controller.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some tests for auto, all, and none options to make sure OC is enabled/disabled as configured?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any ideas on how? Unfortunately, oc is not setup to be used in unittests because the destructor of tester -> controller destroys oc and it can't be restarted because it forks the process on start. Seems like it would require a major refactor to allow oc to be used repeatably in unittests.

We could add some logging and look for a particular log in some integration tests. Let me see what I can do for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's surprising to hear -- it was definitely originally designed so that multiple controllers using OC can exist simultaneously: we have unittests that do that.

But, that was for the non-tier-up usage of OC. So maybe there was a shortcoming of the original impl in this area. Or maybe we've regressed some how.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integration test added.