FuelLabs / sway

🌴 Empowering everyone to build reliable and efficient smart contracts.
https://docs.fuel.network/docs/sway/
Apache License 2.0
62.64k stars 5.36k forks source link

Show byte and gas costs #513

Open AlicanC opened 2 years ago

AlicanC commented 2 years ago

To ease the burden of resource management on developers, we can provide info on resource costs such as the cost of importing a library (in bytes) and executing a line (or any piece) of code (in gas).

Bonus: Display dollar costs (or in any fiat)

Prior Art

VSCode extension for JS: https://github.com/wix/import-cost image

Expectation

We should have byte costs like above and also gas costs. I assume the following data would be available, but not sure which should be displayed: image

adlerjohn commented 2 years ago

Very good suggestion. For posterity, a few notes. Gas costs of loops and things like hashing are not know at compile time, since they're variable. However, basic blocks and fixed gas cost blocks can provide exact costs. Upper bounds can potentially also be provided for variable.

This feature can also tie in with general gas profiling.

AlicanC commented 2 years ago

My initial thought on variables was:

For any point we would either have an exact value or a lower bound we can use instead:

enum GasCost { Equals(u64) /* 10 gas */, AtLeast(u64) /* 10+ gas */ }

Which means we can still calculate foo here, but it's not really that useful since the value is way off when count > 0:

impl TestContract for Contract {
  fn foo(count: u64) { // 0+ gas (but 1000+ if count > 0)
    while count > 0 { // 1000 gas
      veryexpensive(); // 999 gas
      count = count - 1; // 1 gas
    }
  }
}

I don't know how much of an average codebase would display useful values and how much of it would be meaningless X+ gas values.

adlerjohn commented 2 years ago

I don't know how much of an average codebase would display useful values and how much of it would be meaningless X+ gas values.

It's a useful feature to at least prototype and see, for sure!

SilentCicero commented 2 years ago

@AlicanC I'd say this information is more useful as a hover over rather than leaving it as a comment.

My reasoning is that comments are hard to get rid of if you don't want them (while leaving the comments you do want there).

Instead, it would be cool to have a simple Gas Pump icon you turn on, then any line you hover over it will give you it's initial upfront cost.

John is right though, loops and other in-execution are basically impossible to simulate without context and inputs. But showing raw more constant items are possible!

adlerjohn commented 2 years ago

I think @AlicanC was just using comments as a way of showing how it would look. See the first picture in OP. The actual plugin obviously shouldn't be inserting comments into the code, that was understood as obvious.

xgreenx commented 2 years ago

Hi, it is not clear to me how actual that information will be so I will ask several questions regarding the implementation=)

Do you plan to show and update that information in runtime or by some action from the user? I meant each modification of the file will trigger the rendering of a new value, or the user should run some action from the plugins menu in the bar?

During the compilation of the contract, the rust will optimize the code based on the usage(also you can use wasm-opt to optimize the WASM size more). So how do you plan to measure the impact of each line and each import?

Another question is how do you plan to juxtapose the code from the sway code to assembler code? The compiler in some cases can inline methods(instead of direct call) and then optimize them and it is hard to evaluate a correct value.

I only want to be sure that the information will be really useful for the developer and will help him to optimize the contract. If my questions are not actual, sorry=)

adlerjohn commented 2 years ago

Static analysis of gas costs of functions or even lines can only go so far. Ultimately, dynamic capturing of gas usage will be the real way people will optimize their contracts, and that's a planned feature for fuel-core. See: https://github.com/FuelLabs/fuel-vm/pull/58 for the start of this feature (code coverage is just gas profiling with gas costs of all instructions set to 1, really!).

xgreenx commented 2 years ago

Sorry, I still can figure out how you plan to juxtapose the lines of the code with generated commands in the assembler(one line of code can generate many commands/words in the assembler, and self.current_location() shows only the location in the assembler code).

fn give_me_42() -> u32 {
    42
}

fn some_function1() -> u32 {
    let temp = give_me_42();
    temp += 40;
    temp -= 40;
    // We don't return the `temp`, so the compiler will erase all code related to that variable.
    1
}

fn some_function2() -> u32 {
    give_me_42(); // We don't use the value from that function. So the compiler will erase that part of the code.
    13
}

The function give_me_42 is not used actually and the compiler also will remove that from the binary.

Will be those cases handled by the plugin? If yes, then okay, it is my problem that I can't figure out "how"=)

adlerjohn commented 2 years ago

Yes, the plugin and general toolchain (including code coverage, gas profiling, and step-through debugger) will use source mapping, so they'll be able to "understand" eliminated lines/functions, etc.

xgreenx commented 2 years ago

Okay, cool. I guess you will build the contract each time and optimize it. So do you plan to render actual value in runtime or by some action from the user side? The building of the contract on each modification of the contract may be hard from the performance perspective.

adlerjohn commented 2 years ago

Contracts are generally pretty small, and building can be incremental rather than having to re-do the whole thing from scratch. It's in fact currently the case the projects are re-built every time they're saved in vscode, and it's completely seamless!

AlicanC commented 1 year ago

A few notes: