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

Investigate reported size discrepancy between Rust and AssemblyScript contracts #167

Closed
MaksymZavershynskyi opened this issue May 29, 2020 · 19 comments
Assignees

Comments

@MaksymZavershynskyi
Copy link
Contributor

As reported by @willemneal some contracts when implemented in Rust are an order of magnitude larger than equivalent contracts implemented in AssemblyScript. We need to get these contracts and debug where the size bloat comes from. The first thing is however, to get an example of such contract. @nearmax have not observed such discrepancy himself.

Assigning to @willemneal to provide an example of an order of magnitude discrepancy. Also assigning to @olonho to investigate the source of the size bloat.

@olonho
Copy link
Contributor

olonho commented May 29, 2020

Created size analysis tool at https://github.com/near/wasm_sizer. Doesn't work properly on AssemblyScript files yet due to decoding library bug. Filed bug athre0z/wasm#7

@olonho
Copy link
Contributor

olonho commented May 29, 2020

Reachability analysis for ten largest functions in regular build of https://github.com/olonho/voting-app/blob/master/contract/src/lib.rs and after application of wasm-opt.

@olonho
Copy link
Contributor

olonho commented May 29, 2020

flow_base.pdf

@olonho
Copy link
Contributor

olonho commented May 29, 2020

flow_opt.pdf

@willemneal
Copy link
Contributor

Just compiled the assemblyscript counter: https://github.com/near-examples/counter with

node asconfig.js -O3z

and the resulting binary is 18K.

Then compiled https://github.com/near-examples/rust-counter

Initially got 41K. Then wasm-opt -o out/opt.wasm -0z out/main.wasm resulted in out/opt.wasm being 35K.

Then I returned to the assemblyscript. The unoptimized was 30K, but with the above wasm-opt command it became 16K.

Then when I passed the optimized AS binary to wasm-opt I got 12K!

Now I'm going to use @olonho's new tool to investigate further.

@willemneal
Copy link
Contributor

willemneal commented May 29, 2020

@olonho I found that for assemblyscript binaries if I use wasm-dis and then wasm-as the .wasm generated can be used by your sizer tool. Also an interesting side effect of this process is that the generated wasm file was 21K instead of the initial 30K. I assumed it would be less because the initial wasm file includes a name section, but it's crazy that this would be 9K!

Furthermore, the AS compiler also outputs a .wat file, which in the unopt case is 186K. So you would expect that the .wat generated by wasm-dis would be smaller, but instead it's 240K, and with wasm-as becomes the 21K .wasm mentioned above.

@MaxGraey
Copy link

MaxGraey commented May 29, 2020

I guess it's because wasm-dis -> wasm-as loose a lot of debug and sourcemap info. If you disable sourcemapping on AS it could be also small I guess. Also you could add --noAssert flag for release builds

@willemneal
Copy link
Contributor

willemneal commented May 29, 2020

Hmm it wasn't compiled with source maps, but --debug was on so I'll take a look at removing that. Also we need assert because it provides needed runtime checks.

@MaxGraey
Copy link

Ah, ok

@willemneal
Copy link
Contributor

After removing the debug flag and doing -O3z, I was able to get 12K, and the unoptimized went down to 20K!

@willemneal
Copy link
Contributor

@olonho Removing the --debug flag also makes the binary work with your sizer tool.

@willemneal
Copy link
Contributor

So after inspecting the Rust counter's source, I realized it differs from the AssemblyScript as its increment/decrement functions don't take an amount parameter. After adding this to the contract, the rust binary became 100K and optimized to 85K.

@MaksymZavershynskyi
Copy link
Contributor Author

MaksymZavershynskyi commented May 30, 2020

@willemneal Are you saying Rust contract went from 41K to 100K after adding arguments? If that's true then it seems like serde_json adds a significant bloat to our contract size.

@willemneal
Copy link
Contributor

Yep exactly. So it's serde_json for sure.

@MaksymZavershynskyi
Copy link
Contributor Author

Yep exactly. So it's serde_json for sure.

serde + serde_json.

@olonho
Copy link
Contributor

olonho commented May 31, 2020

Improved analysis tool, so that it only shows the relevant nodes (i.e. ones participating in paths to large functions).

@olonho
Copy link
Contributor

olonho commented May 31, 2020

1.pdf

@olonho
Copy link
Contributor

olonho commented May 31, 2020

With 5 largest function source of bloat becomes pretty obvious, and is an indirectly called function 662, which is

static u32 f662(u32 p0, u32 p1) {
  u32 l2 = 0;
  f64 l3 = 0;
  FUNC_PROLOGUE;
  u32 i0, i1, i2, i3, i4;
  f64 d0, d1;
  i0 = p1;
  i0 = i32_load8_u((&memory), (u64)(i0));
  i1 = 1u;
  i0 <<= (i1 & 31);
  i1 = 2u;
  i0 &= i1;
  l2 = i0;
  i0 = p0;
  d0 = f64_load((&memory), (u64)(i0));
  l3 = d0;
  i0 = p1;
  i0 = i32_load((&memory), (u64)(i0 + 16));
  i1 = 1u;
  i0 = i0 != i1;
  if (i0) {
    i0 = p1;
   d1 = l3;
    i2 = l2;
    i0 = f633(i0, d1, i2);
    goto Bfunc;
  }
  i0 = p1;
  d1 = l3;
  i2 = l2;
  i3 = p1;
  i4 = 20u;
  i3 += i4;
  i3 = i32_load((&memory), (u64)(i3));
  i0 = f632(i0, d1, i2, i3);
  Bfunc:;
  FUNC_EPILOGUE;
  return i0;
}

and indeed is never called directly, but stored in the table of indirect calls as

T0.data[offset + 128] = (wasm_rt_elem_t){func_types[1], (wasm_rt_anyfunc_t)(&f662)};

flow.pdf

Curiously, in case of the debug binary, the 5 largest functions are reachable in way saner manner
flow.pdf
and indeed looks like serialization code.

@austinabell
Copy link
Contributor

Closing issue of investigation because this and other points of bloat have been identified since. Feel free to re-open if we want to use this as a tracking issue, but seems stale to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants