Optimizing Burst matrix multiplications
Learn how to achieve 4.5x performance gains with matrix multiplications in Unity’s Burst, and how your C# code affects the final assembly.
Optimizing Burst matrix multiplications

Top of the line UI for the benchmark project
What if I told you that you can get 4.5x performance gains for matrix multiplications in Unity’s Burst with a few code tweaks? And I don’t mean the basic “use [BurstCompile]” optimization, but gains on top of that.
I recently needed to optimize matrix multiplications in an inner loop that calculates skinning matrices from a bones hierarchy, so I looked deeper into the Burst compiler’s output to see if I could squeeze more out of the CPU. Matrix multiplications are used in many places, such as physics, positioning objects, and animation, so optimizations there can go a long way.
This article is a relatively deep dive into improving the Burst compiler’s output, primarily for mobile platforms with Arm’s AArch64 (e.g. Android, iPhone). I describe and analyze a journey of 7 performance improvement attempts.
Although you’ll get more out of this article if you have Arm assembly knowledge, you should be able to follow along most of the content if you have a basic familiarity with any assembly language. If not, you can always stick with the C# code, and perhaps you’ll find a new interest in learning more about assembly. There are links to external reference material in the text, and a list of references at the end.
Test bench
The benchmark code is fairly minimal with a short method with a configurable number of matrix multiplications over a configurable number of matrix pairs. InputData is a struct that holds all inputs and outputs to make passing parameters around easier.
MatrixCount controls the number of matrix results to compute so we can get profiler marker values that aren’t too small. Very small timing values are more susceptible to jittering. Also, when benchmarking several algorithms in the same frame, large enough matrix counts ensure that the CPU cache can’t hold matrices accessed by the previous algorithms. To be on the safe-side, I vary the execution order each frame, and thrash the cache between the algorithms to expel any remaining cached matrices.
MultiplicationCount drives the number of mathematical operations for each matrix. Varying this number gives a more complete performance picture.
MatrixSourceA, MatrixSourceB and Result are of type NativeArray<Matrix4x4>. Such matrices could come from Transform components for example.
Here’s the reference method used for performance comparisons. Again, note that we start with a Burst-compiled method. The inner loop has a somewhat contrived way logic to access a different matrix for each iteration, but without causing a random memory access each time, which would cause many CPU cache misses.
[BurstCompile]
public static void Matrix4x4(ref InputData data)
{
for (int i = 0; i < data.MatrixCount; i++)
{
Matrix4x4 matrix = data.MatrixSourceA[i];
for (int j = 0; j < data.MultiplicationCount; j++)
{
// Use a different matrix for each inner loop iteration
var otherMatrix = data.MatrixSourceB[math.max(0, i - j)];
matrix *= otherMatrix;
}
data.Result[i] = matrix;
}
}
All the results presented here are median values for 100+ frames on Android with a not-so-recent Samsung Galaxy A20e and ARMV8a as the target architecture.
Calling Burst methods
To further minimize external factors on performance, I call the Burst methods on the main thread instead of using jobs. That’s Unity’s officially recommended way to run Burst methods on the main thread.
Note that the NoAlias attribute on the fields of InputData is especially important with this approach.
In most use cases, you won’t need to use the
[NoAlias]attribute. You don’t need to apply it to a struct definition that already has a[NativeContainer]attribute or to fields in job structs because in these cases Burst infers the no-alias information.
Since we’re not using jobs, we need to use the NoAlias attribute for best results, otherwise Burst might generate extra (slower) code to handle memory region overlaps.
[ReadOnly] [NoAlias] public NativeArray<Matrix4x4> MatrixSourceA;
Attempt 1 — Mathematics package
With Burst and mathematical operations, the first step is to use the Unity Mathematics package:
Unity Mathematics is a C# math library that provides vector types and math functions that have a shader-like syntax, similar to SIMD or HLSL. The Burst compiler uses Unity Mathematics to compile C#/IL code into highly efficient native code.
So we’ll use math.mul instead of Matrix4x4.operator* for the matrix multiplications.
[BurstCompile]
public static void MathematicsPkg(ref InputData data)
{
for (int i = 0; i < data.MatrixCount; i++)
{
// Conversion from Matrix4x4 to float4x4
float4x4 matrix = data.MatrixSourceA[i];
for (int j = 0; j < data.MultiplicationCount; j++)
{
// Conversion
float4x4 otherMatrix = data.MatrixSourceB[math.max(0, i - j)];
matrix = math.mul(matrix, otherMatrix);
}
data.Result[i] = matrix; // Implicit conversion to Matrix4x4
}
}
As expected, this already gives a good speedup.

