performance/drop_in_bang.bend
bend-lint-rules@0.2.2.0 · 75 lines · 0 definitions
1# performance/drop_in_bang: a `!` call of a definition that returns Unit: Base's Image.drop, Image.free
2# or Image.sink, or a definition of the program declared `-> Unit`. A pure definition that returns Unit
3# does nothing but free what it receives, and a GPU frees slower than its CPU.
4# Heuristics: measured on a discrete GPU (CUDA), 30 frames that each dropped a 1M-leaf image with
5# `Image.drop!` took 1.55 s, against 0.10 s with `Image.drop` (0.104 s against 0.094 s with `--gpu off`).
6# "bend guide shaders" measured freeing Bend's slash boss scene in a bang at 12.9 ms a frame, against 8.0
7# ms, on an Apple M-series GPU, whose memory is unified: the GPU loses on both kinds of memory. The JS
8# lane has no GPU. A return type written another way (an alias of Unit) is not known.
9import Base
10import bend-lint-api@0.2.2.0/linter/lint.bend as Lint
11import bend-lint-api@0.2.2.0/shared/names.bend as Names
12import bend-lint-rules@0.2.2.0/performance/gpu_fit.bend as GpuFit
13
14# Types
15# =====
16
17# What judging a file's calls reads: the program's definitions, by the name the check gives them, and where the file
18# writes `!(`.
19type Program is Data:
20 Program{defs: Map<&2, Maybe<&2, Lint.Def>>, bangs: List<&2, U32>}
21
22# Functions
23# =========
24def message(callee: String) -> String:
25 callee
26 ++ "! only frees what it receives, as it returns Unit, and a GPU frees slower than its CPU. GPU: measured 15x slower than without the `!` on a discrete GPU (CUDA), dropping a 1M-leaf image; with unified memory (Apple M-series), freeing a scene in a bang made a frame 12.9 ms, against 8.0 ms (\"bend guide shaders\"). CPU (`--gpu off`): 10% slower. JS: no GPU, so no change. Confidence: high, as a GPU does not speed up freeing. Remove the `!`; to drop the last frame, let the next frame's fork tree drop it, a quadrant per tile (\"bend guide shaders\"). If a timing shows the `!` faster here, expect this finding."
27
28# The finding about an application below `path`, if it is a `!` call that only frees.
29def visit(
30 +program: Program,
31 +path: List<&2, Lint.Above>,
32 +node: Lint.Node
33) -> List<&2, Lint.Finding>:
34 match program node:
35 case Program{
36 defs,
37 bangs
38 } Lint.Node{
39 Lint.Application{},
40 n,
41 span,
42 Con{Lint.Node{Lint.Reference{+t}, callee, Some{Lint.Span{p, beg, end}}, k, f}, args},
43 facts
44 }:
45 Lint.finding(
46 Lint.Warning{},
47 List.contains(
48 ~U32,
49 ~U32.is_eq,
50 bangs,
51 end
52 )
53 && GpuFit.is_drop(Names.find(Lint.Def, defs, t), t),
54 span,
55 message(callee)
56 )
57 case _ _:
58 []
59
60def run(+here: Lint.Here) -> Lint.Ask(List<&2, Lint.Finding>):
61 Lint.done(
62 Lint.visit(
63 ~Program,
64 ~Lint.Finding,
65 ~visit,
66 Program{
67 Lint.Program.defs(Lint.Here.sources(here), Map.new(&2, Maybe<&2, Lint.Def>)),
68 GpuFit.bangs(Lint.Here.text(here), 0, [])
69 },
70 Lint.Here.nodes(here)
71 )
72 )
73
74def rule() -> Lint.Rule:
75 Lint.Rule{Lint.Needs{False{}, False{}}, [], Lint.OnFile{run}}