Skip to content

Commit 5896b4c

Browse files
authored
Cost of contract calls snapshot tests (#7440)
## Description This PR is a prequel for #7419. It will set the baseline, which future PRs need to improve. Apart from that it also: 1 - Fix a problem with `SubtsType` for `TyConstantDecl`; 2 - Introduce a new way to write snapshot tests. Now the test source code can have "blocks" ``` /* START BOOL */ fn in_bool(v: bool) -> bool; /* END BOOL */ ``` These blocks can be manipulated from inside the `snapshot.toml`. In this case, the `toml` is: ``` cmds = [ "forc test --path {root} --release --experimental const_generics", { repeat = "for-each-block", cmds = [ "forc test --path {root} --release --experimental const_generics" ] } ] ``` Which repeats the inner `forc test` for each block, removing all others. That allows me to check the cost of the contract method selection algorithm for each contract method. For example, `cost_of_in_array_64` with all other contract methods costs `60382`, but alone it only costs `58218`, which means that around 2000 was "wasted" in the contract method selection. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [ ] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [ ] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers.
1 parent 97efa22 commit 5896b4c

File tree

16 files changed

+1746
-216
lines changed

16 files changed

+1746
-216
lines changed

justfile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ update-fuel-dependencies:
1919
update-contract-ids:
2020
bash ./test/update-contract-ids.sh
2121

22+
[group('automation')]
23+
bisect-forc path command:
24+
bash ./scripts/bisect-forc/bisect-forc.sh "{{path}}" "{{command}}"
25+
2226
[group('benchmark')]
2327
benchmark:
2428
bash ./benchmark.sh
@@ -31,6 +35,33 @@ benchmark-tests:
3135
collect-gas-usage:
3236
cargo r -p test --release -- --verbose --forc-test-only | ./scripts/compare-gas-usage/extract-gas-usage.sh
3337

38+
# This recipe should be used on snapshot tests that contains gas usage from `forc test`,
39+
# because it will extract gas usage from all versions of the file and generate an html interactive report.
40+
# path: path to file to extract gas usage
41+
# open: "-o" will open the default browser showing the report
42+
[linux]
43+
[group('benchmark')]
44+
collect-historic-gas-usage path open:
45+
#! /bin/bash
46+
mkdir -p target
47+
rm target/a.csv
48+
rm target/a.html
49+
echo "test,gas,commit" > target/a.csv
50+
for HASH in `git log --format='%H' -- {{path}}`; do
51+
TIMESTAMP=$(git show -s --format='%as-%ct-%H' "$HASH")
52+
git --no-pager show "$HASH:{{path}}" | bash -c "scripts/compare-gas-usage/extract-gas-usage.sh $TIMESTAMP" >> target/a.csv
53+
done
54+
./scripts/csv2html/csv2html.sh target/a.csv >> target/a.html
55+
if [ -n "{{open}}" ]; then
56+
if which xdg-open &>> /dev/null
57+
then
58+
xdg-open target/a.html
59+
elif which gnome-open &>> /dev/null
60+
then
61+
gnome-open target/a.html
62+
fi
63+
fi
64+
3465
[group('build')]
3566
build-prism:
3667
cd ./scripts/prism && ./build.sh

scripts/bisect-forc/bisect-forc.sh

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ echo -n "Running: "
4848

4949
CACHENAME="$(git show -s --format='%as-%ct-%H' HEAD)"
5050
compile_and_cp_to_cache "$CACHENAME"
51-
INITIAL_COMPILATION_STATUS=$(run_cargo)
51+
INITIAL_COMPILATION_STATUS=$(run_cargo "$1" "$2")
5252

5353
if [ "$INITIAL_COMPILATION_STATUS" = "0" ]; then
5454
echo -e " ${BOLD_GREEN}Ok${NC}"
@@ -69,7 +69,7 @@ for HASH in `git log --format="%H" --tags --no-walk`; do
6969

7070
CACHENAME="$(git show -s --format='%as-%ct-%H' HEAD)"
7171
compile_and_cp_to_cache "$CACHENAME"
72-
LAST_STATUS=$(run_cargo)
72+
LAST_STATUS=$(run_cargo "$1" "$2")
7373
if [ "$INITIAL_COMPILATION_STATUS" != "$LAST_STATUS" ]; then
7474
echo -e "^^^^^^^^^ ${BOLD_WHITE}This version result is different!${NC}"
7575
break
@@ -93,22 +93,22 @@ do
9393
#git --no-pager bisect visualize --oneline
9494

9595
git bisect next | grep "is the first new commit" &>> /dev/null
96-
if [ "$LAST_STATUS" = "0" ]; then
96+
if [ "$?" = "0" ]; then
9797
FIRST_COMMIT="$(git bisect next 2>&1 | head -n 1 | cut -f1 -d" ")"
9898
git checkout "$FIRST_COMMIT" &>> /dev/null
9999
break
100100
fi
101-
101+
102102
git bisect next | grep "Bisecting"
103-
if [ "$LAST_STATUS" != "0" ]; then
103+
if [ "$?" != "0" ]; then
104104
break
105105
fi
106106

107107
git checkout . &>> /dev/null
108108

109109
CACHENAME="$(git show -s --format='%as-%ct-%H' HEAD)"
110110
compile_and_cp_to_cache "$CACHENAME"
111-
LAST_STATUS=$(run_cargo)
111+
LAST_STATUS=$(run_cargo "$1" "$2")
112112
if [ "$LAST_STATUS" = "$INITIAL_COMPILATION_STATUS" ]; then
113113
git bisect new &>> /dev/null
114114
else
@@ -127,7 +127,7 @@ echo -n "checking the found commit has the same behaviour as the initial commit.
127127

128128
CACHENAME="$(git show -s --format='%as-%ct-%H' HEAD)"
129129
compile_and_cp_to_cache "$CACHENAME"
130-
LAST_STATUS=$(run_cargo)
130+
LAST_STATUS=$(run_cargo "$1" "$2")
131131

132132
if [ "$INITIAL_COMPILATION_STATUS" != "$LAST_STATUS" ]; then
133133
echo -e " ${BOLD_RED}Unexpected exit code${NC}"
@@ -143,7 +143,7 @@ git checkout HEAD~1 &>> /dev/null
143143
PREVIOUS_COMMIT="$(git show -s --format='%H' HEAD)"
144144
CACHENAME="$(git show -s --format='%as-%ct-%H' HEAD)"
145145
compile_and_cp_to_cache "$CACHENAME"
146-
LAST_STATUS=$(run_cargo)
146+
LAST_STATUS=$(run_cargo "$1" "$2")
147147

148148
if [ "$INITIAL_COMPILATION_STATUS" = "$LAST_STATUS" ]; then
149149
echo -e " ${BOLD_RED}Unexpected exit code${NC}"

scripts/compare-gas-usage/extract-gas-usage.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# This script extracts full test names and test gas usage from a `forc test` output.
44
# Usage: `forc test | test_gas_usage.sh`.
5+
# if $1 is not empty it will be appended as a new column to the csv
56

67
current_suite=""
78
results=()
@@ -19,7 +20,12 @@ while IFS= read -r line; do
1920
# printf 'Test: %s\n' "$test_name"
2021
gas="${BASH_REMATCH[2]}"
2122
# printf 'Gas: %s\n' "$gas"
22-
results+=("${current_suite}::${test_name},${gas}")
23+
24+
if [ "$1" = "" ]; then
25+
results+=("${current_suite}::${test_name},${gas}")
26+
else
27+
results+=("${current_suite}::${test_name},${gas},$1")
28+
fi
2329
fi
2430
done
2531

scripts/csv2html/csv2html.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
echo '<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<title>Pivot Demo</title>
6+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
7+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
8+
9+
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/pivottable/2.23.0/pivot.min.css">
10+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/2.23.0/pivot.min.js"></script>
11+
12+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
13+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/2.23.0/d3_renderers.min.js"></script>
14+
15+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.11/c3.min.js"></script>
16+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/2.23.0/c3_renderers.min.js"></script>
17+
18+
<script src="https://cdn.plot.ly/plotly-basic-latest.min.js"></script>
19+
<script src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/2.23.0/plotly_renderers.min.js"></script>
20+
21+
<style>
22+
body {font-family: Verdana;}
23+
.node {
24+
border: solid 1px white;
25+
font: 10px sans-serif;
26+
line-height: 12px;
27+
overflow: hidden;
28+
position: absolute;
29+
text-indent: 2px;
30+
}
31+
</style>
32+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
33+
</head>
34+
<body>
35+
<script type="text/javascript">
36+
$(function(){
37+
var renderers = $.extend(
38+
$.pivotUtilities.renderers,
39+
$.pivotUtilities.c3_renderers,
40+
$.pivotUtilities.d3_renderers,
41+
$.pivotUtilities.plotly_renderers
42+
);
43+
$("#output").pivotUI($("table"), {
44+
renderers,
45+
rendererName: "Line Chart",
46+
});
47+
});
48+
</script>
49+
<div id="output" style="margin: 30px;"></div>
50+
<br />
51+
<table>
52+
<thead>'
53+
head -n 1 "$1" | sed -e 's/^/<tr><th>/' -e 's/,/<\/th><th>/g' -e 's/$/<\/th><\/tr>/'
54+
echo ' </thead>
55+
<tbody>'
56+
tail -n +2 "$1" | sed -e 's/^/<tr><td>/' -e 's/,/<\/td><td>/g' -e 's/$/<\/td><\/tr>/'
57+
echo " </tbody>
58+
</table>
59+
<body>
60+
</html>"

sway-core/src/decl_engine/id.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,23 @@ impl SubstTypes for DeclId<TyTraitType> {
240240
}
241241
}
242242

