Skip to content

Commit bf7ff3e

Browse files
authored
Adjust the cost of allocations to a much higher value (#7307)
VM feedback suggests that struct.new etc. should be avoided when we selectify, as the cost is high even in generational GC implementations.
1 parent 9d5628c commit bf7ff3e

File tree

3 files changed

+147
-87
lines changed

3 files changed

+147
-87
lines changed

src/ir/cost.h

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
4343
// but usually requires some loads and comparisons.
4444
static const CostType CastCost = 5;
4545

46+
// Generational GC can be very efficient, but even so allocations have a high
47+
// cost due to shortening the time to the next collection.
48+
static const CostType AllocationCost = 100;
49+
4650
CostType maybeVisit(Expression* curr) { return curr ? visit(curr) : 0; }
4751

4852
CostType visitBlock(Block* curr) {
@@ -671,11 +675,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
671675
return base + nullCheckCost(curr->ref) + maybeVisit(curr->ref);
672676
}
673677
CostType visitStructNew(StructNew* curr) {
674-
// While allocation itself is almost free with generational GC, there is
675-
// at least some baseline cost, plus writing the fields. (If we use default
676-
// values for the fields, then it is possible they are all 0 and if so, we
677-
// can get that almost for free as well, so don't add anything there.)
678-
CostType ret = 4 + curr->operands.size();
678+
CostType ret = AllocationCost + curr->operands.size();
679679
for (auto* child : curr->operands) {
680680
ret += visit(child);
681681
}
@@ -696,16 +696,16 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
696696
visit(curr->expected) + visit(curr->replacement);
697697
}
698698
CostType visitArrayNew(ArrayNew* curr) {
699-
return 4 + visit(curr->size) + maybeVisit(curr->init);
699+
return AllocationCost + visit(curr->size) + maybeVisit(curr->init);
700700
}
701701
CostType visitArrayNewData(ArrayNewData* curr) {
702-
return 4 + visit(curr->offset) + visit(curr->size);
702+
return AllocationCost + visit(curr->offset) + visit(curr->size);
703703
}
704704
CostType visitArrayNewElem(ArrayNewElem* curr) {
705-
return 4 + visit(curr->offset) + visit(curr->size);
705+
return AllocationCost + visit(curr->offset) + visit(curr->size);
706706
}
707707
CostType visitArrayNewFixed(ArrayNewFixed* curr) {
708-
CostType ret = 4;
708+
CostType ret = AllocationCost;
709709
for (auto* child : curr->values) {
710710
ret += visit(child);
711711
}
@@ -743,15 +743,21 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
743743
return 8 + visit(curr->ref) + maybeVisit(curr->start) +
744744
maybeVisit(curr->end);
745745
}
746-
CostType visitStringConst(StringConst* curr) { return 4; }
746+
CostType visitStringConst(StringConst* curr) {
747+
// We do not use AllocationCost here because these will end up imported as
748+
// constants from the outside, after string lowering (and even natively, a
749+
// VM can implement them as global constants).
750+
return 4;
751+
}
747752
CostType visitStringMeasure(StringMeasure* curr) {
748753
return 6 + visit(curr->ref);
749754
}
750755
CostType visitStringEncode(StringEncode* curr) {
751-
return 6 + visit(curr->str) + visit(curr->array) + visit(curr->start);
756+
return AllocationCost + 2 + visit(curr->str) + visit(curr->array) +
757+
visit(curr->start);
752758
}
753759
CostType visitStringConcat(StringConcat* curr) {
754-
return 10 + visit(curr->left) + visit(curr->right);
760+
return AllocationCost + 6 + visit(curr->left) + visit(curr->right);
755761
}
756762
CostType visitStringEq(StringEq* curr) {
757763
// "3" is chosen since strings might or might not be interned in the engine.
@@ -765,13 +771,11 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
765771
}
766772

767773
CostType visitContNew(ContNew* curr) {
768-
// Some arbitrary "high" value, reflecting that this may allocate a stack
769-
return 14 + visit(curr->func);
774+
return AllocationCost + 10 + visit(curr->func);
770775
}
771776
CostType visitContBind(ContBind* curr) {
772-
// Inspired by struct.new: The only cost of cont.bind is that it may need to
773-
// allocate a buffer to hold the arguments.
774-
CostType ret = 4;
777+
// cont.bind may need to allocate a buffer to hold the arguments.
778+
CostType ret = AllocationCost;
775779
ret += visit(curr->cont);
776780
for (auto* arg : curr->operands) {
777781
ret += visit(arg);

test/lit/passes/monomorphize-benefit.wast

Lines changed: 68 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -947,15 +947,11 @@
947947

948948
;; THIRD__: (type $3 (func (param anyref i32)))
949949

950-
;; THIRD__: (type $4 (func (param (ref $A))))
950+
;; THIRD__: (type $4 (func (param i32)))
951951

952-
;; THIRD__: (type $5 (func (param anyref)))
952+
;; THIRD__: (type $5 (func (param (ref $A))))
953953

954-
;; THIRD__: (type $6 (func (param i32) (result (ref $A))))
955-
956-
;; THIRD__: (type $7 (func (param i32)))
957-
958-
;; THIRD__: (type $8 (func (result (ref $A))))
954+
;; THIRD__: (type $6 (func (param anyref)))
959955

960956
;; THIRD__: (import "a" "b" (func $import (type $1)))
961957
;; TWOTRDS: (type $1 (func))
@@ -964,11 +960,11 @@
964960

965961
;; TWOTRDS: (type $3 (func (param anyref i32)))
966962

967-
;; TWOTRDS: (type $4 (func (param (ref $A))))
963+
;; TWOTRDS: (type $4 (func (param i32)))
968964

969-
;; TWOTRDS: (type $5 (func (param anyref)))
965+
;; TWOTRDS: (type $5 (func (param (ref $A))))
970966

971-
;; TWOTRDS: (type $6 (func (param i32)))
967+
;; TWOTRDS: (type $6 (func (param anyref)))
972968

973969
;; TWOTRDS: (import "a" "b" (func $import (type $1)))
974970
;; HUNDRED: (type $1 (func (param anyref) (result (ref $A))))
@@ -984,8 +980,8 @@
984980

985981
;; DEFAULT: (import "a" "c" (func $import2 (type $4) (param (ref $A))))
986982
;; ZERO___: (import "a" "c" (func $import2 (type $8) (param (ref $A))))
987-
;; THIRD__: (import "a" "c" (func $import2 (type $4) (param (ref $A))))
988-
;; TWOTRDS: (import "a" "c" (func $import2 (type $4) (param (ref $A))))
983+
;; THIRD__: (import "a" "c" (func $import2 (type $5) (param (ref $A))))
984+
;; TWOTRDS: (import "a" "c" (func $import2 (type $5) (param (ref $A))))
989985
;; HUNDRED: (import "a" "c" (func $import2 (type $4) (param (ref $A))))
990986
(import "a" "c" (func $import2 (param (ref $A))))
991987

@@ -1173,12 +1169,8 @@
11731169
;; THIRD__-NEXT: )
11741170
;; THIRD__-NEXT: )
11751171
;; THIRD__-NEXT: )
1176-
;; THIRD__-NEXT: (drop
1177-
;; THIRD__-NEXT: (call $target-long
1178-
;; THIRD__-NEXT: (struct.new $A
1179-
;; THIRD__-NEXT: (local.get $y)
1180-
;; THIRD__-NEXT: )
1181-
;; THIRD__-NEXT: )
1172+
;; THIRD__-NEXT: (call $target-long_6
1173+
;; THIRD__-NEXT: (local.get $y)
11821174
;; THIRD__-NEXT: )
11831175
;; THIRD__-NEXT: (call $import2
11841176
;; THIRD__-NEXT: (call $target-long
@@ -1187,7 +1179,7 @@
11871179
;; THIRD__-NEXT: )
11881180
;; THIRD__-NEXT: )
11891181
;; THIRD__-NEXT: )
1190-
;; THIRD__-NEXT: (call $target-long_6)
1182+
;; THIRD__-NEXT: (call $target-long_7)
11911183
;; THIRD__-NEXT: )
11921184
;; TWOTRDS: (func $calls-long (type $3) (param $x anyref) (param $y i32)
11931185
;; TWOTRDS-NEXT: (call $import2
@@ -1207,12 +1199,8 @@
12071199
;; TWOTRDS-NEXT: )
12081200
;; TWOTRDS-NEXT: )
12091201
;; TWOTRDS-NEXT: )
1210-
;; TWOTRDS-NEXT: (drop
1211-
;; TWOTRDS-NEXT: (call $target-long
1212-
;; TWOTRDS-NEXT: (struct.new $A
1213-
;; TWOTRDS-NEXT: (local.get $y)
1214-
;; TWOTRDS-NEXT: )
1215-
;; TWOTRDS-NEXT: )
1202+
;; TWOTRDS-NEXT: (call $target-long_6
1203+
;; TWOTRDS-NEXT: (local.get $y)
12161204
;; TWOTRDS-NEXT: )
12171205
;; TWOTRDS-NEXT: (call $import2
12181206
;; TWOTRDS-NEXT: (call $target-long
@@ -1221,13 +1209,7 @@
12211209
;; TWOTRDS-NEXT: )
12221210
;; TWOTRDS-NEXT: )
12231211
;; TWOTRDS-NEXT: )
1224-
;; TWOTRDS-NEXT: (drop
1225-
;; TWOTRDS-NEXT: (call $target-long
1226-
;; TWOTRDS-NEXT: (struct.new $A
1227-
;; TWOTRDS-NEXT: (i32.const 42)
1228-
;; TWOTRDS-NEXT: )
1229-
;; TWOTRDS-NEXT: )
1230-
;; TWOTRDS-NEXT: )
1212+
;; TWOTRDS-NEXT: (call $target-long_7)
12311213
;; TWOTRDS-NEXT: )
12321214
;; HUNDRED: (func $calls-long (type $2) (param $x anyref) (param $y i32)
12331215
;; HUNDRED-NEXT: (call $import2
@@ -1275,9 +1257,9 @@
12751257
;; * Optimize in all cases when the minimum benefit is 0% (except the
12761258
;; first call, which is a trivial call context). Removing a cast is
12771259
;; enough to justify optimizing.
1278-
;; * In 33% we optimize only the very last case. There we remove both a
1279-
;; cast and a struct.new, which ends up just over 33%.
1280-
;; * In 66% and 100% we optimize nothing at all.
1260+
;; * In 33% and 66% we optimize only two cases. There we remove both a cast
1261+
;; and a struct.new, and the struct.new's high cost makes it worthwhile.
1262+
;; * In 100% we optimize nothing at all.
12811263

12821264
;; Call with an unknown input and the output is sent to an import.
12831265
(call $import2
@@ -1379,29 +1361,35 @@
13791361
;; THIRD__-NEXT: (local.get $x)
13801362
;; THIRD__-NEXT: )
13811363
;; THIRD__-NEXT: )
1382-
;; THIRD__-NEXT: (call $target-short_7
1364+
;; THIRD__-NEXT: (call $target-short_8
13831365
;; THIRD__-NEXT: (local.get $x)
13841366
;; THIRD__-NEXT: )
13851367
;; THIRD__-NEXT: (call $import2
1386-
;; THIRD__-NEXT: (call $target-short_8
1387-
;; THIRD__-NEXT: (local.get $y)
1368+
;; THIRD__-NEXT: (call $target-short
1369+
;; THIRD__-NEXT: (struct.new $A
1370+
;; THIRD__-NEXT: (local.get $y)
1371+
;; THIRD__-NEXT: )
13881372
;; THIRD__-NEXT: )
13891373
;; THIRD__-NEXT: )
13901374
;; THIRD__-NEXT: (call $target-short_9
13911375
;; THIRD__-NEXT: (local.get $y)
13921376
;; THIRD__-NEXT: )
13931377
;; THIRD__-NEXT: (call $import2
1394-
;; THIRD__-NEXT: (call $target-short_10)
1378+
;; THIRD__-NEXT: (call $target-short
1379+
;; THIRD__-NEXT: (struct.new $A
1380+
;; THIRD__-NEXT: (i32.const 42)
1381+
;; THIRD__-NEXT: )
1382+
;; THIRD__-NEXT: )
13951383
;; THIRD__-NEXT: )
1396-
;; THIRD__-NEXT: (call $target-short_11)
1384+
;; THIRD__-NEXT: (call $target-short_10)
13971385
;; THIRD__-NEXT: )
13981386
;; TWOTRDS: (func $calls-short (type $3) (param $x anyref) (param $y i32)
13991387
;; TWOTRDS-NEXT: (call $import2
14001388
;; TWOTRDS-NEXT: (call $target-short
14011389
;; TWOTRDS-NEXT: (local.get $x)
14021390
;; TWOTRDS-NEXT: )
14031391
;; TWOTRDS-NEXT: )
1404-
;; TWOTRDS-NEXT: (call $target-short_6
1392+
;; TWOTRDS-NEXT: (call $target-short_8
14051393
;; TWOTRDS-NEXT: (local.get $x)
14061394
;; TWOTRDS-NEXT: )
14071395
;; TWOTRDS-NEXT: (call $import2
@@ -1411,7 +1399,7 @@
14111399
;; TWOTRDS-NEXT: )
14121400
;; TWOTRDS-NEXT: )
14131401
;; TWOTRDS-NEXT: )
1414-
;; TWOTRDS-NEXT: (call $target-short_7
1402+
;; TWOTRDS-NEXT: (call $target-short_9
14151403
;; TWOTRDS-NEXT: (local.get $y)
14161404
;; TWOTRDS-NEXT: )
14171405
;; TWOTRDS-NEXT: (call $import2
@@ -1421,7 +1409,7 @@
14211409
;; TWOTRDS-NEXT: )
14221410
;; TWOTRDS-NEXT: )
14231411
;; TWOTRDS-NEXT: )
1424-
;; TWOTRDS-NEXT: (call $target-short_8)
1412+
;; TWOTRDS-NEXT: (call $target-short_10)
14251413
;; TWOTRDS-NEXT: )
14261414
;; HUNDRED: (func $calls-short (type $2) (param $x anyref) (param $y i32)
14271415
;; HUNDRED-NEXT: (call $import2
@@ -1466,8 +1454,10 @@
14661454
(func $calls-short (param $x anyref) (param $y i32)
14671455
;; As above, but now calling the short function:
14681456
;; * 0% is the same with the long function: any improvement is enough.
1469-
;; * 33% optimizes them all (but for the first, which is a trivial call
1470-
;; context).
1457+
;; * 33% optimizes some, but not all (the first is a trivial call context;
1458+
;; some of the others do not optimize because the struct.new is
1459+
;; expensive, and if the output goes into another call, we cannot remove
1460+
;; it).
14711461
;; * 66% optimizes a few less cases: when the output isn't dropped then we
14721462
;; can't do enough work to justify it.
14731463
;; * 100% optimizes nothing.
@@ -1606,7 +1596,7 @@
16061596
;; ZERO___-NEXT: (nop)
16071597
;; ZERO___-NEXT: )
16081598

1609-
;; THIRD__: (func $target-long_6 (type $1)
1599+
;; THIRD__: (func $target-long_6 (type $4) (param $0 i32)
16101600
;; THIRD__-NEXT: (call $import)
16111601
;; THIRD__-NEXT: (call $import)
16121602
;; THIRD__-NEXT: (call $import)
@@ -1615,38 +1605,53 @@
16151605
;; THIRD__-NEXT: (call $import)
16161606
;; THIRD__-NEXT: )
16171607

1618-
;; THIRD__: (func $target-short_7 (type $5) (param $0 anyref)
1619-
;; THIRD__-NEXT: (nop)
1620-
;; THIRD__-NEXT: )
1621-
1622-
;; THIRD__: (func $target-short_8 (type $6) (param $0 i32) (result (ref $A))
1623-
;; THIRD__-NEXT: (struct.new $A
1624-
;; THIRD__-NEXT: (local.get $0)
1625-
;; THIRD__-NEXT: )
1608+
;; THIRD__: (func $target-long_7 (type $1)
1609+
;; THIRD__-NEXT: (call $import)
1610+
;; THIRD__-NEXT: (call $import)
1611+
;; THIRD__-NEXT: (call $import)
1612+
;; THIRD__-NEXT: (call $import)
1613+
;; THIRD__-NEXT: (call $import)
1614+
;; THIRD__-NEXT: (call $import)
16261615
;; THIRD__-NEXT: )
16271616

1628-
;; THIRD__: (func $target-short_9 (type $7) (param $0 i32)
1617+
;; THIRD__: (func $target-short_8 (type $6) (param $0 anyref)
16291618
;; THIRD__-NEXT: (nop)
16301619
;; THIRD__-NEXT: )
16311620

1632-
;; THIRD__: (func $target-short_10 (type $8) (result (ref $A))
1633-
;; THIRD__-NEXT: (struct.new $A
1634-
;; THIRD__-NEXT: (i32.const 42)
1635-
;; THIRD__-NEXT: )
1621+
;; THIRD__: (func $target-short_9 (type $4) (param $0 i32)
1622+
;; THIRD__-NEXT: (nop)
16361623
;; THIRD__-NEXT: )
16371624

1638-
;; THIRD__: (func $target-short_11 (type $1)
1625+
;; THIRD__: (func $target-short_10 (type $1)
16391626
;; THIRD__-NEXT: (nop)
16401627
;; THIRD__-NEXT: )
16411628

1642-
;; TWOTRDS: (func $target-short_6 (type $5) (param $0 anyref)
1629+
;; TWOTRDS: (func $target-long_6 (type $4) (param $0 i32)
1630+
;; TWOTRDS-NEXT: (call $import)
1631+
;; TWOTRDS-NEXT: (call $import)
1632+
;; TWOTRDS-NEXT: (call $import)
1633+
;; TWOTRDS-NEXT: (call $import)
1634+
;; TWOTRDS-NEXT: (call $import)
1635+
;; TWOTRDS-NEXT: (call $import)
1636+
;; TWOTRDS-NEXT: )
1637+
1638+
;; TWOTRDS: (func $target-long_7 (type $1)
1639+
;; TWOTRDS-NEXT: (call $import)
1640+
;; TWOTRDS-NEXT: (call $import)
1641+
;; TWOTRDS-NEXT: (call $import)
1642+
;; TWOTRDS-NEXT: (call $import)
1643+
;; TWOTRDS-NEXT: (call $import)
1644+
;; TWOTRDS-NEXT: (call $import)
1645+
;; TWOTRDS-NEXT: )
1646+
1647+
;; TWOTRDS: (func $target-short_8 (type $6) (param $0 anyref)
16431648
;; TWOTRDS-NEXT: (nop)
16441649
;; TWOTRDS-NEXT: )
16451650

1646-
;; TWOTRDS: (func $target-short_7 (type $6) (param $0 i32)
1651+
;; TWOTRDS: (func $target-short_9 (type $4) (param $0 i32)
16471652
;; TWOTRDS-NEXT: (nop)
16481653
;; TWOTRDS-NEXT: )
16491654

1650-
;; TWOTRDS: (func $target-short_8 (type $1)
1655+
;; TWOTRDS: (func $target-short_10 (type $1)
16511656
;; TWOTRDS-NEXT: (nop)
16521657
;; TWOTRDS-NEXT: )

0 commit comments

Comments
 (0)