performance/drop_in_bang.bend

bend-lint-rules@0.1.1.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.1.1.0/linter/lint.bend as Lint
11import bend-lint-api@0.1.1.0/shared/names.bend as Names
12import bend-lint-rules@0.1.1.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, GpuFit.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(GpuFit.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        GpuFit.program.defs(Lint.Here.sources(here), Map.new(&2, Maybe<&2, GpuFit.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}}