[PATCH -perfbook 1/2] toolsoftrade: Apply scheme of enhanced \clnref (2nd batch)
Akira Yokosawa <[email protected]> Sun, 2 Aug 2026 19:32:46 +0900
| Newsgroups | org.kernel.vger.perfbook |
|---|---|
| Message-ID | <[email protected]> |
Following commit e3dcd91d7351 ("Enhance \clnref for multi-part listing
and employ it"), apply the enhanced scheme in listings converted since.
Signed-off-by: Akira Yokosawa <[email protected]>
---
toolsoftrade/toolsoftrade.tex | 254 ++++++++++++++++++++--------------
1 file changed, 149 insertions(+), 105 deletions(-)
diff --git a/toolsoftrade/toolsoftrade.tex b/toolsoftrade/toolsoftrade.tex
index 9c0bf4db..d6146b47 100644
--- a/toolsoftrade/toolsoftrade.tex
+++ b/toolsoftrade/toolsoftrade.tex
@@ -1850,19 +1850,20 @@ first store.
\begin{adjustwidth}{10pt}{5pt}
\footnotesize
\renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-in_progress = 1;
-do_something();
-in_progress = 2;
-do_something_else();
-in_progress = 0;
+\begin{fcvlabel}[ln:toolsoftrade:Fusing Stores]
+\begin{VerbatimT}[commandchars=\\\[\]]
+in_progress = 1; \lnlbl[A1]
+do_something(); \lnlbl[A2]
+in_progress = 2; \lnlbl[A3]
+do_something_else(); \lnlbl[A4]
+in_progress = 0; \lnlbl[A5]
\end{VerbatimT}
Might be transformed to:
\renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-do_something();
-do_something_else();
-in_progress = 0;
+\begin{VerbatimT}[commandchars=\\\[\]]
+do_something(); \lnlbl[B1]
+do_something_else(); \lnlbl[B2]
+in_progress = 0; \lnlbl[B3]
\end{VerbatimT}
Because this code stores to \co{in_progress}, the compiler is
allowed so assume that there are neither concurrent loads nor
@@ -1871,14 +1872,15 @@ This permits the compiler to drop the first store to this variable, which
might frustrate those expecting it to sometimes have a non-zero value.
Prevent this by using \co{WRITE_ONCE()} and \co{barrier()} as follows:
\renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-WRITE_ONCE(in_progress, 1);
-do_something();
-WRITE_ONCE(in_progress, 2);
-do_something_else();
-WRITE_ONCE(in_progress, 0);
-in_progress = false;
+\begin{VerbatimT}[commandchars=\\\[\]]
+WRITE_ONCE(in_progress, 1); \lnlbl[C1]
+do_something(); \lnlbl[C2]
+WRITE_ONCE(in_progress, 2); \lnlbl[C3]
+do_something_else(); \lnlbl[C4]
+WRITE_ONCE(in_progress, 0); \lnlbl[C5]
+in_progress = false; \lnlbl[C6]
\end{VerbatimT}
+\end{fcvlabel}
\end{adjustwidth}
\caption{Fusing Stores}
\label{lst:toolsoftrade:Fusing Stores}
@@ -1886,22 +1888,26 @@ in_progress = false;
However, there are exceptions, for example as shown in
\cref{lst:toolsoftrade:Fusing Stores}.
-The code on line~A1 sets the variable \co{in_progress} to the value~1
-to indicate that \co{do_something()} is executing, line~A3 sets it to~2
-to indicate that \co{do_something_else()} is executing, and line~A5 sets
+\begin{fcvref}[ln:toolsoftrade:Fusing Stores]
+\setlnpref{A}
+The code on \clnref{A1} sets the variable \co{in_progress} to the value~1
+to indicate that \co{do_something()} is executing, \clnref{A3} sets it to~2
+to indicate that \co{do_something_else()} is executing, and \clnref{A5} sets
it to~0 to indicate that neither is executing.
Except that these are normal stores to a normal variable, which allows the
compiler to assume that this variable is not subject to concurrent loads.
-As noted above, this in turn means that the stores on lines~A1 and~A3
-may be dropped, as shown on lines~B1-B3.
+As noted above, this in turn means that the stores on \clnref{A1,A3}
+\setlnpref{B}
+may be dropped, as shown on \clnrefrange{B1}{B3}.
This in turn means that the value of \co{in_progress} is always zero,
which might confuse and frustrate users expecting its value to indicate
whether either of \co{do_something()} or \co{do_something_else()} is
currently executing.
+\setlnpref{C}
This problem can be avoided by using \co{WRITE_ONCE()}, as shown
-on lines~C1, C3, and~C5.
+on \clnref{C1,C3,C5}.
This use of \co{WRITE_ONCE()} informs the compiler that there might
be concurrent loads and stores, preventing it from dropping the
first two stores.
@@ -1910,48 +1916,54 @@ Unfortunately, there are additional problems with the code in
\cref{lst:toolsoftrade:Fusing Stores},
that those \co{WRITE_ONCE()} calls do not solve, including code
reordering.
+\end{fcvref}
\begin{listing}[tb]
\begin{adjustwidth}{10pt}{5pt}
\footnotesize
+\begin{fcvlabel}[ln:toolsoftrade:Reordering Code]
\renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-WRITE_ONCE(in_progress, 1);
-do_something();
-WRITE_ONCE(in_progress, 2);
-do_something_else();
-WRITE_ONCE(in_progress, 0);
+\begin{VerbatimT}[commandchars=\\\[\]]
+WRITE_ONCE(in_progress, 1); \lnlbl[A1]
+do_something(); \lnlbl[A2]
+WRITE_ONCE(in_progress, 2); \lnlbl[A3]
+do_something_else(); \lnlbl[A4]
+WRITE_ONCE(in_progress, 0); \lnlbl[A5]
\end{VerbatimT}
Might be transformed to:
\renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-do_something();
-do_something_else();
-WRITE_ONCE(in_progress, 1);
-WRITE_ONCE(in_progress, 2);
-WRITE_ONCE(in_progress, 0);
+\begin{VerbatimT}[commandchars=\\\[\]]
+do_something(); \lnlbl[B1]
+do_something_else(); \lnlbl[B2]
+WRITE_ONCE(in_progress, 1); \lnlbl[B3]
+WRITE_ONCE(in_progress, 2); \lnlbl[B4]
+WRITE_ONCE(in_progress, 0); \lnlbl[B5]
\end{VerbatimT}
This can happen if neither \co{do_something()} nor
\co{do_something_else()} contain atomic or volatile operations.
+\begin{fcvref}[ln:toolsoftrade:Fusing Stores]
The compiler is permitted to reorder these functions past both
\co{WRITE_ONCE()} invocations, obtaining close to the same
-frustrating results as shown on lines~B1-B3 of
+\setlnpref{B}
+frustrating results as shown on \clnrefrange{B1}{B3} of
\cref{lst:toolsoftrade:Fusing Stores}.
+\end{fcvref}
Prevent this by using \co{barrier()} as follows:
\renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-WRITE_ONCE(in_progress, 1);
-barrier();
-do_something();
-barrier();
-WRITE_ONCE(in_progress, 2);
-barrier();
-do_something_else();
-barrier();
-WRITE_ONCE(in_progress, 0);
-barrier();
-in_progress = false;
+\begin{VerbatimT}[commandchars=\\\[\]]
+WRITE_ONCE(in_progress, 1); \lnlbl[C1]
+barrier(); \lnlbl[C2]
+do_something(); \lnlbl[C3]
+barrier(); \lnlbl[C4]
+WRITE_ONCE(in_progress, 2); \lnlbl[C5]
+barrier(); \lnlbl[C6]
+do_something_else(); \lnlbl[C7]
+barrier(); \lnlbl[C8]
+WRITE_ONCE(in_progress, 0); \lnlbl[C9]
+barrier(); \lnlbl[C10]
+in_progress = false; \lnlbl[C11]
\end{VerbatimT}
+\end{fcvlabel}
\end{adjustwidth}
\caption{Reordering Code}
\label{lst:toolsoftrade:Reordering Code}
@@ -1961,9 +1973,11 @@ in_progress = false;
combine common subexpressions, reduce register pressure, and
improve utilization of the many functional units available on
modern superscalar microprocessors.
+\begin{fcvref}[ln:toolsoftrade:Reordering Code]
+\setlnpref{A}
It is also another reason why the code in
\cref{lst:toolsoftrade:Fusing Stores}
-is buggy, as shown on lines~A1-A3 of
+is buggy, as shown on \clnrefrange{A1}{A3} of
\cref{lst:toolsoftrade:Reordering Code}.
If the functions \co{do_something()} and \co{do_something_else()} are free
@@ -1971,11 +1985,12 @@ of volatile and atomic operations, the compiler will assume that there
are no conflicting accesses to variables accessed by these two functions.
Here, conflicting accesses to a variable are a set of concurrent accesses
to that variable, at least one of which is a store.
+\setlnpref{B}
Yes, the \co{WRITE_ONCE()} calls prevent the compiler from dropping
any of the stores, but the compiler is under no obligation to maintain
-ordering of other code with those stores, as shown on lines~B1-B3.
+ordering of other code with those stores, as shown on \clnrefrange{B1}{B3}.
Because the three stores execute in quick succession, for most purposes,
-the first two stores (lines~B3 and~B4) might as well have been dropped!
+the first two stores (\clnref{B3,B4}) might as well have been dropped!
It might seem futile to prevent the compiler from changing the order of
accesses in cases where the underlying hardware is free to reorder them.
@@ -1992,11 +2007,13 @@ independent of the ordering provided by the underlying hardware.\footnote{
you use atomics or variables of type \apic{sig_atomic_t}, instead
of \apik{READ_ONCE()} and \apik{WRITE_ONCE()}.}
+\setlnpref{C}
And this problem can be solved by inserting \co{barrier()}
-calls on lines~C2, C4, C6, C6, C8, and~C10.
+calls on \clnref{C2,C4,C6,C8,C10}.
See
\cref{sec:toolsoftrade:Assembling the Rest of a Solution}
for more detail on \co{barrier()}.
+\end{fcvref}
\QuickQuiz{
This is ridiculous!!!
@@ -2034,10 +2051,12 @@ increases in cache misses, and thus significant degradation of
both performance and scalability.
\item[Invented stores] can occur in a number of situations.
-For example, a compiler emitting code for lines~B1--B3 of
+\begin{fcvref}[ln:toolsoftrade:Fusing Stores]
+\setlnpref{B}
+For example, a compiler emitting code for \clnrefrange{B1}{B3} of
\cref{lst:toolsoftrade:Fusing Stores}
might notice that \co{in_progress} is not accessed by either
-\co{do_something()} or \co{do_something_else()}, and is stored to on line~B3.
+\co{do_something()} or \co{do_something_else()}, and is stored to on \clnref{B3}.
If either function was a complex and inline, it might be
necessary to do a register spill, in which case one attractive
place to use for temporary storage is \co{in_progress}.
@@ -2046,25 +2065,27 @@ After all, there are no accesses to it, so what is the harm?
Of course, a store of any value outside the range 0--2 to this variable
could confuse and frustrate any user who would naturally be expecting
its value to remain in this range.
+\end{fcvref}
\begin{listing}[tb]
\begin{adjustwidth}{10pt}{5pt}
\footnotesize
\renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-if (condition)
- a = 1;
-else
- do_a_bunch_of_stuff(&a);
+\begin{fcvlabel}[ln:toolsoftrade:Inventing Stores]
+\begin{VerbatimT}[commandchars=\\\[\]]
+if (condition) \lnlbl[A1]
+ a = 1; \lnlbl[A2]
+else \lnlbl[A3]
+ do_a_bunch_of_stuff(&a); \lnlbl[A4]
\end{VerbatimT}
Might be transformed to:
\renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-a = 1;
-if (!condition) {
- a = 0;
- do_a_bunch_of_stuff(&a);
-}
+\begin{VerbatimT}[commandchars=\\\[\]]
+a = 1; \lnlbl[B1]
+if (!condition) { \lnlbl[B2]
+ a = 0; \lnlbl[B3]
+ do_a_bunch_of_stuff(&a); \lnlbl[B4]
+} \lnlbl[B5]
\end{VerbatimT}
If \co{do_a_bunch_of_stuff()} uses a normal store to modify \co{a}, then
there is a store to \co{a} on both legs of the \co{if} statement.
@@ -2072,12 +2093,13 @@ This permits compiler to insert an unconditional store and a compensating
store, reducing an if-then-else to an if-then.
Prevent this by using \co{WRITE_ONCE()} as follows:
\renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-if (condition)
- WRITE_ONCE(a, 1);
-else
- do_a_bunch_of_stuff(&a);
+\begin{VerbatimT}[commandchars=\\\[\]]
+if (condition) \lnlbl[C1]
+ WRITE_ONCE(a, 1); \lnlbl[C2]
+else \lnlbl[C3]
+ do_a_bunch_of_stuff(&a); \lnlbl[C4]
\end{VerbatimT}
+\end{fcvlabel}
\end{adjustwidth}
\caption{Inventing Stores}
\label{lst:toolsoftrade:Inventing Stores}
@@ -2089,16 +2111,20 @@ Nevertheless, readers might be justified in wanting a less
outlandish example, which is provided by
\cref{lst:toolsoftrade:Inventing Stores}.
-A compiler emitting code for lines~A1--A4 of
+\begin{fcvref}[ln:toolsoftrade:Inventing Stores]
+\setlnpref{A}
+A compiler emitting code for \clnrefrange{A1}{A4} of
\cref{lst:toolsoftrade:Inventing Stores}
might know that the value of \co{a} is initially zero and that
\co{do_a_bunch_of_stuff()} modifies the variable referenced by its
argument (in this case, \co{a}).
+\setlnpref{B}
Such a compiler might be strongly tempted to optimize away one branch
-by transforming this code to that shown in lines~B1--B5.
-Here, line~B1 unconditionally stores \co{1} to \co{a}, then resets the
-value back to zero on line~B3 if the value of \co{condition} was zero.
+by transforming this code to that shown in \clnrefrange{B1}{B5}.
+Here, \clnref{B1} unconditionally stores \co{1} to \co{a}, then resets the
+value back to zero on \clnref{B3} if the value of \co{condition} was zero.
This transforms the if-then-else into an if-then, saving one branch.
+\end{fcvref}
\QuickQuiz{
Ouch!
@@ -2125,8 +2151,11 @@ This transforms the if-then-else into an if-then, saving one branch.
By inventing the store, the compiler might be introducing a
data race, which it is not permitted to do.
- Furthermore, on line C4 of
+\begin{fcvref}[ln:toolsoftrade:Inventing Stores]
+\setlnpref{C}
+ Furthermore, on \clnref{C4} of
\cref{lst:toolsoftrade:Inventing Stores},
+\end{fcvref}
the address of that variable is passed to
\co{do_a_bunch_of_stuff()}.
If the compiler can see this function's definition, and
@@ -2148,38 +2177,43 @@ against compiler optimizations that invent data races.
\begin{listing}[tb]
\begin{adjustwidth}{10pt}{5pt}
\footnotesize
+\begin{fcvlabel}[ln:toolsoftrade:Store-to-Load Conversion]
\renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-r1 = p;
-if (unlikely(r1))
- do_something_with(r1);
-barrier();
-p = NULL;
+\begin{VerbatimT}[commandchars=\\\[\]]
+r1 = p; \lnlbl[A1]
+if (unlikely(r1)) \lnlbl[A2]
+ do_something_with(r1); \lnlbl[A3]
+barrier(); \lnlbl[A4]
+p = NULL; \lnlbl[A5]
\end{VerbatimT}
Might be transformed to:
\renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-r1 = p;
-if (unlikely(r1))
- do_something_with(r1);
-barrier();
-if (p != NULL)
- p = NULL;
+\begin{VerbatimT}[commandchars=\\\[\]]
+r1 = p; \lnlbl[B1]
+if (unlikely(r1)) \lnlbl[B2]
+ do_something_with(r1); \lnlbl[B3]
+barrier(); \lnlbl[B4]
+if (p != NULL) \lnlbl[B5]
+ p = NULL; \lnlbl[B6]
\end{VerbatimT}
+\begin{fcvref}[ln:toolsoftrade:Store-to-Load Conversion]
+\setlnpref{A}
Although the \co{barrier()} prevents the compiler from using the value
-loaded from \co{p} on line~A1, profile-guided optimization might tell
+loaded from \co{p} on \clnref{A1}, profile-guided optimization might tell
the compiler that the value of \co{p} was almost always \co{NULL}.
+\end{fcvref}
Checking the value before storing might be an attractive optimization,
destructive though it might be to ordering in the case where
\co{do_something_with()} contains an \co{smp_wmb()}.
Prevent this by using \co{READ_ONCE()} and \co{WRITE_ONCE()} as follows:
\renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
-\begin{VerbatimT}
-r1 = READ_ONCE(p);
-if (unlikely(r1))
- do_something_with(r1);
-WRITE_ONCE(p, NULL);
+\begin{VerbatimT}[commandchars=\\\[\]]
+r1 = READ_ONCE(p); \lnlbl[C1]
+if (unlikely(r1)) \lnlbl[C2]
+ do_something_with(r1); \lnlbl[C3]
+WRITE_ONCE(p, NULL); \lnlbl[C4]
\end{VerbatimT}
+\end{fcvlabel}
\end{adjustwidth}
\caption{Store-to-Load Conversion}
\label{lst:toolsoftrade:Store-to-Load Conversion}
@@ -2189,21 +2223,23 @@ WRITE_ONCE(p, NULL);
that a plain store might not actually change the value in memory.
For example, consider
\cref{lst:toolsoftrade:Store-to-Load Conversion}.
-Line~A1 fetches \co{p}, but the \qco{if} statement on
-line~A2 also tells the compiler that the developer thinks that
+\begin{fcvref}[ln:toolsoftrade:Store-to-Load Conversion]\setlnpref{A}
+\Clnref{A1} fetches \co{p}, but the \qco{if} statement on
+\clnref{A2} also tells the compiler that the developer thinks that
\co{p} is usually \co{NULL}.\footnote{
The \apik{unlikely()} function provides this hint to the compiler,
and different compilers provide different ways of implementing
\co{unlikely()}.}
-The \apik{barrier()} statement on line~A4 forces the compiler to forget
+The \apik{barrier()} statement on \clnref{A4} forces the compiler to forget
the value of \co{p}, but one could imagine a (perhaps buggy) compiler
choosing to remember the hint---or getting a replacement hint via
profile-guided optimization.
-Doing so could cause the compiler to realize that line~A5 is often an
+Doing so could cause the compiler to realize that \clnref{A5} is often an
expensive no-op.
+\setlnpref{B}
Such a compiler might therefore guard the store of \co{NULL}
-with a check, as shown on lines~B5--B6.
+with a check, as shown on \clnrefrange{B5}{B6}.
Although this transformation is often desirable, it could be problematic
if the actual store was required for ordering.
For example, if \co{do_something_with()} contained a write memory barrier
@@ -2211,25 +2247,33 @@ For example, if \co{do_something_with()} contained a write memory barrier
check load.
This situation might suggest use of \apik{smp_store_release()} over
\apik{smp_wmb()}.
+\setlnpref{C}
Or alternatively, use of \co{READ_ONCE()} and \co{WRITE_ONCE()} combined
-with the assumed \co{smp_wmb()}, as shown on lines~C1 and~C4.
+with the assumed \co{smp_wmb()}, as shown on \clnref{C1,C4}.
+\end{fcvref}
\QuickQuiz{
- But doesn't the \co{if} statement on line~C2 provide a control
+\begin{fcvref}[ln:toolsoftrade:Store-to-Load Conversion]
+ \setlnpref{C}
+ But doesn't the \co{if} statement on \clnref{C2} provide a control
dependency that makes the \co{smp_wmb} unnecessary?
+\end{fcvref}
}\QuickQuizAnswer{
No, and for two reasons.
+\begin{fcvref}[ln:toolsoftrade:Store-to-Load Conversion]
+ \setlnpref{C}
First, in the Linux-kernel memory model, the control dependency
- does not extend beyond the end of the \co{if} on line~C3, and
- thus does not extend to the store on line~C4.
+ does not extend beyond the end of the \co{if} on \clnref{C3}, and
+ thus does not extend to the store on \clnref{C4}.
Second, even if the control dependency did somehow extend to
- line~C4 (perhaps due to C4 being moved into the body of the
+ \clnref{C4} (perhaps due to \clnref{C4} being moved into the body of the
\co{if} statement), that dependency would order the load on
- line~C1 with the store on line~C4.
+ line~C1 with the store on \clnref{C4}.
It would \emph{not} order any stores prior to the assumed
\co{smp_wmb()} in \co{do_something_with()}.
+\end{fcvref}
So there is a control dependency, but it does not extend far
enough and it also might not order the prior writes that the
base-commit: 6fa444b92d1158310a43074b6d680055e93fbbca
--
2.43.0