Speedup with Unity’s Mathematics package
In the C# code, there are two significant changes:
- The matrix multiplications are done inline instead of with a function call, which avoids copying matrix values on the stack as function parameters.
- The matrices are converted from
Matrix4x4tofloat4x4.
The generated assembly in the Burst Inspector shows the details.

Assembly comparison for inner loop
The speedup graph is relatively flat because the cost of the original function call is orders of magnitude larger than anything else.
Attempt 2 — Burst attributes
Since the inner loop uses lots of floating point operations, the [BurstCompile] parameter FloatMode is very relevant.
[BurstCompile(FloatMode = FloatMode.Fast)]
public static void FloatModeAttrib(ref InputData data)
Another very good speedup for virtually no work.

Speedup with Burst attributes
The performance gains come from fewer assembly instructions for the matrix multiplications. FloatMode.Fast allows Burst to rearrange SIMD operations which in turn allows it to merge sequences of FADD and FMUL instructions into FMLA (fused multiply-add). The excellent Matrix multiplication section of Arm’s Neon Programmer Guide for Armv8-A explains how this works.
The speedup increases asymptotically with the number of multiplications since we only optimized the inner loop. The non-multiplication related code is identical, and it’s proportionally more significant than it was in the previous attempt.
Attempt 3 — NativeArray casts
The optimizations from the previous two attempts are probably well-known. Now we’ll start with less well-known (or even new?) optimizations.
If this sentence from the first attempt caused you to raise an eyebrow, you were on to something:
The matrices are converted from
Matrix4x4tofloat4x4.
Both types contain 16 float values, with the same column major order (and no other field). They’re identical in memory! There should be nothing to convert. However, the Burst compiler doesn’t quite act fully on this equivalence sadly. We can enlighten it if we reinterpret (i.e. cast) the NativeArray parameters from Matrix4x4 to float4x4.
The NativeArray local variables also provide a second smaller optimization (see code comments). In the previous attempts, we reference the arrays directly from ref InputData data inside the loops. Since that’s a ref type, the arrays could in theory change at any time (i.e. in another thread). Therefore Burst inserts an extra memory access for each loaded matrix to lookup its array field.
So this is caveat #2 of calling Burst methods directly. Since those calls require ref (or in) struct parameters, we need to make sure that the compiler can deduce inputs that don’t change during the method’s execution. Burst jobs sidestep this issue because their data is stored directly in structs, not references to structs.
[BurstCompile(FloatMode = FloatMode.Fast)]
public static void NativeArrayCasts(ref InputData data)
{
// "Cast" from NativeArray<Matrix4x4> to NativeArray<float4x4>
var sourceA = data.MatrixSourceA.Reinterpret<float4x4>();
var sourceB = data.MatrixSourceB.Reinterpret<float4x4>();
var result = data.Result.Reinterpret<float4x4>();
for (int i = 0; i < data.MatrixCount; i++)
{
// float4x4 matrix = data.MatrixSourceA.ReinterpretLoad<float4x4>(i);
// ^ Other alternative, but not as efficient here as reinterpreting the
// array because InputData is a ref type
float4x4 matrix = sourceA[i];
for (int j = 0; j < data.MultiplicationCount; j++)
{
float4x4 otherMatrix = sourceB[math.max(0, i - j)];
matrix = math.mul(matrix, otherMatrix);
}
result[i] = matrix;
}
}
Yet another significant speedup from a one-liner, nice!