243+
// This implementation deviates from all other DeclId<...> implementations.
244+
// For more, see https://github.com/FuelLabs/sway/pull/7440#discussion_r2428833840.
245+
// A better solution will be implemented in the future.
246+
//
247+
// TL;DR:
248+
// When a constant is declared inside a function, its value is shared by every
249+
// “version” of that function—that is, by all monomorphizations. If we “replace” the
250+
// constant, as other implementations do, we would change its value in *every* version,
251+
// which is incorrect.
243252
impl SubstTypes for DeclId<TyConstantDecl> {
244253
fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
245254
let decl_engine = ctx.engines.de();
246255
let mut decl = (*decl_engine.get(self)).clone();
247256
if decl.subst(ctx).has_changes() {
248-
decl_engine.replace(*self, decl);
257+
*self = *decl_engine
258+
.insert(decl, decl_engine.get_parsed_decl_id(self).as_ref())
259+
.id();
249260
HasChanges::Yes
250261
} else {
251262
HasChanges::No

sway-core/src/type_system/ast_elements/type_argument.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@ impl DebugWithEngines for GenericArgument {
225225
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
226226
match self {
227227
GenericArgument::Type(a) => {
228-
write!(f, "{:?}", engines.help_out(&*engines.te().get(a.type_id)))
228+
write!(
229+
f,
230+
"{:?} -> {:?}",
231+
engines.help_out(&*engines.te().get(a.initial_type_id)),
232+
engines.help_out(&*engines.te().get(a.type_id))
233+
)
229234
}
230235
GenericArgument::Const(a) => match &a.expr {
231236
ConstGenericExpr::Literal { val, .. } => {

sway-lib-std/src/codec.sw

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2789,6 +2789,13 @@ impl<const N: u64> AbiDecode for str[N] {
27892789
}
27902790
}
27912791

2792+
#[cfg(experimental_const_generics = false)]
2793+
impl AbiDecode for str[0] {
2794+
fn abi_decode(ref mut _buffer: BufferReader) -> str[0] {
2795+
__to_str_array("")
2796+
}
2797+
}
2798+
27922799
// BEGIN STRARRAY_DECODE
27932800
#[cfg(experimental_const_generics = false)]
27942801
impl AbiDecode for str[1] {
@@ -3390,6 +3397,16 @@ where
33903397
}
33913398
}
33923399

3400+
#[cfg(experimental_const_generics = false)]
3401+
impl<T> AbiDecode for [T; 0]
3402+
where
3403+
T: AbiDecode,
3404+
{
3405+
fn abi_decode(ref mut _buffer: BufferReader) -> [T; 0] {
3406+
[]
3407+
}
3408+
}
3409+
33933410
// BEGIN ARRAY_DECODE
33943411
#[cfg(experimental_const_generics = false)]
33953412
impl<T> AbiDecode for [T; 1]

0 commit comments

Comments
 (0)