-
Notifications
You must be signed in to change notification settings - Fork 294
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
interpreter pauses #85
Comments
This might be something that would be useful for wasmi, but before working on the details, let's discuss other, non-invasive, solutions. So it sounds to me as you trying to achieve async calls with sync interface. And i'm wondering, have you considered using asynchronous interface? That is, As for limiting run time. In blockchains we have a similar problem: we need to limit execution of wasm code by the run time of an average machine that runs the node and do it deterministically. To solve this problem we use a mechanism called The reason why I'm want to discuss non-invasive solutions is that I believe you don't want to build production-like product in wasmi since, well, wasmi is an interpreter (and will always be) and interpreters are usually slow. So even if we introduce these features into wasmi, I believe, you won't see any such feature in a near feature in other engines. So it might be a dead end. |
the For asynchronous calls, unfortunately, without a way to pause the interpreter to wait for the event (like a blocking The speed of wasmi is not a big concern for me right now, as I might investigate JIT based solutions if it ever becomes a problem. I'm putting off that choice for now to take time to experiment, and wasmi is great for that: nice to use, and the code is easy to follow. I understand the goal of non invasive solutions, you probably don't want to move wasmi in a direction that does not follow your use case.
With those changes, it's possible for wasmi users to create a function context and a stack, and rewrite a |
What about a JS-like approach? That is, when wasm module doesn't have control over main loop. Wasm module only gets control on some event, such as,
It's not like that : ) Indeed, these features might be useful for breakpoints and stacktraces.
Yeah, I'm just worried about the case when you done with experimentation and try to move on some other engine and find out that your model isn't suitable for it.
This might work! But there is a problem, it will expose a bit of inner workings of interpreter. For example, now every stack frame creates 2 (two!) vectors. The obvious optimization would be to flatten all these into the single stack. When I thought about stacktraces, one possible solution I was thinking of is the following: Each execution requires some context. Let's call it |
Also, there was another idea in a different context: the most complex part of the interpreter is dispatching and executing instructions (i.e |
a callback approach could work. so the interpreter loop would end, but the code would have registered another internal function to be called later with a new interpreter, but the same environment. But that would mean that the host would be able to call arbitrary closures inside the wasm code?
That would be on me then, and I'm willing to put in the work if I find a new model :)
That would definitely work, at the same time alllowing pauses and stack traces, but also hiding the innards behind a nicer API. Good idea! Having the instruction execution in a separate crate would allow even more tooling, like state save/restore and step by step debugging. |
Sorry, I think I don't follow. The host would be able to call only functions exported from the wasm module, like:
Yah, but this, admitedly, requires more engineering work comparing to your approach.
And that might require even more work... 🤔 |
do you want me to send a PR to test my proposal? |
After thinking a bit I became a little bit more skeptical about this approach. You see, opening this internals likely would impose some maintenance burden on wasmi. At the same time it also can make supporting such kind of stuff painful due to internal changes... |
so the middle ground would be having the interpreter expose some context when it returns? That still requires making the interpreter public |
Another thought: I think your approach is ok, if we make it clear that this APIs are experimental. E.g by hiding it behind the feature. It the simplest way but still will make clients suffer in case of big changes.
What context exactly? |
The context you mentioned in:
|
Ah, my bad. I shouldn't call it Let's call it But in general yes, that would be sorta middle ground. Although I'm also good with:
|
alright, I'll try the first method then |
hello! The first commit ( f107c64 ) marks some parts as public and adds the The second commit appears because I'm now trying to store the interpreter that I paused, and the way the externals are used is a bit annoying. We have the following: pub struct Interpreter<'a, E: Externals + 'a> {
externals: &'a mut E,
} If I'm running the interpreter until the function ends, having it hold a mutable reference to the externals is fine, because we will drop the interpreter afterwards. I'm not sure what would be the right solution here, or if all those changes are even desirable. I'm still testing things out, so let's not rush things :) |
Yeah and unfortunately that's quite undesirable. But I think I didn't get it: why do you need to keep Note that it is ok to dispose |
Oh, you're right, I could just create another interpreter, this will make
everything much easier
…On Thu, Apr 26, 2018, 14:08 Sergey Pepyakin ***@***.***> wrote:
But that would change the API in multiple places, like
FuncInstance::invoke or ModuleInstance::invoke_export.
Yeah and unfortunately that's quite undesirable.
But I think I didn't get it: why do you need to keep Interpreter instance
around? Can't we create another one and then provide it with Externals?
Note that it is ok to dispose Externals and then create them again. In
fact, when designed that feature was kept in mind.
For example, consider this sandboxing use-case
<https://github.com/paritytech/polkadot/blob/c6fbcbf2a1f824ef622828b24a4b1c9d9da959bf/substrate/executor/src/wasm_executor.rs#L364>.
GuestExternals
<https://github.com/paritytech/polkadot/blob/c6fbcbf2a1f824ef622828b24a4b1c9d9da959bf/substrate/executor/src/sandbox.rs#L171-L280>
have a mutable reference to a supervisor's Externals. That's ok since
GuestExternals created only for a one call!
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#85 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAHSAHWT6v7zp8B8J4E6Gd3ldvHUjohSks5tsbihgaJpZM4TawE_>
.
|
A good pointer for how to design an interface like this is Linux's aio |
I'm experimenting with using WASM as an embedded language in database systems. I was looking for the same features and I'm pleasantly surprised by the non-invasive solutions suggested. @pepyakin Can you point me somewhere I can have a look at the gas metering so I can learn how to use it? |
@portstrom Yeah, sure! In order to inject gas metering into a wasm binary you need basically two things:
Then you take a pwasm-util crate and call it's The example code can be found here. However, I have a little doubt that you actually need it. I think that way because gas metering might introduce some unwanted complexity and overhead in order to provide its deterministic guarantees. Have you assessed the use of a plain timeout for this use-case? |
@pepyakin Thank you! I had to get an old version of For my use case execution time is limited by real time, not by counting instructions. I did it so that after every one million gas, the host function checks the elapsed time. This doesn't work however when instructions are executed in a sequence all the time without switching block, assuming calls to the |
@pepyakin I don't see a good way to do with a timeout without gas metering. I would have to run each module in a separate process and kill the process after the timeout. That's not what I want to do. |
Gas metering apparently makes my function take almost three times as much time to run (optimized build). Would probably be easy to optimize by having a special opcode for gas metering instead of calling a host function. |
hello,
I'm currently developing serverless-wasm and I'm wondering how I could implement two features:
From what I understand, I could proceed like this (for the async part):
Interpreter::start_execution
that would store the function context when receiving that trap fromInterpreter::run_interpreter_loop
instead of dropping itIt looks like it would be doable, and could be used to implement breakpoints, too.
The other feature, limiting the run time of a function, would probably be easier to implement, with the loop regularly checking that we do not exceed some limit passed as arguments.
Do you think those features would be useful in wasmi?
The serverless-wasm project is in very early stage, and I admit the readme is not too serious, but I really want to get it to a state where it's nice to use, and I need at least the async networking feature to get it.
The text was updated successfully, but these errors were encountered: