159 lines
6.7 KiB
TeX
159 lines
6.7 KiB
TeX
%% SCRAP: hardware/performance-profiling/ARM64_OPTIMIZATIONS
|
|
%% SOURCE: docs/working/hardware/performance-profiling/ARM64_OPTIMIZATIONS.adoc
|
|
%% STATUS: CURRENT
|
|
%% FITS: dev-guide/ch-raspi
|
|
%% EDITORIAL: lifted — prose rewritten to press voice
|
|
|
|
\section{ARM64 Assembly Optimizations}
|
|
|
|
StarForth's ARM64 optimizations target the AArch64 instruction set, validated
|
|
primarily on the Cortex-A72 of the Raspberry Pi 4. The work spans four headers:
|
|
\texttt{include/vm\_asm\_opt\_arm64.h} (core optimizations),
|
|
\texttt{include/vm\_inner\_interp\_arm64.h} (direct-threaded interpreter), and
|
|
\texttt{include/arch\_detect.h} (automatic architecture detection), with a full
|
|
build guide in the Raspberry Pi chapter.
|
|
|
|
\subsection{Architectural Advantages}
|
|
|
|
ARM64 offers several structural wins over x86\_64 for a stack machine. With 31
|
|
general-purpose registers against 16, the implementation keeps the VM pointer,
|
|
instruction pointer, and both stack pointers in registers and still has room to
|
|
cache the top of stack (TOS) in \texttt{x23} --- eliminating a memory access on
|
|
every operation --- with \texttt{x24}--\texttt{x28} held in reserve. Most ARM64
|
|
instructions carry conditional variants (\texttt{csel}, \texttt{cneg},
|
|
\texttt{cinc}) where x86\_64 offers only \texttt{CMOV}, removing branches and the
|
|
pipeline stalls they cause. Load and store with auto-increment fuse a memory
|
|
access and a pointer bump into a single instruction, shrinking code and easing
|
|
i-cache pressure. NEON SIMD compares sixteen bytes at once, accelerating string
|
|
comparison and bulk memory work.
|
|
|
|
\begin{table}[h]
|
|
\centering
|
|
\begin{tabular}{lll}
|
|
\toprule
|
|
Feature & x86\_64 & ARM64 (Cortex-A72) \\
|
|
\midrule
|
|
General-purpose registers & 16 & 31 \\
|
|
TOS caching & Limited & Excellent (\texttt{x23}) \\
|
|
Conditional execution & \texttt{CMOV} only & Most instructions \\
|
|
Load/store & Complex modes & Post-increment \\
|
|
SIMD width & 256-bit (AVX2) & 128-bit (NEON) \\
|
|
Power envelope & 15--25\,W TDP & 7--8\,W \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\caption{Architectural comparison relevant to the StarForth inner loop.}
|
|
\end{table}
|
|
|
|
\subsection{Expected Gains}
|
|
|
|
\begin{table}[h]
|
|
\centering
|
|
\begin{tabular}{lll}
|
|
\toprule
|
|
Optimization & x86\_64 speedup & ARM64 speedup \\
|
|
\midrule
|
|
Stack operations & 2--3$\times$ & 2.5--4$\times$ \\
|
|
Inner interpreter & 3--5$\times$ & 4--6$\times$ \\
|
|
Arithmetic & 1.5--2$\times$ & 1.8--2.5$\times$ \\
|
|
Dictionary lookup & 2--3$\times$ & 2--3$\times$ \\
|
|
String operations & 2--3$\times$ & 2--3$\times$ (NEON) \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\caption{Projected speedups by optimization class.}
|
|
\end{table}
|
|
|
|
\subsection{TOS Caching in Practice}
|
|
|
|
Because the top of stack lives in \texttt{x23} and the data-stack pointer
|
|
\texttt{x21} is a true pointer rather than an index, a push collapses to a
|
|
single auto-incrementing store:
|
|
|
|
\begin{lstlisting}[language=C]
|
|
; TOS already in x23, no load needed
|
|
str x23, [x21, #8]! ; store TOS, advance DSP in one instruction
|
|
\end{lstlisting}
|
|
|
|
Dictionary traversal benefits from prefetching the next entry while comparing
|
|
the current one. Each entry carries roughly 100--200\,ns of latency; a prefetch
|
|
hides 50--80\,ns of it, yielding a 30--50\% speedup on cold searches.
|
|
|
|
\subsection{Raspberry Pi 4 Target}
|
|
|
|
The Cortex-A72 runs four out-of-order cores at 1.5\,GHz with a 4096-entry
|
|
branch-prediction buffer. Each core has 32\,KB of L1 instruction and 32\,KB of
|
|
L1 data cache; 1\,MB of L2 is shared. Memory bandwidth on LPDDR4-3200 is
|
|
roughly 12\,GB/s theoretical and 8--10\,GB/s measured. Practical guidance
|
|
follows from the cache hierarchy: keep hot code under 32\,KB to fit L1I, align
|
|
critical loops to cache lines, prefetch predictable access patterns, structure
|
|
data on 64-byte boundaries, and minimize memory traffic by keeping working data
|
|
in registers.
|
|
|
|
\subsection{Build Configuration}
|
|
|
|
Architecture detection selects flags from \texttt{uname -m}: \texttt{-march=native}
|
|
on x86\_64, and \texttt{-march=armv8-a+crc+simd -mtune=cortex-a72} on AArch64,
|
|
each with a matching \texttt{ARCH\_*} define. The optimized profile adds
|
|
\texttt{-O3 -DUSE\_ASM\_OPT=1}; the performance profile adds
|
|
\texttt{-DUSE\_DIRECT\_THREADING=1 -flto}. Cross-compilation uses
|
|
\texttt{aarch64-linux-gnu-gcc} with static linking.
|
|
|
|
\subsection{Benchmark Results}
|
|
|
|
Measured on a Raspberry Pi 4 Model B (4\,GB) running 64-bit Raspberry Pi OS,
|
|
kernel 6.1.21-v8+, GCC 12.2.0.
|
|
|
|
\begin{table}[h]
|
|
\centering
|
|
\begin{tabular}{lll}
|
|
\toprule
|
|
Implementation & Time & Speedup \\
|
|
\midrule
|
|
C baseline (\texttt{-O2}) & 285\,ms & 1.0$\times$ \\
|
|
C optimized (\texttt{-O3}) & 198\,ms & 1.4$\times$ \\
|
|
ARM64 ASM & 68\,ms & 4.2$\times$ \\
|
|
ARM64 + direct threading & 42\,ms & 6.8$\times$ \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\caption{One million stack operations.}
|
|
\end{table}
|
|
|
|
For recursive Fibonacci(30), direct threading eliminated roughly 1.7 billion
|
|
branches (an 83\% reduction), cut instruction count by 60\%, and reduced cache
|
|
misses by 75\%, bringing the C baseline of 1250\,ms down to 245\,ms. Dictionary
|
|
lookup over 1000 words across 100k searches fell from 89\,ms (45k cache misses)
|
|
to 41\,ms (25k misses) with combined prefetch and NEON \texttt{strcmp}.
|
|
|
|
\subsection{Energy Efficiency}
|
|
|
|
ARM64 optimizations raise instantaneous power slightly through higher
|
|
utilization but complete work far faster, lowering energy per operation. One
|
|
million stack operations cost roughly 1.2\,J on the C baseline
|
|
(285\,ms $\times$ 4.2\,W) versus 0.19\,J optimized (42\,ms $\times$ 4.5\,W) ---
|
|
a 6.3$\times$ improvement in energy efficiency. Sustained workloads warrant at
|
|
least a passive heatsink; without cooling the Cortex-A72 throttles to 1.2\,GHz
|
|
near 80\,\textdegree{}C.
|
|
|
|
\subsection{Known Limitations}
|
|
|
|
\begin{itemize}
|
|
\item The NEON string compare assumes alignment and may fault on unaligned
|
|
input; an alignment check or unaligned loads would resolve it.
|
|
\item \texttt{vm\_mul\_double} produces correct low and signed-high 64-bit
|
|
halves, but unsigned 128-bit division is unimplemented; \texttt{*/MOD}
|
|
falls back to software division.
|
|
\item Cache-line zeroing via \texttt{dc zva} requires an aligned address and
|
|
may be disabled by the hypervisor or kernel.
|
|
\item The assembly is validated on Cortex-A72; other ARM64 cores (A53, A76,
|
|
Apple M-series) may need retuning and should be benchmarked on the target.
|
|
\end{itemize}
|
|
|
|
\subsection{Portability and Future Work}
|
|
|
|
The same ARM64 code runs on Apple M1/M2 (wider execution and a much larger L2,
|
|
expected 2--3$\times$ over the Pi 4, built with \texttt{-mcpu=apple-m1}), AWS
|
|
Graviton (\texttt{-mcpu=neoverse-n1}), and Android devices via the NDK
|
|
toolchain. Future directions include NEON parallel stack operations, the
|
|
Scalable Vector Extension on ARMv9, and the ARMv8.3+/8.5 security features
|
|
Pointer Authentication and Branch Target Identification.
|
|
%% PATENT: adaptive-runtime mechanisms referenced elsewhere are patent pending; no claims drafted here.
|