High-Performance Complex Matrix Multiplication
High-Performance Complex Matrix Multiplication
Austin, TX (field@[Link])
Funding: This research was partially sponsored by grants from Intel Corporation and the
National Science Foundation (Award ACI-1550493). Any opinions, findings and conclusions or
recommendations expressed in this material are those of the author(s) and do not necessarily reflect
the views of the National Science Foundation (NSF).
1 Because they exhibit slightly less favorable numerical properties, we exclude Strassen-like efforts
Cj += A Bj
kC Bp
Cj Ap
+=
kC ~
Pack Bp → Bp
3rd loop around micro-kernel
~
Ci mC mC Ai Bp
+= ~
Pack Ai → Ai
+=
kC
1st loop around micro-kernel
nR
mR
+= kC
micro-kernel
main memory
1
L3 cache
L2 cache
L1 cache += 1
registers
Fig. 1.1. An illustration of the algorithm for computing high-performance matrix multiplication,
taken from [21], which expresses computation in terms of a so-called “block-panel” subproblem.
to remain largely the same as its real domain counterpart. (See Figure 1.1.) However,
this approach also doubles the number of assembly kernels that must be written in
order to fully support computation in either domain (real or complex) for the desired
floating-point precisions. And while computation in the complex domain may not be
of interest to all developers, it is absolutely essential for many fields and applications
in part because of complex numbers’ unique ability to encode both the phase and
magnitude of a wave. Thus, the maintainers of general-purpose matrix libraries—such
as those that export the Basic Linear Algebra Subprograms (BLAS) [2]—are typically
compelled by their diverse user bases to support complex matrix multiplication despite
the implementation and maintenance costs it may impose.
Life would be simpler for matrix library developers if complex matrix multipli-
cation was not necessary. Of course, complex matrix multiplication will always be
necessary. But what if complex matrix multiplication kernels were found to be un-
necessary? To certain actors, particularly matrix library developers, such a finding
would carry non-trivial consequence.
The predecessor to the current article investigates whether (and to what degree
of effectiveness) real domain matrix multiplication kernels can be repurposed and
leveraged toward the implementation of complex matrix multiplication [21]. In that
article, the authors develop a new class of algorithms that implement these so-called
“induced methods” for matrix multiplication in the complex domain. Instead of re-
lying on an assembly-coded complex kernel, as a conventional implementation would,
these algorithms express complex matrix multiplication only in terms of real domain
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 3
primitives.2 The authors first investigate the 4m method, which is merely the matrix
analogue of the classic definition of complex scalar multiplication and addition. They
show how this method’s implementation is facilitated by reordering real and imaginary
elements within the internal storage format used when making temporary contiguous
“packed” copies of the current matrix blocks. The second3 approach, the 3m method,
benefits from requiring only three (instead of four) matrix products at the cost of an
additional three accumulations. This Strassen-like method, which is capable of faster
runtimes but exhibits slightly lower numerical accuracy, is implemented by employing
a special data format similar to that of 4m, except that it also serves as temporary
workspace for storing the results of intermediate computations.4
We consider the current article a companion and follow-up to that previous
work [21]. Here, we will once again consider a new method for emulating complex
matrix multiplication using only real domain building blocks, and we will once again
show that a rearrangement of the real and imaginary elements within the packed ma-
trices is key to facilitating an induced method capable of delivering performance that
rivals solutions based on the conventional approach.
1.1. Audience. It is also worth emphasizing that this article’s primary audience
consists of dense linear algebra (DLA) library implemenators and related experts.
While this audience is rather narrow, the contributions we present have the potential
for wide indirect impact since the community relies heavily on this relatively small
group of implementors to develop and maintain DLA kernels for the benefit of the
entire high-performance computing (HPC) ecosystem.
1.2. Contributions. This article makes the following contributions:
• It introduces a new induced method—the 1m method—that replaces each
complex matrix multiplication with only a single real matrix multiplication.
We introduce two algorithms and, as with the previous article, analyze issues
germane to their high-performance implementations, including workspace,
packing formats, cache behavior, multithreadability, and programming effort.
A detailed review shows how 1m avoids virtually all of the challenges observed
of the 4m method.
• It promotes code reuse and portability by continuing the previous article’s
focus on solutions which may be cast in terms of real matrix multiplication
kernels. Such solutions have clear implications for developer productivity, as
they allow kernel authors to focus their efforts on fewer and simpler kernels.
• It builds on the theme of the BLIS framework as a productivity multiplier [22],
further demonstrating how complex matrix multiplication may be imple-
mented with relatively minor modifications to the source code and in such a
way that results in immediate instantiation of complex implementations for
all level-3 BLAS-like operations.
• It demonstrates performance of 1m implementations that is not only superior
to the previous effort based on the 4m method but also competitive with
2 In [21], the authors use the term “primitive” to refer to a functional abstraction that implements
a single real matrix multiplication. Such primitives are often not general purpose, and may come
with significant prerequisites to facilitate their use.
3 While this was the first method investigated, the authors of [21] actually choose to present
improve performance, including in the context of Strassen’s algorithm [8, 10, 11, 12], the computation
of the K-Nearest Neighbors [24], and tensor contraction [9].
4 FIELD G. VAN ZEE
This allows the DLA library developers to focus on a smaller and simpler
set of real domain kernels. This benefit would manifest most obviously when
instantiating BLAS-like functionality on new hardware [20].
• Portability. Induced methods avoid dependence on complex domain kernels
because they encode the idea of complex matrix product at a higher level.
This would naturally allow us to encode such methods portably within a
framework such as BLIS [22]. Once integrated into the framework, developers
and users would benefit from the immediate availability of complex matrix
multiplication implementations whenever real matrix kernels were present.
• Performance. Implementations of complex matrix multiplication that rely
on real domain kernels would likely inherit the high-performance properties
of those kernels. Any improvement to the real kernels would benefit both real
and complex domains.
Thus, it is clear that finding a suitable induced method would carry significant benefit
to DLA library and kernel developers.
2.2. The 3m and 4m methods. The authors of [21] investigated two general
ways of inducing complex matrix multiplication: the 3m method and the 4m method.
These methods are then contrasted to the conventional approach, whereby a blocked
matrix multiplication algorithm is executed with a complex domain kernel—one that
implements complex arithmetic at the scalar level, in assembly language.
The 4m method begins with the classic definition of complex scalar multiplication
and addition in terms of real and imaginary components of α, β, γ ∈ C:
γ r := γ r + αr β r − αi β i
(2.1) γ i := γ i + αi β r + αr β i
We then observe that we can apply such a definition to complex matrices A ∈ Cm×k ,
B ∈ Ck×n , and C ∈ Cm×n , provided that we can reference the real and imaginary
parts as logically separate submatrices:
C r := C r + Ar B r − Ai B i
(2.2) C i := C i + Ai B r + Ar B i
C r := C r + Ar B r − Ai B i
C i := C i + Ar + Ai B r + B i − Ar B r − Ai B i
This re-expression reduces the number of matrix products to three at the expense of
increasing the number of accumulations from four to seven. However, when the cost
of a matrix product greatly exceeds that of an accumulation, this trade-off can result
in a net reduction in computational runtime.
The authors of [21] observe that both methods may be applied to any particular
level of a blocked matrix multiplication algorithm, resulting in several algorithms,
each exhibiting somewhat different properties. The blocked algorithm used in that
article is shown in Figure 1.1 and revisited in Section 2.4 of the present article.
6 FIELD G. VAN ZEE
Algorithms that implement the 3m method were found to yield “effective flops
per second” performance that not only exceeded that of 4m, but also approached or
exceeded the theoretical peak rate of the hardware.6 Unfortunately, these compelling
results come at a cost: the numerical properties of implementations based on 3m are
slightly less robust than that of algorithms based on the conventional approach or 4m.
And although the author of [7] found that 3m was stable enough for most practical
purposes, many applications simply will not be willing to stray from the numerical
expectations implicit in conventional matrix multiplication. Thus, going forward, we
will turn our attention away from 3m and instead focus on the 4m as the standard
reference method against which we will compare.
2.3. Previous findings. For the reader’s convenience, we will now summarize
the key findings, observations, and other highlights from the previous article regarding
algorithms and implementations based on the 4m method [21].
• Since all algorithms in the 4m family execute the same number of flops, the
algorithms’ relative performance depends entirely on (1) the number of mem-
ops executed and (2) the level of cache from which fundamental elements of
matrices A and B (or rather, f.e. of the packed copies of these matrices, Ãi
and B̃p ) are reused7 . The number of memops is affected only by a halving of
certain cache blocksize needed in order to leave cache footprints of Ãi and B̃p
unchanged. The level from which f.e. are reused is determined by the level
of the matrix multiplication algorithm to which the 4m method was originally
applied. The lower the 4m method is applied, the higher the efficiency of data
reuse from and movement through the cache hierarchy.
• The lowest-level application, algorithm 4m 1a, efficiently moves f.e. of A,
B, and C from main memory to the L1 cache only once per rank-kC update,
with virtually no excess movement due to incidental cache line proximity,
and reuses f.e. from the L1 cache. It relies on a relatively simple packing
format in which complex micro-panels are stored with real and imaginary f.e.
separated into two consecutive real micro-panels, each with identical register
blocksize and k dimensions. Algorithm 4m 1a requires negligible workspace
(limited to the storage capacity of the vector register set), is well-suited for
multithreading, and is minimally disruptive to the encoding within the BLIS
framework. And while algorithm 4m 1b—a slightly higher-level application—
very narrowly outperforms 4m 1a by trading away the most optimal cache
reuse behavior for an unreduced kC cache blocksize, 4m 1a proves to be more
versatile and can be extended relatively easily to all other level-3 operations.
• The conventional assembly-based approach to complex matrix multiplication
can be viewed as a special case of 4m in which f.e. are reused from registers
rather than cache. In this way, a conventional implementation embodies the
lowest-level application of 4m possible, in which the method is applied to
individual scalars (and, typically, then optimally encoded via vector instruc-
tions).
• The way complex numbers are stored has a significant effect on performance.
6 Note that 3m and other Strassen-like algorithms are able to exceed the hardware’s theoretical
peak performance when measured in effective flops per second: that is, the 3m implementation’s
wall clock time—now shorter because of avoided matrix products—divided into the flop count of a
conventional algorithm.
7 Here, the term “reuse” refers to the reuse of f.e. that corresponds to the recurrence of Ar ,
Ai , B r , and B i in Eq. 2.2, not the reuse of whole (complex) elements that naturally occurs in the
execution of the matrix multiplication algorithm in Figure 1.1.
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 7
8 The advantages of interleaving data in advance of computation via vector instructions is also
discussed by the authors of [1] in the context of performing “batched” level-3 operations.
9 The traditional pair-wise storage is also awkward for 4m algorithms during the packing of data
each, while another factor of two comes from the cache blocksize scaling required on kC in order to
maintain the cache footprints of micro-panels of Ãi and B̃p .
8 FIELD G. VAN ZEE
The algorithm captured by Figure 1.1 forms the basis for all level-3 implementa-
tions found in the BLIS framework (as of this writing). This algorithm is based on a
so-called block-panel matrix multiplication.12 The register (mR , nR ) and cache (mC ,
kC , nC ) blocksizes labeled in the algorithmic diagram are typically chosen by the
kernel developer as a function of hardware characteristics, such as the vector register
set, cache sizes, and cache associativity. The authors of [16] present an analytical
model for identifying suitable (if not optimal) values for these blocksizes.
3. 1m method. The primary motivation for seeking a better induced method
comes from the observation that 4m inherently must update real and imaginary f.e.
of C: (1) in separate steps, and may not use vector instructions to do so (due to the
traditional pair-wise storage format); and (2) twice as frequently, in the case of 4m 1a,
due to the algorithm’s half-of-optimal cache blocksize kC . As reviewed in Section 2.3,
this imposes a significant drag on performance. If there existed an induced method
that could update real and imaginary elements in one step, it may conveniently avoid
both issues.
3.1. Derivation. Consider the classic definition of complex scalar multiplication
and accumulation, shown in Eq. 2.1, refactored and expressed in terms of matrix and
vector notation:
r r
α −αi
r
γ β
(3.1) +=
γi αi αr βi
Here, we have a singleton complex matrix multiplication problem that can naturally
be expressed as a tiny real matrix multiplication where m = k = 2 and n = 1.
Let us assume we implement this very small matrix multiplication according to the
high-performance algorithm discussed in Section 2.4.
From this, we make the following key observation: If we pack α to Ãi in such a
way that duplicates αr and αi to the second column of the micro-panel (while also
swapping the placement of the duplicates and negating the duplicated αi ), and if we
pack β to B̃p such that β i is stored to the second row of the micro-panel (which,
granted, only has one column), then a real domain gemm micro-kernel executed on
those micro-panels will compute the correct result in the complex domain and do so
with a single invocation of that micro-kernel.
Thus, Eq. 3.1 serves as a packing template that hints at how the data must be
stored. Furthermore, this template can be generalized. We augment α, β, γ with
conventional row and column indices to denote the complex elements of matrices A,
B, and C, respectively. Also, let us apply the Eq. 3.1 to the special case of m = 3,
n = 4, and k = 2 to better observe the general pattern.
r r r r
r i r i
γ00 γ01 γ02 γ03 α00 −α00 α01 −α01
i i i i i r i r
r r r r
γ00
r γ01 γ02 γ03 α00 α00 α01 α01 β00 β01 β02 β03
r r
r
γ10 γ11 γ12 γ13 r −α10 i r
−α11i i i i i
(3.2) i i i i
+= α10 i r
α11
i r
β00
r
β01
r
β02
r
β03
r
γ10 γ11 γ12 γ13 α10 α10 α11 α11 β10 β11 β12 β13
r r r
r
r i r
i i i i i
γ20 γ21 γ22 γ23 α20 −α20 α21 −α21 β10 β11 β12 β13
i i i i i r i r
γ20 γ21 γ22 γ23 α20 α20 α21 α21
the second loop around the micro-kernel. An alternative algorithm that casts its largest cache-bound
subproblem in terms of panel-block matrix multiplication is discussed in [19].
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 9
Table 3.1
1m complex domain blocksizes as a function of real domain blocksizes
Note: Blocksizes mP and nP represent the so-called “packing dimensions” for the micro-panels of
Ãi and B̃p , respectively. These values are analogous to the leading dimensions of matrices stored
by columns or rows. In BLIS micro-kernels, typically mR = mP and nR = nP , but sometimes the
kernel author may find it useful for mR < mP or nR < nP .
Algorithm: [ C ] := rmmbp( A, B, C )
for ( j = 0 : n − 1 : nC )
Identify Bj , Cj from B, C
for ( p = 0 : k − 1 : kC )
Identify Ap , Bjp from A, Bj
Pack Bjp → B̃p
for ( i = 0 : m − 1 : mC )
Identify Api , Cji from Ap , Cj
Pack Api → Ãi
for ( h = 0 : nC − 1 : nR )
Identify B̃ph , Cjih from B̃p , Cji
for ( l = 0 : mC − 1 : mR )
Identify Ãil , Cjihl from Ãi , Cjih
Cjihl := rkern( Ãil , B̃ph , Cjihl )
Fig. 3.1. Abbreviated pseudo-code for implementing the general matrix multiplication algorithm
depicted in Figure 1.1. Here, rkern calls a real domain gemm micro-kernel.
Fig. 3.2. Left: Pseudo-code for Algorithms 1m c bp and 1m r bp, which result from applying
1m c and 1m r algorithmic variants to the block-panel algorithm depicted in Figure 1.1. Here,
Pack1e and Pack1r pack matrices into the 1e and 1r formats, respectively. Right: Pseudo-code
for a virtual micro-kernel used by all 1m algorithms.
respectively. Figure 3.2 shows a hybrid algorithm that encompasses both, supporting
row- and column-stored matrices C.
As with the 3m and 4m algorithms in [21], we have separated the so-called virtual
micro-kernel into a separate function, shown in Figure 3.2 (right). This 1m-specific
virtual micro-kernel, vk1m, largely consists of a call to the real domain micro-kernel
12 FIELD G. VAN ZEE
rkern, with some added special case handling and book-keeping needed to properly
induce complex matrix multiplication. Some of the details of the virtual micro-kernel
will be addressed later.
3.5. Performance properties. Table 3.2 tallies the total number of f.e. mem-
ops required by the block-panel algorithm for both variants of 1m (1m c and 1m r).
For comparison, we also include the corresponding memop counts for a selection of
4m algorithms as well as a conventional assembly-based solution, as first published in
Table III in [21].
Notice that 1m c bp (and 1m r bp) incur additional memops relative to a con-
ventional assembly-based solution. This stems from the fact that, unlike the assembly-
based solution, 1m implementations cannot reuse13 all real and imaginary f.e. from
vector registers.
We can hypothesize that the observed performance signatures of 1m c bp and
1m r bp may be slightly different, because each places the additional memop overhead
that is unique to 1m on different parts of the computation. This stems from the fact
that there exists an asymmetry in the assignment of packing formats to matrices in
each 1m variant. Specifically, 50% more memops—relative to a conventional assembly
solution—are required during the initial packing and the movement between caches
for the matrix packed according to 1e, since that format writes four f.e. for every
two that it reads from the source operand. (Packing to 1r incurs the same number
of memops as an assembly-based solution.) Also, if 1m c bp and 1m r bp use real
micro-kernels with different micro-tile shapes (i.e., different values of mR and nR ),
those micro-kernels’ differing performance properties will likely cause the performance
signatures of 1m c bp and 1m r bp to deviate further.
Table 3.3 summarizes Table 3.2 and also lists the level of the memory hierarchy
from which each matrix operand is reused as well as a measure of memory movement
efficiency. The information listed for 4m and assembly algorithms is reproduced from
Table IV of the previous article.
3.6. Algorithm details. This section lays out important details that must be
handled when implementing the 1m method.
3.6.1. Micro-kernel I/O preference. Within the BLIS framework, micro-
kernels are registered with a property that describes their input/output preference.
The I/O preference describes whether the micro-kernel is set up to ideally use vector
instructions to load and store elements of the micro-tile by rows or by columns. This
property typically originates from the semantic orientation of vector registers used to
accumulate the mR × nR micro-panel product. Whenever possible, the BLIS frame-
work will perform logical transpositions14 so that the apparent storage of C matches
the preference property of the micro-kernel being used. This guarantees that the
micro-kernel will be able to load and store f.e. of C using vector instructions.
This preference property is merely an interesting performance detail for conven-
tional implementations (real and complex). However, in the case of 1m, it becomes
important for constructing a correctly-functioning implementation. Specifically, the
micro-kernel’s I/O preference determines whether the 1m c or 1m r algorithm is pre-
scribed. Generally speaking, a 1m c algorithmic variant must employ a micro-kernel
that prefers to access C by columns, while a 1m r algorithmic variant must use a
13
Here, the term “reuse” refers to the same reuse described in Footnote 7.
14
This amounts to a swapping of the row and column strides and a swapping of the m and n
dimensions.
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 13
Table 3.2
f.e. memops incurred by various algorithms, broken down by stage of computation
a Algorithms 4m h, 4m 1b, 4m 1a, and the assembly implementation employ a block-panel algo-
rithm, and therefore their memop counts more closely resemble those of 1m c bp and 1m r bp.
b We express the number of iterations executed in the 5th, 4th, 3rd, and 2nd loops as nn , kk , mm ,
C C C
and nn . The precise number of iterations along a dimension x using a cache blocksize xC would
R
actually be d xx e. Similarly, when blocksize scaling of 21 is required, the precise value x x/2
C C
is expressed as x2x . These simplifications allow easier comparison between algorithms while still
C
providing meaningful approximations.
c As described in Section 3.6.2, mR × nR workspace sometimes becomes mandatory, such as when
β i 6= 0. When workspace is employed in a 4m-based algorithm, the number of f.e. memops
incurred updating the micro-tile typically doubles from the values shown here.
Table 3.3
Performance properties of various algorithms
a This assumes that the micro-tile is not evicted from the L1 cache during the next call to rkern.
b In the case of 1m algorithms, we consider f.e. of A and B to be “reused” from the level of cache
in which the 1e-formatted matrix resides.
3.7. 2m. The previous article noted that because of its expression in terms of
real and imaginary matrices, 4m would perform more favorably on complex matrices
that stored real and imaginary values separately, as two real matrices. This would not
benefit the accesses on A and B since those matrices must almost always be packed to
achieve high performance and can easily be separated during that process. However,
it would benefit the accesses on C. Referring back to Eq. 3.2, we can see that storing
the real and imaginary f.e. of C separately is equivalent to a permutation of the
rows of C. In order to keep the computation expressed unchanged, this permutation
would need to be applied to matrix A as well since they both share an m dimension.
Applying such a permutation to Eq. 3.2 yields:
r r r r r i r i
γ00 γ01 γ02 γ03 α00 −α00 α01 −α01
r r r r r i r i
r r r r
γ10 γ11 γ12 γ13 α10 −α10 α11 −α11 β00 β01 β02 β03
r r r r
r i r i
i i i i
γ20 γ21 γ22 γ23 += α20
−α20 α21 −α21 β00 β01 β02 β03
(3.5)
γi i i i i r i r
r
β10 r r r
00 γ01 γ02 γ03
α
00 α00 α01 α01 β11 β12 β13
i i i i i r i r β10i i i i
γ10 γ11 γ12 γ13 α10 α10 α11 α11 β11 β12 β13
i i i i i r i r
γ20 γ21 γ22 γ23 α20 α20 α21 α21
r r r r r r i i
γ00 γ01 γ02 γ03 α00 α01 −α00 −α01
r r r r r r i i
r r r r
γ10
r γ11 γ12 γ13
α10
r α11 −α10 −α11 β00
β01 β02 β03
r r r r i i r r r r
γ20
i γ21 γ22 γ23 α20
+= i α21 −α20 −α21 β10
i β11 β12 β13
γ i i i i r r i i i
00 γ01 γ02 γ03
α
00 α01 α00 α01 β
00 β01 β02 β03
i
γ10 i i i i i r r i i i i
γ11 γ12 γ13 α10 α11 α10 α11 β10 β11 β12 β13
i i i i i i r r
γ20 γ21 γ22 γ23 α20 α21 α20 α21
16 FIELD G. VAN ZEE
Cr Ar −Ai Br
(3.6) +=
Ci Ai Ar Bi
This matrix multiplication can be computed via just two calls to a real domain matrix
multiplication primitive:
Br Br
C r += Ar −Ai , C i += Ai Ar
Bi Bi
Br
Ar −Ai , Ai Ar , and
provided that can each be stored so that they
Bi
can be referenced as single matrices. We call this the 2m method and refer to the
specific instance derived from Eq. 3.6 as 2m c.
Applying similar permutations to the n and k dimensions to Eq. 3.4 yields:
Br Bi
r i r i
(3.7) C C += A A
−B i B r
which can also be broken down in two instances of real matrix multiplication:
Br Bi
r r i i r i
C += A A , C += A A
−B i Br
which corresponds to 2m r.
We can now make a few observations about 2m:
• Eqs. 3.6 and 3.7 are identical to Eqs. 3.1 and 3.3, respectively, except that
scalars are replaced with matrices.
• The permutation along the k dimension is actually unnecessary and so the
formatting captured by Eq. 3.5 also falls under 2m. This permutation (or lack
thereof) only changes the order in which intermediate terms are accumulated.
• The storage of C r and C i in both Eqs. 3.6 and 3.7 is unspecified and does not
depend on which matrix—A or B—was originally formatted with 1e (prior
to permutation). Either C r or C i may be stored by rows, columns, or a more
exotic storage scheme, and their storage formats need not even be identical.
Thus, neither 2m c nor 2m r implies the storage of C; rather, they only
imply how matrices A and B are formatted and stored—that is, which one
contains duplicated (and negated) f.e..
• The 2m method can be applied at an arbitrary level of matrix multiplication.
For example, if we assume from Eq. 3.6 that input matrices Ar −Ai ,
r
B
Ai Ar , and
are each stored as micro-panels (stored by columns,
Bi
columns, and rows, respectively), then the implied primitive is the real gemm
micro-kernel, which will update mR × nR micro-tiles of C.
4. Performance. In this section we present performance results for implemen-
tations of 1m algorithms on a recent Intel architecture. For comparison, we include
results for a 4m algorithm as well as conventional assembly-based approaches in the
real and complex domains.
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 17
Table 4.1
Register and cache blocksizes used by various BLIS implementations of matrix multiplication,
as configured for an Intel Xeon E5-2690 v3 “Haswell” processor
Note: For 1m implementations, division by 2 is made explicit to allow the reader to quickly see both
the complex blocksize values as well as the values that would be used by the underlying real domain
micro-kernels when performing real matrix multiplication. The I/O preference of the assembly-based
implementations is indicated by a “(c)” or “(r)” (for column- or row-preferring).
to [15], the maximum the clock frequency when executing AVX instructions is 3.2 GHz when utilizing
one or two cores, and 3.0 GHz when utilizing three or more cores.
16 Accounting for the reduced AVX clock frequency, the peak performance when utilizing 24 cores
identified, with high probability, by the first 10 digits of its git “commit” (SHA1 hash) number:
ceee2f973e.
18 FIELD G. VAN ZEE
80 80
GFLOPS
GFLOPS
60 60
40 40
cgemm assembly (c) cgemm assembly (r)
sgemm assembly (c) sgemm assembly (r)
20 20
cgemm 1m_c_bp cgemm 1m_r_bp
cgemm 4m_1a (c) cgemm 4m_1a (r)
0 0
0 500 1000 1500 2000 0 500 1000 1500 2000
problem size (m = n = k) problem size (m = n = k)
gemm, double-precision (1 thread) gemm, double-precision (1 thread)
50 50
40 40
GFLOPS
GFLOPS
30 30
20 20
zgemm assembly (c) zgemm assembly (r)
dgemm assembly (c) dgemm assembly (r)
10 10
zgemm 1m_c_bp zgemm 1m_r_bp
zgemm 4m_1a (c) zgemm 4m_1a (r)
0 0
0 500 1000 1500 2000 0 500 1000 1500 2000
problem size (m = n = k) problem size (m = n = k)
the theoretical peak performance coincides with the top of each graph.
4.2. Sequential results. Figure 4.1 reports performance results for various im-
plementations of double- and single-precision complex matrix multiplication on a
single core of the Haswell processor. For these results, all matrix dimensions were
equal (e.g. m = n = k). Results for 1m c bp (which uses a column-preferring
micro-kernel) appears on the left of Figure 4.1 while those of 1m r bp (which uses a
row-preferring micro-kernel) appears on the right.
Each graph in Figure 4.1 also contains three reference implementations: BLIS’s
complex gemm based on conventional assembly-coded kernels (e.g. “cgemm assem-
bly”); BLIS’s real gemm (e.g. “sgemm assembly”); and the 4m 1a implementation
found in BLIS.19 We configured all three of these reference codes to use column-
19 Within any given graph of Figures 4.1 and 4.2, the 1m and 4m 1a implementations use the same
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 19
4.3. Multithreaded results. Figure 4.2 shows single- and double-precision per-
formance using 24 threads, with one thread bound to each physical core of the proces-
sor. Performance is presented in units of gigaflops per core to facilitate visual assess-
ment of scalability. For all BLIS implementations, we employed 4-way parallelism
within the 5th loop, 3-way parallelism within the 3rd loop, and 2-way parallelism in
the 2nd loop for a total of 24 threads. This parallelization scheme was chosen in a
manner consistent with that of the previous article using a strategy set forth in [18].
Compared to the single-threaded case, we find a more noticable difference in
multithreaded performance between the 1m algorithms. Specifically, the 1m r bp
implementation (based on a row-preferring micro-kernel) outperforms that of 1m c bp
(based on a column-preferring micro-kernel), with the difference more pronounced in
single-precision. We suspect this is rooted not in the algorithms, per se, but in the
differing micro-kernel implementations used by each 1m algorithm. The 1m r bp
algorithm is implemented with a real micro-kernel that is 6 × 16 and 6 × 8 in the
single- and double-precision cases, respectively, while 1m c bp uses 16 × 6 and 8 ×
6 micro-kernels for single- and double-precision implementations, respectively. The
observed difference in performance between the 1m algorithms is likely attributable
to the fact that the micro-kernels’ different values for mR and nR place different
bandwidth requirements when reading f.e. from the caches (primarily L1 and L2).
More specifically, larger values of mR place a heavier burden on loading elements from
the L2 cache, which is usually disadvantageous since that cache can typically sustain
lower bandwidth. By contrast, a micro-kernel with larger nR loads more elements
(per mR × nR rank-1 update) from the L1 cache, which resides closer to the processor
and can typically sustain higher bandwidth than the L2 cache.
The multithreaded 1m implementation approximately matches or exceeds its real
domain counterpart in all cases.
The 1m algorithm based on a row-preferential micro-kernel, 1m r bp, outper-
forms 4m 1a, especially in single-precision where the margin is quite wide. The
1m algorithm based on column-preferential micro-kernels, 1m c bp, performs more
real-domain micro-kernel that of the real gemm (e.g. “sgemm assembly” or “dgemm assembly”).
20 FIELD G. VAN ZEE
80 80
GFLOPS/core
GFLOPS/core
60 60
40 40
cgemm assembly (c) cgemm assembly (r)
20 sgemm assembly (c) 20 sgemm assembly (r)
cgemm 1m_c_bp cgemm 1m_r_bp
cgemm 4m_1a (c) cgemm 4m_1a (r)
0 0
0 2000 4000 6000 0 2000 4000 6000
problem size (m = n = k) problem size (m = n = k)
gemm, double-precision (24 threads) gemm, double-precision (24 threads)
40 40
GFLOPS/core
GFLOPS/core
30 30
20 20
zgemm assembly (c) zgemm assembly (r)
10 dgemm assembly (c) 10 dgemm assembly (r)
zgemm 1m_c_bp zgemm 1m_r_bp
zgemm 4m_1a (c) zgemm 4m_1a (r)
0 0
0 2000 4000 6000 0 2000 4000 6000
problem size (m = n = k) problem size (m = n = k)
poorly, barely ediging out 4m 1a in single precision and tracking closely with 4m 1a
in double precision. We suspect that 4m 1a is more resilient to the lower-performing
column-preferential micro-kernel by virtue of the fact that the algorithm’s virtual
micro-kernel leans heavily on the L1 cache, which on this architecture is capable of
being read from and written to at relatively high bandwidth (64 bytes/cycle and 32
bytes/cycle, respectively) [14].
4.4. Comparing to other implementations. While our primary goal is not
to compare the performance of the newly developed 1m implementations with that
of other established BLAS solutions, some basic comparison is merited and thus we
have included Figure 4.3 (left). These graphs are similar to those in Figure 4.1,
except that: we show only implementations based on row-preferential micro-kernels;
we omit 4m 1a; and we include results for complex gemm implementations provided
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 21
80
80
GFLOPS/core
60
GFLOPS
60
40 cgemm MKL
40 cgemm MKL
cgemm assembly (r) cgemm assembly (r)
sgemm assembly (r) 20 sgemm assembly (r)
20
cgemm 1m_r_bp cgemm 1m_r_bp
cgemm OpenBLAS cgemm OpenBLAS
0 0
0 500 1000 1500 2000 0 2000 4000 6000
problem size (m = n = k) problem size (m = n = k)
gemm, double-precision (1 thread) gemm, double-precision (24 threads)
50
40
40
GFLOPS/core
30
GFLOPS
30
Fig. 4.3. Single-threaded (left) and multithreaded (right) performance of various implementa-
tions of single-precision (top) and double-precision (bottom) complex gemm on a single core (left)
or 12 cores (right) of an Intel Xeon E5-2690 v3 “Haswell” processor. All multithreaded data points
reflect the use of 24 threads. The 1m curves are identical from those shown in Figures 4.1 and 4.2.
The theoretical peak performance coincides with the top of each graph.
multiplication via conventional assembly-based kernels. We now revisit this list and
briefly discuss whether, to what degree, and how those limitations are overcome by
algorithms based on the 1m method.
Number of calls to primitive. The most versatile 4m algorithm, 4m 1a, incurs
up to a four-fold increase in function call overhead over a comparable assembly-based
implementation. By comparison, 1m algorithms require at most a doubling of micro-
kernel function call overhead, and in certain common cases (e.g., when β ∈ R and C
is row- or column-stored), this overhead can be avoided completely. The 1m method
is clearly an improvement over 4m due to its reliance on a single invocation of the
matrix multiplication primitive.
Inefficient reuse of input data from A, B, and C. The most cache-efficient
application of 4m is the lowest level algorithm, 4m 1a, which reuses f.e. of A, B, and
C from the L1 cache. But, as shown in Table 3.3, both 1m r and 1m c variants reuse
f.e. of two of the three matrices from registers, with 1m r bp reusing f.e. of the third
matrix from the L1 cache. This would seem to be a significant improvement, though
observed performance improvement over 4m 1a will depend on properties specific to
the hardware.
Non-contiguous output to C. Algorithms based on the 4m method must up-
date only the real and then only the imaginary parts of the output matrix, twice each.
Since C is typically stored (by rows or columns) with real and imaginary f.e. inter-
leaved, this piecemeal approach prevents the real micro-kernel from using vector load
and store instructions on C during those four updates. The 1m method avoids this
issue altogether by packing A and B to formats that allow the real micro-kernel to
update contiguous real and imaginary f.e. of C simultaneously within a single invo-
cation. We suspect that this is, perhaps, the largest contributor to 1m’s performance
superiority over 4m.
Reduction of kC . Algorithm 4m 1a requires that the real micro-kernel’s pre-
ferred kC blocksize be halved in the complex algorithm in order to maintain proper
cache footprints of Ãi and B̃p as well the footprints of their constituent micro-panels.20
Using such sub-optimally sized micro-panels can noticeably hobble the performance
of 4m 1a. Looking back at Table 3.1, it may seem like 1m suffers a similar handicap;
however, the reason for halving kC and its effect are both completely different. In the
case of 1m, the use of kCz
= 12 kC is simply a conversion of units (complex elements to
real f.e.) for the purposes of identifying the size of the complex submatrices to be
packed that will induce the optimal kC value from the perspective of the real micro-
kernel, not a reduction in the f.e. footprint of the micro-panels operated upon by
that real micro-kernel. Indeed, the ability of 1m to achieve high performance when
k = 21 kC is a strength in the context of certain higher-level applications, such as
Cholesky, LU, and QR factorizations based on rank-k update. Those operations tend
to perform better when the algorithmic blocksize (corresponding to kC ) is as nar-
row as possible in order to limit the amount of computation in the lower-performing
unblocked subproblem.
Framework accommodation. The 1m algorithms are no more disruptive to the
BLIS framework than the most accommodating of 4m algorithms, 4m 1a, and much
20 Recall that the halving of k
C for 4m 1a was motivated by the desire to keep not just two, but
four real micro-panels in the L1 cache simultaneously. These correspond to the real and imaginary
parts of the current micro-panels of Ãi and B̃p .
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 23
less disruptive than the remaining algorithms. This is because, like with 4m 1a, al-
most all of the 1m implementation details are sequestered within the packing routines
and the virtual micro-kernel.
Interference with multithreading. Because the 1m algorithms are imple-
mented entirely within the packing facility and virtual micro-kernel, they parallelize
just as easily as the most thread-friendly of the 4m algorithms, 4m 1a, and entirely
avoid the threading difficulties of higher-level 4m algorithms.21
Non-applicability to two-operand operations. Certain higher-level appli-
cations of 4m are inherently incompatible with two-operand operations because they
would overwrite the original contents of the input/output operand even though subse-
quent stages of computation depend on that original input. 1m avoids this limitation
entirely. Like 4m 1a, 1m can easily be applied to two-operand level-3 operations such
as trmm and trsm.22
Summary. The analysis above suggests that the 1m method solves or avoids
most of the performance-degrading weaknesses of 4m and in the remaining cases is
no worse off than the best 4m algorithm. Thus, its observed performance superiority
was predictable.
5.2. Further discussion. Before concluding, here we offer some final thoughts
on the 1m method and its place in the larger spectrum of approaches to implementing
complex matrix multiplication.
5.2.1. Geometric interpretation. Matrix multiplication is sometimes thought
of as a three-dimensional operation with a contraction (accumulation) over the k di-
mension. This interpretation carries into the complex domain as well. However, when
each complex element is viewed in terms of its real and imaginary components, we
find that a fourth pseudo-dimension of computation (of fixed size 2) emerges, one
which also involves a contraction. The 1m method reorders and duplicates elements
of A and B in such a way that exposes and “flattens” this extra dimension of com-
putation. This, combined with the exposed treatment of real and imaginary f.e.,
causes the resulting floating-point operations to appear indistinguishable from a real
domain matrix multiplication with m and k dimensions (for column-stored C) or k
and n dimensions (for row-stored C) that are twice as long.
5.2.2. Data reuse: efficiency vs. programmability. Both the conventional
approach and 1m move data efficiently through the memory hierarchy.23 However,
once in registers, a conventional complex micro-kernel reuses those loaded values
to perform twice as many flops as 1m. The previous article observes that all 4m
algorithms make different variations of the same tradeoff: by forgoing the reuse of
f.e. from registers and instead reusing those data from some level of cache, the
algorithms avoid the need to explicitly encode complex arithmetic at the assembly
level. As it turns out, 1m makes a similar tradeoff, but gives up less while gaining
more: it is able to effectively reuse f.e. from two of the three matrix operands from
registers while still avoiding the need for a complex micro-kernel, and it manages
21 This thread-friendly property holds even when the virtual micro-kernel is bypassed altogether
rather inefficient use of cache lines as they travel through the L3, L2, and L1 caches.
24 FIELD G. VAN ZEE
to replace that kernel operation with a single real matrix multiplication. And we
would argue that increasing programmability and productivity by forfeiting a modest
performance advantage is a good trade to make under almost any circumstance.
5.2.3. Micro-kernel bandwidth. Because 1m algorithms do not explicitly re-
use f.e. of Ãi and B̃p from registers, they require higher memory bandwidth during
the micro-kernel computation than a conventional assembly-based solution.24 Specif-
ically, increased bandwidth is utilized when reading from the copy of the matrix that
is packed into the 1e format, which resides in either the L2 or L1 cache, depending
on which algorithm is being employed.25 In practice, this potential weakness of 1m
is not a concern. Yes, in principle, one could design an architecture with sufficiently
low memory bandwidth from L2 or L1 cache that a conventional complex matrix
multiplication achieves high performance while a 1m-based implementation struggles.
However, this would imply a corresponding performance shortfall in the underlying
real domain matrix kernel. Given the ubiquity and importance of real matrix multi-
plication in the scientific community, hardware vendors have great incentive to design
architectures that allow sgemm and dgemm to achieve high performance. Thus, we
would expect that 1m will remain a viable alternative for the foreseeable future.
5.2.4. Storage. The supremacy of the 1m method is closely tied to the inter-
leaved, pairwise storage of real and imaginary values—specifically, of the output ma-
trix C. If users and applications decide to begin storing complex matrices as two real
matrices (traditionally-stored, by rows or columns), one each for real and imaginary
components, the 2m approach (for numerically sensitive settings) as well as low-level
applications of 3m (for numerically insensitive settings) become more appropriate.
6. Conclusions. We began the article by reviewing the general motivations for
induced methods for complex matrix multiplication as well as the specific methods, 3m
and 4m, studied in the previous article. Then, we recast complex scalar multiplication
(and accumulation) in such a way that revealed a template that could be used to
fashion a new induced method, one that casts complex matrix multiplication in terms
of a single real matrix product. The key is the application of two new packing formats
on the left- and right-hand matrix product operands that allows us to disguise the
complex matrix multiplication as a real matrix multiplication with slightly modified
input parameters. This 1m method is shown to have two variants, depending on
whether the output matrix is stored by rows or columns. We also briefly contemplated
a related 2m method that would be applicable to more exotic storage arrangements
that separate the real and imaginary components of the matrix operands. When
implemented in the BLIS framework, competitive performance was observed for 1m
algorithms on a recent Intel microarchitecture. Finally, we reviewed the limitations of
the 4m method that are overcome by 1m and concluded by discussing a few high-level
observations.
24 While this bandwidth distinction actually holds for all induced methods, different algorithms
will place differing degrees of bandwidth pressure on the memory hierarchy, depending on the level(s)
of cache from which they reuse f.e. of A and B.
25 The bandwidth from the 1r-formatted matrix is unaffected since it only reorders (rather than
duplicates) its f.e.. And since 1m uses half the kC that is optimal in the real domain (and therefore
would perform roughly twice as many rank-kC updates), the bandwidth required for accessing f.e.
of the output matrix may also be higher, though the precise level of increase will depend on the value
of kCz that would be used by a comparable assembly-based implementation. However, bandwidth
requirements on C are already quite low, so we would not expect this difference to measurably impact
performance.
COMPLEX MATRIX MULTIPLICATION VIA THE 1M METHOD 25
The key takeaway from our study of induced methods is that the real and imag-
inary elements of complex matrices can always be reordered to accommodate the
desired fundamental primitives, whether those primitives are defined to be various
forms of real matrix multiplication (as is the case for the 4m, 3m, 2m, and 1m meth-
ods), or vector instructions (as is the case for micro-kernels that implement complex
arithmetic in assembly code). Indeed, even in the real domain, the classic matrix
multiplication algorithm’s packing format is simply a reordering of data that targets
the fundamental primitive implicit in the micro-kernel—namely, an mR × nR rank-1
update. The family of induced methods presented here and in the previous article ex-
pand upon this basic reordering so that the mathematics of complex arithmetic can be
expressed at different levels of the algorithm and of its corresponding implementation,
each yielding different benefits, costs, and performance.
Appendix A. Additional Performance Results.
In this section we present performance results for implementations of 1m method
on a recent ARM-based architecture. The primary purpose of gathering these results
was to confirm 1m performance on an non-x86 64 architecture, and also to assess the
relative performance of 1m on a platform for which Intel’s MKL was not available.
A.1. Platform and implementation details. Results presented in this sec-
tion were gathered on a single compute node consisting of two 28-core Marvell Thun-
derX2 CN9975 processors.26 Each core, running at a clock rate of 2.2 GHz, provides
a single-core peak performance of 17.6 gigaflops (GFLOPS) in double precision and
35.2 GFLOPS in single precision. Each socket has a 32MB L3 cache that is shared
among cores, and each core has a private 256KB L2 cache and 32KB L1 (data) cache.
Performance experiments were gathered under the Ubuntu 16.04 operating system
running the Linux 4.15.0 kernel. Source code was compiled by the GNU C compiler
(gcc) version [Link] The version of BLIS used in these tests was version 0.5.0-1.28
In this section, we show results for only 1m Algorithms 1m c bp, omitting re-
sults for the other three algorithms. We chose to omit 1m r bp because we did not
develop a row-preferential micro-kernel for this architecture. (Recall that 1m r vari-
ant algorithms require a micro-kernel that reads and writes elements of C in a row
orientation.) And unlike the results shown in Section 4, we did not develop conven-
tional assembly-based micro-kernels with which to compare. For reference, we also
measured performance for the complex gemm implementations found in OpenBLAS29
and ARMPL 18.4.0.
All other parameters, such as values of α and β, and the number of trials per-
formed for each problem size, as well as graphing conventions, such as scaling of the
y-axis, remain identical to those of Section 4.
A.2. Analysis. Figure A.1 contains single-threaded (left) and multithreaded
(right) performance of single-precision (top) and double-precision (bottom) complex
26 While four-way symmetric multithreading is available on this hardware, the feature was disabled
at boot-time so that the operating system detects only one logical core per physical core and schedules
threads accordingly.
27 The following optimization flags were used during compilation of BLIS and its test drivers: -O3
-ftree-vectorize -mtune=cortex-a57. In addition to those flags, the following flags were also used
when compiling assembly kernels: -march=armv8-a+fp+simd -mcpu=cortex-a57.
28 This version of BLIS may be uniquely identified, with high probability, by the first 10 digits of
30 30
25 25
GFLOPS/core
GFLOPS
20 20
15 15
15 15
GFLOPS/core
GFLOPS
10 10
Fig. A.1. Single-threaded (left) and multithreaded (right) performance of various implementa-
tions of single-precision (top) and double-precision (bottom) complex gemm on a single core (left)
or 56 cores (right) of a Marvell ThunderX2 CN9975 processor. All multithreaded data points reflect
the use of 56 threads. The real domain gemm implementation from BLIS uses a column-preferential
microkernel, as indicated the a “(c)” in the legends. (The 1m c bp implementation uses the same
column-preferential microkernel as the real domain gemm implementation.) The theoretical peak
performance coincides with the top of each graph.
REFERENCES
enough for high-performance BLIS, ACM Trans. Math. Soft., 43 (2016), pp. 12:1–12:18,
[Link] [Link]
[17] OpenBLAS. [Link] 2019.
[18] T. M. Smith, R. A. van de Geijn, M. Smelyanskiy, J. R. Hammond, and F. G. Van
Zee, Anatomy of high-performance many-threaded matrix multiplication, in Proceedings
of the 28th IEEE International Parallel & Distributed Processing Symposium (IPDPS),
IPDPS ’14, Washington, DC, USA, 2014, IEEE Computer Society, pp. 1049–1059, https:
//[Link]/10.1109/IPDPS.2014.110, [Link]
[19] F. G. Van Zee, Inducing complex matrix multiplication via the 1m method, FLAME Working
Note #85 TR-17-03, The University of Texas at Austin, Department of Computer Sciences,
2017.
[20] F. G. Van Zee, T. Smith, F. D. Igual, M. Smelyanskiy, X. Zhang, M. Kistler, V. Austel,
J. Gunnels, T. M. Low, B. Marker, L. Killough, and R. A. van de Geijn, The BLIS
framework: Experiments in portability, ACM Trans. Math. Soft., 42 (2016), pp. 12:1–12:19,
[Link]
[21] F. G. Van Zee and T. M. Smith, Implementing high-performance complex matrix multiplica-
tion via the 3m and 4m methods, ACM Trans. Math. Soft., 44 (2017), pp. 7:1–7:36.
[22] F. G. Van Zee and R. A. van de Geijn, BLIS: A framework for rapidly instantiating BLAS
functionality, ACM Trans. Math. Soft., 41 (2015), pp. 14:1–14:33, [Link]
1145/2764454.
[23] R. C. Whaley, A. Petitet, and J. J. Dongarra, Automated empirical optimization of soft-
ware and the ATLAS project, Parallel Computing, 27 (2001), pp. 3–35, [Link]
[Link] [Link]
article/pii/S0167819100000879. New Trends in High Performance Computing.
[24] C. D. Yu, J. Huang, W. Austin, B. Xiao, and G. Biros, Performance optimization for
the k-nearest neighbors kernel on x86 architectures, in Proceedings of the International
Conference for High Performance Computing, Networking, Storage and Analysis, SC ’15,
New York, NY, USA, 2015, ACM, pp. 7:1–7:12, [Link]
[Link]