Speedup with NativeArray casts
The assembly of the previous attempt has 8 LDP (load pair of registers) instructions in the inner loop. Notice how the destination register names start with s (e.g. s16), which means 8 single float value pair loads. In other words, it missed out on the ability to load full float4 matrix columns in each SIMD&FP register (i.e. floating point and vector operation registers).
As the Neon guide explains, we only need 2 LDP instructions to load the 4 matrix columns of otherMatrix. We’re now loading pairs of full float4 values (e.g. in q16 and q17 registers) for each LDP instruction.

Assembly comparison for inner loop
Attempt 4 — Micro optimizations
At this point, the largest slow-downs are removed, but looking closely at the assembly shows a few more opportunities:
- Similarly to the arrays in the previous attempt, the fields
data.MatrixCountanddata.MultiplicationCountare reloaded from the struct’s memory on every loop iteration, even though their value doesn’t change. We can load these values once (in plentiful registers) instead. - Forward iteration in the outer loop requires an
ADDandCMPinstruction. Reverse iteration (i.e. towards 0) can use a singleSUBS(note theSsuffix) instruction that both decrements and generates a program flow condition code. We won’t use reverse iteration for the inner loop because matrix multiplications aren’t commutative.
[BurstCompile(FloatMode = FloatMode.Fast)]
public static void MicroOptimizations(ref InputData data)
{
var sourceA = data.MatrixSourceA.Reinterpret<float4x4>();
var sourceB = data.MatrixSourceB.Reinterpret<float4x4>();
var result = data.Result.Reinterpret<float4x4>();
// Move loop control struct fields to local variables
int matrixCount = data.MatrixCount;
int multiplicationCount = data.MultiplicationCount;
for (int i = matrixCount - 1; i >= 0; i--) // Reverse loop
{
float4x4 matrix = sourceA[i];
for (int j = 0; j < multiplicationCount; j++)
{
float4x4 otherMatrix = sourceB[math.max(0, i - j)];
matrix = math.mul(matrix, otherMatrix);
}
result[i] = matrix;
}
}
The gains aren’t as impressive this time, and they seem more susceptible to noise, but it’s still a consistent win.

