-
Notifications
You must be signed in to change notification settings - Fork 87
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
Optimize interpreter::blockchain::{load_contract_code, code_copy
to read contract starting from offset
#847
base: master
Are you sure you want to change the base?
Conversation
318b0b3
to
9f4117d
Compare
9f4117d
to
ab0cc7b
Compare
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.
Looks alright to me. The TODO comment needs to be resolved before I can approve. There's also some code duplication that I think we could factor out, but that's not blocking from my end.
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.
Nice stuff!
Poke me when you've fixed the clippy errors and I'll re-approve |
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 not a VM guy, so maybe I could get you to walk me through these changes.
CHANGELOG.md
Outdated
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). | |||
|
|||
## [Unreleased] | |||
|
|||
### Changed | |||
- [#847](https://github.com/FuelLabs/fuel-vm/pull/847): Remove `contract_read`, and change `load_contract_code`, `code_copy` and `code_root` to explicitly load the contract code in a buffer. Also check for mismatches between contract size stored and actual size of contract in those functions. |
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 confused by this. There is no function called contract_read
that is removed in these changes. And you added a call to read_contract
, which sounds like the same thing?
.ok_or(PanicReason::ContractNotFound)? | ||
.map_err(RuntimeError::Storage)?; | ||
|
||
if contract_buffer.len() != contract_len { |
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.
If it's already allocated to to contracct_len
size, will the len
call ever not be contract_len
?
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.
Yes, this is a leftover from refactoring, most probably this check should not be there.
(Btw, I noticed that we have a slightly different implementation of the same function:
fuel-vm/fuel-vm/src/interpreter/flow.rs
Line 631 in a6abe47
fn read_contract<S>( |
let contract = super::contract::contract(self.storage, &contract_id)?; | ||
let contract_bytes = contract.as_ref().as_ref(); | ||
|
||
let contract_buffer: Vec<u8> = load_contract_code_from_storage( |
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.
After reviewing this change, I see that we basically have the same code as before and allocate vectors in all cases.
So basically, this change doesn't make a lot of sense=D
The main problem is why we can't do the optimal implementation and copy directly into the memory - contract offset(and blob offset, since we have the same problem for blobs LDC
with mode == 1
and BLDD
opcode).
I think we need to extend the StorageRead
trait if we want to fix this issue in a proper way. A new method will do the same as the read
or change the read
method itself to something like:
fn read(&self, key: &Type::Key, offset: usize, buf: &mut [u8])
-> Result<Option<usize>, Self::Error>
Could you change this method to work with offset
and update all code to not allocate vector, please?
Also, it would be nice if you do the same for blobs as well.
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.
Yes, the original idea was to have only one allocation. The only actual improvement in this PR as is now is fixing <MemoryStorage as StorageRead>::read`, which was panicking before due to slices of been different size being copied.
Happy to extend the trait to read. I think it would be better to add a new function read_from_offset
, and then change read
to be a provided method with offset 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'm fine with the new method and with reworking the old one. I think changing the old one makes more sense because the compiler will help you identify places that also need to be updated in the code to avoid allocation of the Vec
. Plus this read
function only exists for load opcodes, so they mainly define their existence, usage and shape.
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.
Sure, just to make sure: if we opt for modifying the StorageRead::read
function, then there will be several places where the implementation needs to be updated, both in fuel-vm
and fuel-core
; also it is not clear to me that we can read from an arbitrary offset easily in all the implementations of the StorageRead
trait. But I will try and see how far can I go before getting into trouble
Happy to do it once I address the comments left by @xgreenx :) |
let contract_bytes = contract.as_ref().as_ref(); | ||
let contract_len = contract_bytes.len(); | ||
let contract_bytes = self.memory.write(self.owner, dst_addr, length)?; | ||
let contract_len = contract_size(&self.storage, &contract_id)?; | ||
let charge_len = core::cmp::max(contract_len as u64, length); |
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.
We never read more than length
bytes with the new version. Should charge_len
be changed accordingly to reflect this?
With the new version the function |
interpreter::blockchain::{load_contract_code, code_copy
to read contract starting from offset
[Link to related issue(s) here, if any]
Closes #681
[Short description of the changes.]
When loading the contract code into the stack, we use the new semantics of
StorageRead
from #863 to only fetch the portion of a contract starting from the specified offset. We also read the contract bytes directly into the stack.A similar optimization is done for
code_copy
.Checklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]