From d356b5a7097ed4a821e7d496cb8359d8ac2422e4 Mon Sep 17 00:00:00 2001 From: Nicolas Frisby Date: Fri, 13 Sep 2024 10:21:41 -0700 Subject: [PATCH] WIP second attempt --- scripts/estimating-validation-bitrate/README | 70 ------------------- .../estimating-validation-bitrate/README.md | 17 +++++ .../cumuboth.awk | 10 +++ scripts/estimating-validation-bitrate/gp.scr | 16 ----- scripts/estimating-validation-bitrate/gp.scr2 | 4 -- scripts/estimating-validation-bitrate/plot.gp | 17 +++++ .../stream-then-plot.sh | 13 ++++ .../streaming.sh | 22 ++++++ 8 files changed, 79 insertions(+), 90 deletions(-) delete mode 100644 scripts/estimating-validation-bitrate/README create mode 100644 scripts/estimating-validation-bitrate/README.md create mode 100644 scripts/estimating-validation-bitrate/cumuboth.awk delete mode 100644 scripts/estimating-validation-bitrate/gp.scr delete mode 100644 scripts/estimating-validation-bitrate/gp.scr2 create mode 100644 scripts/estimating-validation-bitrate/plot.gp create mode 100644 scripts/estimating-validation-bitrate/stream-then-plot.sh create mode 100644 scripts/estimating-validation-bitrate/streaming.sh diff --git a/scripts/estimating-validation-bitrate/README b/scripts/estimating-validation-bitrate/README deleted file mode 100644 index d46ddc15a4..0000000000 --- a/scripts/estimating-validation-bitrate/README +++ /dev/null @@ -1,70 +0,0 @@ -- `db-analyser --benchmark-ledger-ops` lists the fixed stats, and then for Byron and Shelley it currently also lists the number of txs and the total size of the txs. - -- `db-analyser --show-block-header-size` lists the size of the block's header. - I ran a patched version that also simply includes the block size. - -- `MsgBlock` has an overhead of 2 bytes (ie the list length and the word tag, since both are <24). - -- I _think_ `network-mux`'s SDU overhead is 8 bytes, from https://github.com/IntersectMBO/ouroboros-network/blob/db61131c2f375842f0930a8a9cf7f83b0cb80992/network-mux/src/Network/Mux/Codec.hs#L28-L40. - However, I don't know how many SDUs each byte requires. - So I'll omit this. - -Thus the number of bytes in each block that are carried by the `network-mux` is: 2 + 1 + hdrSize + txSize. - ------ - -The basic idea of this simple script is to divide real-world time up into non-overlapping 10 second chunks. -We use only mutator time, essentially assuming that GC is instantaneous; this conservatively _over_-estimates the effective bit rate. -Map each chunk to the set of blocks whose validation began during that chunk. -Then divide the sum of the validation mutator duration for each block by the sum of the on-the-wire size of each. - -First, a sanity check. - -``` -$ cat show-block-header-size.txt | awk '($4 != "SlotNo") {print $0} ($6 != "header") {print $0}' -[0.834136s] Started ShowBlockHeaderSize -[0.834136s] Started ShowBlockHeaderSize -[1926.666379s] Maximum encountered header size = 1012 -[1926.666379s] Maximum encountered header size = 1012 -[1926.666504s] Done -[1926.666504s] Done -ImmutableDB tip: At (Block {blockPointSlot = SlotNo 133220620, blockPointHash = 8b0597e7cf5c65b9d00af8a162d30eaae6647f868224004b02d7bd30e1d3f93f}) -ImmutableDB tip: At (Block {blockPointSlot = SlotNo 133220620, blockPointHash = 8b0597e7cf5c65b9d00af8a162d30eaae6647f868224004b02d7bd30e1d3f93f}) -``` - -Then, the real plot as well as another sanity check of the number of blocks mapped to each 10 second chunk of mutator time. - -The assumed inputs are benchmark-ledger-ops.csv and patched-show-block-header-sizes.txt, where the latter used a patched `db-analyser` to include an additional column for `GetBlockSize`. -(The sum of the `txSizes` in the `..era-specific stats` is a slight underestimate.) - -``` -$ cat benchmark-ledger-ops.csv | cut -d' ' -f1,12,13 | tail -n+2 >SlotNo-BodyTickDur-BodyAppDur.txt -$ paste SlotNo-BodyTickDur-BodyAppDur.txt <(tail -n+2 patched-show-block-header-sizes.txt) | gawk 'BEGIN {CONVFMT="%.18g" } ($1 != $8) { exit} {x = x + $2 + $3; y = y + 2 + $14; print $1, x, y}' > SlotNo-CumuDur-CumuSize.txt -$ cat SlotNo-CumuDur-CumuSize.txt | gawk 'BEGIN {w = 10; CONVFMT="%.18g"; prevX = -1; prevY = 0; prevZ = 0} {x = int($2 / (1000 * 1000 * w)); y = $3} (x != prevX) {print $1, (y - prevY) / w * 8 / (1000*1000), NR - prevZ; prevY = y; prevZ = NR} {prevX = x}' >catch -$ tail -n1 SlotNo-CumuDur-CumuSize.txt -133075481 140291882237 190610147733 -$ head -n1000 gp.scr* -==> gp.scr <== -set multiplot layout 2,1 - -set title 'Validation Bit Rate (on an AMD EPYC 7702P)' - -set xlabel 'slot number of the last block in each 10 second chunk (millions of slots)' -set ylabel 'bytes validated per 10 second chunk of mutator time (megabits per second)' - -plot 'catch' using 1:2 notitle - -set yrange [0:25] - -unset title -set xlabel 'same' -set ylabel 'same, clamped to 25' - -plot 'catch' using 1:($2 > 25 ? 25 : $2) notitle - -==> gp.scr2 <== -set xlabel 'slot number of the last block in each 10 second chunk (millions of slots)' -set ylabel 'blocks per 10 second chunk of mutator time, clamped to 1000' - -plot 'catch' using 1:(1000 < $3 ? 1000 : $3) notitle -``` diff --git a/scripts/estimating-validation-bitrate/README.md b/scripts/estimating-validation-bitrate/README.md new file mode 100644 index 0000000000..bea529f20c --- /dev/null +++ b/scripts/estimating-validation-bitrate/README.md @@ -0,0 +1,17 @@ +- `db-analyser --benchmark-ledger-ops` lists the fixed stats, including validation times and the full blockSize. + +- `MsgBlock` has an overhead of 2 bytes (ie the list length and the word tag, since both are <24). + +- I _think_ `network-mux`'s SDU overhead is 8 bytes, from https://github.com/IntersectMBO/ouroboros-network/blob/db61131c2f375842f0930a8a9cf7f83b0cb80992/network-mux/src/Network/Mux/Codec.hs#L28-L40. + However, I don't know how many SDUs each byte requires. + So I'll omit this. + +Thus the number of bytes on-the-wire is sufficiently dominated by blockSize. + +----- + +The `nix-shell -p gawk gnuplot --run 'source stream-then-plot.sh'` command renders images that help roughly answer the question: will a full buffer containing B blocks be able to refill before its entire contents is validated? +(As of slot 134028831, it runs for about about 3.5 minutes on my laptop.) + +The image width is as great as Cairo would allow. +If you open them in Firefox and then left-click, it will zoom to full height; then you can scroll along the x-axis. diff --git a/scripts/estimating-validation-bitrate/cumuboth.awk b/scripts/estimating-validation-bitrate/cumuboth.awk new file mode 100644 index 0000000000..0260a178c5 --- /dev/null +++ b/scripts/estimating-validation-bitrate/cumuboth.awk @@ -0,0 +1,10 @@ +BEGIN { CONVFMT = "%.18g"; } + +{ + SlotNo = $1; Microseconds = $12 + $13; Bytes = $14; + + CumuBytes = CumuBytes + Bytes ; + CumuMicroseconds = CumuMicroseconds + Microseconds; + + print SlotNo, CumuMicroseconds, CumuBytes; +} diff --git a/scripts/estimating-validation-bitrate/gp.scr b/scripts/estimating-validation-bitrate/gp.scr deleted file mode 100644 index 617f9f8314..0000000000 --- a/scripts/estimating-validation-bitrate/gp.scr +++ /dev/null @@ -1,16 +0,0 @@ -set multiplot layout 2,1 - -set title 'Validation Bit Rate' - -set xlabel 'slot number of the last block in each 10 second chunk (millions of slots)' -set ylabel 'bytes validated per 10 second chunk of mutator time (megabits per second)' - -plot 'catch' using ($1 / 1000000):2 notitle - -set yrange [0:25] - -unset title -set xlabel 'same' -set ylabel 'same, clamped to 25' - -plot 'catch' using ($1 / 1000000):($2 > 25 ? 25 : $2) notitle diff --git a/scripts/estimating-validation-bitrate/gp.scr2 b/scripts/estimating-validation-bitrate/gp.scr2 deleted file mode 100644 index 96db6f345a..0000000000 --- a/scripts/estimating-validation-bitrate/gp.scr2 +++ /dev/null @@ -1,4 +0,0 @@ -set xlabel 'slot number (at end of each 10 second chunk)' -set ylabel 'blocks per 10 second chunk of mutator time, clamped to 1000' - -plot 'catch' using 1:(1000 < $3 ? 1000 : $3) notitle diff --git a/scripts/estimating-validation-bitrate/plot.gp b/scripts/estimating-validation-bitrate/plot.gp new file mode 100644 index 0000000000..d3f940dc60 --- /dev/null +++ b/scripts/estimating-validation-bitrate/plot.gp @@ -0,0 +1,17 @@ +if (!exists('prefix')) prefix = 'catch' +if (!exists('suffix')) suffix = '' + +set terminal pngcairo transparent enhanced size 32767, 1024 +set output 'plot'.suffix.'.png' + +set grid ytics +set xtics 500 + +set xlabel 'total duration of validation (s)' +set ylabel 'megabits per second' + +sizes = '10 100 1000' + +# FYI: words() gives length and word(,) extracts one word + +plot for [i=1:words(sizes)] prefix.'-'.word(sizes, i) using ($1/1000000):($2*8 < 100 ? $2*8 : 100) title word(sizes, i).' block buffer' diff --git a/scripts/estimating-validation-bitrate/stream-then-plot.sh b/scripts/estimating-validation-bitrate/stream-then-plot.sh new file mode 100644 index 0000000000..65e8079963 --- /dev/null +++ b/scripts/estimating-validation-bitrate/stream-then-plot.sh @@ -0,0 +1,13 @@ +# Crunch all the data. +for i in 10 100 1000; do B=$i . streaming.sh & done; wait + +# Split the x-axis in half, so the plots are more legible. +# +# 125000 seconds is _currently_ the end of the x-axis if I plot the whole data set in one image. +for i in 10 100 1000; do cat catch-$i | awk '($1/1000000 < 125000/2) {print $0}' > catch1-$i & done +for i in 10 100 1000; do cat catch-$i | awk '($1/1000000 >= 125000/2) {print $0}' > catch2-$i & done +wait + +# Render plot-1.png and plot-2.png. +for i in 1 2; do gnuplot -e "prefix='catch$i'" -e "suffix='-$i'" plot.gp & done +wait diff --git a/scripts/estimating-validation-bitrate/streaming.sh b/scripts/estimating-validation-bitrate/streaming.sh new file mode 100644 index 0000000000..2cc8a8b92e --- /dev/null +++ b/scripts/estimating-validation-bitrate/streaming.sh @@ -0,0 +1,22 @@ +### Will a full buffer of size B refill before it empties? + +out=catch-$B + +# The resulting file has 3 columns: SlotNo, CumuMicroseconds, CumuBytes. +tail -n+2 benchmark-ledger-ops.csv | awk -f cumuboth.awk >$out + +# Discard Byron. +# cat $out | awk '($1 >= 4492800) { print $0 }' >$out.tmp; mv $out.tmp $out + +# Time and space sizes of windows of B-blocks +# +# ChainSel and BlockFetch clients use a buffer of 10 blocks. On top of that, +# BlockFetch itself is buffered according to the low/high watermark, which are +# at least 192 kibibytes and 384 kibibytes, respectively. This logic here only +# considers the block-counted buffer, not the bytes in-flight. +paste $out <(tail -n+$((B + 1)) $out) | awk '(NF > 3) {print $2, $5 - $2, $6 - $3}' >$out.tmp; mv $out.tmp $out + +# The scatter plot of this data informs the question: assuming the buffer is +# currently full, what bit rate would be necessary in order to completely +# refill the buffer before it empties. +paste $out <(tail -n+2 $out) | awk '(NF > 3) {print ($1 + $4) / 2, $6 / $2}' >$out.tmp; mv $out.tmp $out