Speedup with micro optimizations
We’ll switch gears in the next 3 attempts. But before we do, a quick digression.
Custom assembly in Burst
As part of my research for this article, I wanted to try to inject hand-coded assembly into a Burst method. I had two goals in mind:
- Test if replacing pairs of
LDPinstructions with a singleLD1instruction, as shown in the Neon guide’s matrix multiplication code sample, would be faster. While Burst provides thevld1intrinsic for Arm, several variations of it are missing, including the one I needed that fills 4 vector registers with 4 floats each. - Simply to see if I could do it. I couldn’t find any information out there about this process so it became an extra challenge to make it work.
Briefly, the results are:
- Failure!
LD1was consistently slower. The Cortex-A75 Software Optimization Guide offers a possible explanation withLD1having higher latency, which can impede instruction pipelining. - Success! It’s definitely more fiddly than inline assembly in C++, but it’s possible.
The RGB conversion example in the Neon guide uses the LD3 instruction which isn’t available either in Burst 1.8 as of writing. Conceivably, custom assembly could potentially offer performance gains in that scenario. However, the better long-term path would probably be to get Unity to add that intrinsic to Burst!
How to use custom assembly in Burst
For reference, here’s an overview of the steps to inject custom assembly in a Burst method of an Android build. The steps should be similar with lib_burst_generated.dll on Windows. I haven’t found information about this elsewhere so perhaps it’ll prove useful in the future.
- Add a long enough Burst method with a recognizable name.
- In Unity, export the Android project.
- Locate the Burst-compiled library
unityLibrary/src/main/jniLibs/arm64-v8a/lib_burst_generated.so. Note that it contains (Dwarf) debugging symbols at this stage. - Locate the start of the Burst method’s assembly in the library file. You can do this with
readelf -Ws lib_burst_generated.so | grep MyMethodto get the method’s virtual address, followed byreadelf -WS lib_burst_generated.so | grep ‘\[13]’(13 is the number of the .text section) to get the section’s virtual address and file offset. The first byte of the method in the file is atfile_offset + method_VMA - section_VMA. - Modify the assembly with a hex editor, a script, etc. Make sure that the new assembly isn’t longer than the original or you’ll overwrite unrelated code. Shorter is fine. Tip: Ghidra is pretty good at exploring binaries and applying patches.
- Open the project in Android Studio and build it.
In theory, it should be possible to integrate all these steps as part of a build pipeline.
One final warning: in case you’re looking for binary instruction patterns based on what you read in the Burst Inspector, know that the instructions in the compiled binary are sometimes slightly reordered.
Affine matrices
So far we’ve dealt with general 4x4 matrices. However most matrices from “regular use” of Unity (i.e. mostly all except perspective projection) are affine matrices. Combinations of translation, scaling, rotation, and shearing produce this type of matrix (e.g. a Transform component’s matrix).
For our purposes, their most interesting property is that the last row is [0, 0, 0, 1]. We can exploit this knowledge to further optimize matrix multiplications and reduce the size of each matrix in memory. Here are the AffineTransform fields, which flatten down to 4 float3 values.
public struct AffineTransform : IEquatable<AffineTransform>, IFormattable
{
/// <summary>
/// The rotation and scale part of the affine transformation.
/// </summary>
public float3x3 rs;
/// <summary>
/// The translation part of the affine transformation.
/// </summary>
public float3 t;
From here on we’ll assume that all source matrices (and therefore results) are affine. Note also that the following speedup graphs are comparisons with the “micro optimizations” attempt 4, not a rolling “vs previous attempt” as above.
Attempt 5 — Optimized affine multiplication
As we’ve seen above, the assembly for a 4x4 matrix multiplication is 16 vector instructions. Each one multiplies a column of matrix A by one of the 16 values in matrix B. The fixed last row of an affine matrix means that we can skip some operations and get the same result.
Let’s replace math.mul with a new custom shorter MulAffine method. Technically, the method requires only matrix B be affine to get correct results; matrix A can be any type of matrix.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float4x4 MulAffine(float4x4 a, float4x4 bAffine)
{
return math.float4x4(
a.c0 * bAffine.c0.x + a.c1 * bAffine.c0.y + a.c2 * bAffine.c0.z,
a.c0 * bAffine.c1.x + a.c1 * bAffine.c1.y + a.c2 * bAffine.c1.z,
a.c0 * bAffine.c2.x + a.c1 * bAffine.c2.y + a.c2 * bAffine.c2.z,
a.c0 * bAffine.c3.x + a.c1 * bAffine.c3.y + a.c2 * bAffine.c3.z + a.c3);
}
Shaving off 4 of the 16 multiplication instructions in the inner loop pays off.

Speedup with custom affine multiply
Onwards!
Attempt 6 — AffineTransform source
What if we stored only the 12 meaningful elements of each matrix in memory? The Mathematics package conveniently supplies the AffineTransform type, along with specialized operations for the type. One such example is the math.mul method.
I’ll skip the code here which only changes the element type of all arrays to AffineTransform. Loading 12 floats from memory instead of 16 and fewer instructions for the multiplication is bound to be a win right?

Speedup (slowdown) with AffineTransform
Wrong… A quick look at the assembly shows why. The compiler inserts many instructions to deal with the mismatch between the float3 columns of AffineTransform and the CPU’s float4-sized vector registers.

Assembly of inner loop
But not all is lost…
Attempt 7 — Intrinsics to the rescue
As observed, the float3 columns of AffineTransform aren’t optimal for the CPU’s 128-bit vector registers. There’s no speed advantage to using those registers partially since the operations that operate on them take the same amount of time.
The first intuition is that in terms of the number of vector registers uses, there’s really no difference between a 4x4 and an affine (i.e. 3x4) matrix; the last vector element of each affine matrix’s column just becomes padding that’s later discarded.

Affine matrix in memory
We can load 3x128 bit vectors once and use Neon EXT instructions to rearrange the data in 4 vector registers.
The second intuition is that otherMatrix in the inner loop doesn’t even need to be an actual matrix (4x4 or affine). With the vectorized multiplication, each element of the right side matrix is used separately as a scalar. So let’s take advantage of the float4x3 type to hold those matrix elements tightly packed in 3 vector registers, and use a custom multiplication method adjusted to match the type.
Here’s the inner loop code that uses Burst intrinsics to optimize loading the initial matrix and storing the multiplication result.
[BurstCompile(FloatMode = FloatMode.Fast)]
public static void AffineTransformIntrinsics(ref InputData data)
{
// Use float4x3 to directly load matrices in 3 128-bit vectors
// (i.e. 3 x float4)
var sourceA = data.MatrixSourceAffineA.Reinterpret<float4x3>();
var sourceB = data.MatrixSourceAffineB.Reinterpret<float4x3>();
var result = data.ResultAffine.Reinterpret<float4x3>();
int matrixCount = data.MatrixCount;
int multiplicationCount = data.MultiplicationCount;
for (int i = 0; i < matrixCount; i++)
{
float4x3 affineMatrixPacked = sourceA[i];
// Each column's float4 is [row0, row1, row2, PAD]
// Note: extracting this to a separate method leads to suboptimal code.
// Compiler bug?
float4x4 matrix;
if (IsNeonSupported)
{
// Note: element ordering in a v128 is [3, 2, 1, 0]
v128 matrixPacked_0_3 = ToV128(affineMatrixPacked.c0);
v128 matrixPacked_4_7 = ToV128(affineMatrixPacked.c1);
v128 matrixPacked_8_11 = ToV128(affineMatrixPacked.c2);
matrix = new float4x4(affineMatrixPacked.c0,
ToFloat4(vextq_s32(matrixPacked_0_3, matrixPacked_4_7, 3)),
ToFloat4(vextq_s32(matrixPacked_4_7, matrixPacked_8_11, 2)),
ToFloat4(vextq_s32(matrixPacked_8_11, matrixPacked_8_11, 1)));
}
else
{
// Desktop didn't need intrinsics to get optimal assembly
// Note: explicit shuffle() (currently) gives better results than
// swizzling
// e.g. float4(affineMatrixPacked.c0.w, affineMatrixPacked.c1.xyz)
var c1 = math.shuffle(affineMatrixPacked.c0, affineMatrixPacked.c1,
math.ShuffleComponent.LeftW, math.ShuffleComponent.RightX,
math.ShuffleComponent.RightY, math.ShuffleComponent.RightZ);
var c2 = math.shuffle(affineMatrixPacked.c1, affineMatrixPacked.c2,
math.ShuffleComponent.LeftZ, math.ShuffleComponent.LeftW,
math.ShuffleComponent.RightX, math.ShuffleComponent.RightY);
var c3 = math.shuffle(affineMatrixPacked.c2, affineMatrixPacked.c2,
math.ShuffleComponent.LeftY, math.ShuffleComponent.LeftZ,
math.ShuffleComponent.LeftW, math.ShuffleComponent.LeftX);
matrix = new float4x4(affineMatrixPacked.c0, c1, c2, c3);
}
for (int j = 0; j < multiplicationCount; j++)
{
// We don't need the "proper" matrix type for multiplication.
// All floats are used separately so any type with 12 floats works.
float4x3 otherMatrix = sourceB[math.max(0, i - j)];
matrix = MulAffine(matrix, otherMatrix);
}
// Reverses the unpacking/loading from above
// Note: strangely, extracting this part to a separate method is fine.
result[i] = AffineFloat4x4ToPackedAffine(matrix);
}
}

Speedup with Burst intrinsics
Very good improvements over the previous attempt at using the AffineTransform type! It’s still not as fast as attempt 5 with 4x4 matrices, which shows that for this algorithm, on this particular device, performance is probably bound more by the number of instructions than memory accesses.
Results summary
Here’s an overview of all attempts in a single graph.

Algorithm speedup summary — Mobile
The first two speedups were both the largest and the most easily achieved. They’re essentially about following Burst best practices.
However there were still a lot of performance gains available from “helping” the Burst compiler get around some hurdles as the next two attempts show.
The next big gain came from reducing the number of vector multiplication operations to multiply two affine matrices together. It’s a drop-in replacement for the math.mul method to use where appropriate, while avoiding the slowdown from using the AffineTransform type directly.
Lastly, storing the matrices as affine in memory first gave a significant slowdown, but we were able to overcome most of it with intrinsics. Keep in mind that reducing memory usage (~25% here) can be a valuable gain on its own, so that lower multiplication performance could be worth it depending on the situation.
Desktop results
Here’s a summary of performance on desktop, without a detailed analysis of each attempt like above.

Algorithm speedup summary — Desktop
While the type mismatch observed in AffineTransform hits hard, once we reach the intrinsics version (actually, some well chosen math.shuffle calls are enough on desktop), it becomes the fastest alternative.
I’ll make an educated guess that since the clock speed is 2.2x faster on desktop, the bottleneck of the short inner loop is memory access rather than float/ALU instructions, so loading 12 floats instead of 16 (3 vs 4 vmovu instructions) has a greater effect than on mobile.
Conclusion
When I started working on this article I didn’t expect it would become so involved. My starting point was to share information about the speedup from NativeArray “casts”, but then surprisingly many more facets and possibilities appeared. It’s been a very interesting journey learning a lot more about AArch64 and ARMv8-A, as well as its Neon SIMD instruction set, the structure of ELF files, and getting more familiar with reverse engineering in Ghidra.
My main take-aways from this research are:
- Significant performance gains are possible when working with affine matrices, but be cautious of using the
AffineTransformtype itself. - A sufficiently advanced (Burst) compiler could automatically deduce several of the optimizations I described. Maybe it will eventually. It could be useful to add a
math.ShuffleComponent.DontCarevalue formath.shuffleas a compiler hint so it can pick whatever “padding” vector element is most efficient with the current instruction set. - In some scenarios I examined, the Burst compiler was able to produce more efficient assembly for x86–64 than for Arm. So it’s possibly even more relevant to inspect the Burst Inspector for mobile platforms.
- While Unity recommends not using jobs to execute Burst methods on the main thread, there’s a number of subtle caveats from doing that. I’m not entirely sure that the added complexities are worth the possible minor optimization of directly calling Burst methods.
- It’s possible to inject hand-written assembly into Burst methods, but the maintenance cost of that approach could be (too) high. Use sparingly if at all.
It can be quite time consuming to optimize algorithms at this low-level, so I would suggest making sure it’s time well spent by profiling first (and also after!). It also pays off to know some details of the architecture that runs your code. Coming from x86 assembly, I found it surprisingly easy to pick up AArch64 assembly once I found the right learning resources.
Thanks to Victor for reviewing this post.
References
Arm AArch64
- Learn the Architecture — A-profile — Arm ‒ Structured landing page with many guides for AArch64.
- Learn the architecture — Introducing the Arm architecture ‒ High level overview of the Arm architecture.
- Learn the architecture — A64 Instruction Set Architecture Guide ‒ Introduction to the instruction set. Includes the function call standard and how arguments are passed with registers.
- Getting Started with Arm Assembly Language ‒ Basics of using assembly in practice.
- Arm Compiler armasm User Guide ‒ Guide for Arm’s compiler and programmer-focused instruction set reference.
- Arm A-profile A64 Instruction Set Architecture ‒ More detailed instruction set reference, including instruction encoding.
- Procedure Call Standard for the Arm 64-bit Architecture (AArch64) ‒ Detailed description of the standard for calling functions.
- Arm Architecture Reference Manual for A-profile architecture ‒ Everything you could want to know in a single very long document.
- How to train your (snap)dragon — part 1 — assembling the team ‒ Short third-party guide for an “Hello World” in assembly on arm64 Windows.
Arm’s Neon SIMD instruction set
- Neon Programmer Guide for Armv8-A Coding for Neon ‒ Overview of the SIMD Neon instruction set. Includes a matrix multiplication example.
- Arm Neon Intrinsics reference ‒ Filterable list of all intrinsics with descriptions.
Burst
- Using Neon C# intrinsics with Unity Burst ‒ Arm guide to using their intrinsics with Burst.
- Intrinsics: Low-level engine development with Burst — Unite Copenhagen
- ECS Track: Deep Dive into the Burst Compiler — Unite LA
- Analyzing Burst generated assemblies
And lastly, a very good 25 part series from Raymond Chen on many practical topics of AArch64. I couldn’t find a landing page for the series, so here are all the links:
- The AArch64 processor (aka arm64), part 1: Introduction — The Old New Thing
- The AArch64 processor (aka arm64), part 2: Extended register operations — The Old New Thing
- The AArch64 processor (aka arm64), part 3: Addressing modes — The Old New Thing
- The AArch64 processor (aka arm64), part 4: Addition and subtraction — The Old New Thing
- The AArch64 processor (aka arm64), part 5: Multiplication and division — The Old New Thing
- The AArch64 processor (aka arm64), part 6: Bitwise operations — The Old New Thing
- The AArch64 processor (aka arm64), part 7: Bitfield manipulation — The Old New Thing
- The AArch64 processor (aka arm64), part 8: Bit shifting and rotation — The Old New Thing
- The AArch64 processor (aka arm64), part 9: Sign and zero extension — The Old New Thing
- The AArch64 processor (aka arm64), part 10: Loading constants — The Old New Thing
- The AArch64 processor (aka arm64), part 11: Loading addresses — The Old New Thing
- The AArch64 processor (aka arm64), part 12: Memory access and alignment — The Old New Thing
- The AArch64 processor (aka arm64), part 13: Atomic access — The Old New Thing
- The AArch64 processor (aka arm64), part 14: Barriers — The Old New Thing
- The AArch64 processor (aka arm64), part 15: Control transfer — The Old New Thing
- The AArch64 processor (aka arm64), part 16: Conditional execution — The Old New Thing
- The AArch64 processor (aka arm64), part 17: Manipulating flags — The Old New Thing
- The AArch64 processor (aka arm64), part 18: Return address protection — The Old New Thing
- The AArch64 processor (aka arm64), part 19: Miscellaneous instructions — The Old New Thing
- The AArch64 processor (aka arm64), part 20: The classic calling convention — The Old New Thing
- The AArch64 processor (aka arm64), part 21: Classic function prologues and epilogues — The Old New Thing
- The AArch64 processor (aka arm64), part 22: Other kinds of classic prologues and epilogues — The Old New Thing
- The AArch64 processor (aka arm64), part 23: Common patterns — The Old New Thing
- The AArch64 processor (aka arm64), part 24: Code walkthrough — The Old New Thing
- The AArch64 processor (aka arm64), part 25: The ARM64EC ABI — The Old New Thing
메타데이터
- post_id
- bfb301de80ce
- slug
- optimizing-burst-matrix-multiplications-bfb301de80ce
- url
- https://medium.com/toca-boca-tech-blog/optimizing-burst-matrix-multiplications-bfb301de80ce
- canonical_url
- https://medium.com/toca-boca-tech-blog/optimizing-burst-matrix-multiplications-bfb301de80ce
- author_url
- https://medium.com/@berniegp
- status
- ok
- fetched_at
- 2026-06-23 17:05:31