-
Notifications
You must be signed in to change notification settings - Fork 60
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
Remove stack limit #87
Conversation
…en indexing out of the range of the vector
include/eosio/vm/vector.hpp
Outdated
class unmanaged_vector { | ||
public: | ||
constexpr unmanaged_vector(size_t size=0) : _data(size) {} | ||
constexpr unmanaged_vector(const unmanaged_vector& v) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to delete the copy constructor explicitly, when you define a move constructor.
include/eosio/vm/vector.hpp
Outdated
public: | ||
constexpr unmanaged_vector(size_t size=0) : _data(size) {} | ||
constexpr unmanaged_vector(const unmanaged_vector& v) = delete; | ||
constexpr unmanaged_vector(unmanaged_vector&& v) : _data(std::move(v._data)), _index(v._index) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=default
include/eosio/vm/vector.hpp
Outdated
_data = std::move(v._data); | ||
_index = v._index; | ||
return *this; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=default
include/eosio/vm/vector.hpp
Outdated
} | ||
|
||
constexpr inline void resize( size_t size ) { | ||
_data.resize(size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can break the required invariant that _index < _data.size()
include/eosio/vm/vector.hpp
Outdated
} | ||
|
||
constexpr inline void back() { | ||
return _data[_index]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off by one.
include/eosio/vm/vector.hpp
Outdated
constexpr inline T& at( size_t i ) { | ||
// TODO: fix this to be more robust, assuming that the read 'i' will be in close to the max index | ||
// this is the usage pattern currently by eos-vm, a better solution will be created for the abstraction | ||
// of the various points of validation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does eos-vm ever access after the current end? The fact the this has different behavior from the const version is scary.
include/eosio/vm/vector.hpp
Outdated
@@ -56,7 +118,7 @@ namespace eosio { namespace vm { | |||
} | |||
|
|||
constexpr inline void pop_back() { | |||
EOS_VM_ASSERT( _index >= 0, wasm_vector_oob_exception, "vector pop out of bounds" ); | |||
EOS_VM_ASSERT( _index-- > 0, wasm_vector_oob_exception, "vector pop out of bounds" ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has the same issue as in unmanaged_vector.
include/eosio/vm/wasm_stack.hpp
Outdated
using type = unmanaged_vector<Elem>; | ||
}; | ||
|
||
using base_data_store_t = typename base_data_store<ElemT, Allocator>::type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using base_data_store_t = std::conditional<std::is_same_v<Allocator, nullptr_t>,
unmanaged_vector<Elem>,
managed_vector<ElemT, Allocator>>;
@@ -50,7 +50,7 @@ inline bool check_nan(const std::optional<eosio::vm::operand_stack_elem>& v) { | |||
} | |||
|
|||
#define BACKEND_TEST_CASE(name, tags) \ | |||
TEMPLATE_TEST_CASE(name, tags, eosio::vm::interpreter, eosio::vm::jit) | |||
TEMPLATE_TEST_CASE(name, tags, eosio::vm::jit, eosio::vm::interpreter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it was just there from oddness in unit tests.
include/eosio/vm/vector.hpp
Outdated
|
||
private: | ||
std::vector<T> _data; | ||
size_t _index = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this whole _index
business confusing. vector has size and capacity. We add an _index
on top of that here. Then stack adds another _index
on top of that.
include/eosio/vm/wasm_stack.hpp
Outdated
using base_data_store_t = typename base_data_store<ElemT, Allocator>::type; | ||
|
||
base_data_store_t _store; | ||
uint16_t _index = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This imposes a limit of 65536 elements, which will silently wrap instead of causing an error.
I'm very suspicious of the claim that vector's allocation is too slow. You're reserving an amount equal to the previous hard limit. i.e. it should never need to reallocate on normal usage, so the performance of allocation should be insignificant. |
I should be, but it wasn't. Given the wasm benchmarks the average overhead over develop is about 45-50%, so high enough that we need to do something different. I tried quite a few different ways of using std::vector and the 45-50% overhead was the best I could get it, some ways lead to 80-90% overhead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried quite a few different ways of using std::vector and the 45-50% overhead was the best I could get it, some ways lead to 80-90% overhead.
I don't know exactly what you tried, but e8554f5 is severely broken, because it is confused about vector size vs. capacity.
include/eosio/vm/vector.hpp
Outdated
|
||
constexpr inline void emplace_back( T&& val ) { | ||
EOS_VM_ASSERT( _index < _size, wasm_vector_oob_exception, "vector write out of bounds" ); | ||
_data[_index++] = std::forward<T>(val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::forward is a bit pointless, given that this implementation requires T to have a trivial default constructor/copy constructor/destructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm aware, I didn't push up a lot of different tries because of the excess overhead.
I'm not seeing that std::vector is slower. In fact, simply replacing unmanaged_vector with std::vector at the current commit seems to make it faster. |
tests/stack_restriction_tests.cpp
Outdated
i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) i32.const 0 call 0) (memory | ||
(;0;) 0) (export "fun" (func 1))) | ||
*/ | ||
std::vector<uint8_t> __8k_stack_size_1_under_wasm = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a reserved identifier.
Hmmm, you are right. On Linux I wasn't seeing a benefit (they were close enough together), but on Mac there is a small benefit (maybe 5% faster) with std::vector. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks fine except for the test issues.
tests/vector_tests.cpp
Outdated
unmanaged_vector<char> uv0(10); | ||
for (int i = 0; i < uv0.size(); i++) | ||
uv0[i] = 'a'; | ||
CHECK_THROWS_AS([&](){ uv0[10] = 'a'; }(), std::exception); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fails, since unmanaged_vector is std::vector.
No description provided.