The contents of this document are provided in connection with Advanced Micro Devices,
Inc. (“AMD”) products. AMD makes no representations or warranties with respect to the
accuracy or completeness of the contents of this publication and reserves the right to make
changes to specifications and product descriptions at any time without notice. The information contained herein may be of a preliminary or advance nature and is subject to
change without notice. No license, whether express, implied, arising by estoppel or otherwise, to any intellectual property rights is granted by this publication. Except as set forth
in AMD’s Standard Terms and Conditions of Sale, AMD assumes no liability whatsoever,
and disclaims any express or implied warranty, relating to its products including, but not
limited to, the implied warranty of merchantability, fitness for a particular purpose, or
infringement of any intellectual property right.
AMD’s products are not designed, intended, authorized or warranted for use as components in systems intended for surgical implant into the body, or in other applications
intended to support or sustain life, or in any other application in which the failure of
AMD’s product could create a situation where personal injury, death, or severe property or
environmental damage may occur. AMD reserves the right to discontinue or make changes
to its products at any time without notice.
AMD, the AMD Arrow logo, and combinations thereof, 3DNow!, AMD Virtualization and AMD-V are trademarks of Advanced Micro
Devices, Inc.
HyperTransport is a licensed trademark of th
Linux is a registered trademark o
Microsoft and Windows are registered trademarks
MMX is a trademark of Intel Corporation.
PCI-X and PCI Express are registered trademarks of the PCI-Special Interest Group (PCI-SIG).
Solaris is a registered trademar
Other product names used in this publication are for iden
f Linus Torvald.
k of Sun Microsystems, Inc.
e HyperTransport Technology Consortium.
of Microsoft Corporation.
tification purposes only and may be trademarks of their respective companies.
Page 3
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
xviTables
Page 17
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Revision History
DateRev. Description
November 20083.09Corrected the document title.
October 20083.08 Corrected assemby code examples in section 4.5, ’Take Advantage of x86 and
AMD64 Complex Addressing Modes’ on page 57.
Made minor clarifications in 4.10, ’Alternatives to SHLD Instruction’ on page 66.
Clarified latency information in 6.7, ’Avoiding the LOOP Instruction’ on page 106.
Corrected assembly language statements in 7.2, ’Loop Unrolling’ on page 110.
Clarified latency discrepancies between processor revisions in ’Changes in
Latency’ on page 239.
April 20083.06 Revised section 4.13, on page ’Code Padding with Operand-Size Override and
Multibyte NOP’ on page 68.
Revised section 9.16, ’Floating-Point Scalar Conversions’ on page 166.
Revised the ‘Tag-to-retire Count’ and ’Completion-to-retire count’ on page 304.
Clarified the use of the IbsDcStBnkCon and IbsDcLdBnkCon fields in IbsOpData3
register, clarified the description concerning data cache miss latency in ’Interpreting
IBS Load/Store Data’ on page 305.
December 20073.05Added Appendix E, ’NUMA Optimizations for I/O Devices’ on page 283. Added
Appendix F, ’Remarks on the RDTSC(P) Instruction’ on page 295
September 2007 3.04 Updated ’Memory and String Routines’ on page 92; updated ’Code Padding with
Operand-Size Override and Multibyte NOP’ on page 68; updated Appendix C,
’Instruction Latencies’ on page 237; Added Appendix G, ’Guide to InstructionBased Sampling on AMD Family 10h Processors’ on page 297.
June 20073.03 Made several minor corrections.
May 20073.02 Corrected several AMD-V™ related typos.
May 20073.01 Corrected the specified value for the MXCSR misaligned exception mask bit.
April 20073.00 Initial release.
Revision Historyxvii
Page 18
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
xviiiRevision History
Page 19
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Chapter 1Introduction
This guide provides optimization information and recommendations for AMD Family 10h
processors. These optimizations are designed to yield software code that is fast, compact, and
efficient. Toward this end, the optimizations in each of the following chapters are listed in order of
importance.
This chapter covers the following topics:
TopicPage
Intended Audience1
Getting Started1
Using This Guide2
Important New Terms4
Key Optimizations6
What’s New on AMD Family 10h Processors6
1.1Intended Audience
This book is intended for compiler and assembler designers, as well as C, C++, and assemblylanguage programmers writing performance-sensitive code sequences. This guide assumes that you
are familiar with the AMD64 instruction set and the AMD64 architecture (registers and programming
modes). For complete information on the AMD64 architecture and instruction set, see the
multivolume AMD64 Architecture Programmer’s Manual available from AMD.com. Individual
volumes and their order numbers are provided below.
TitleOrder Number
Volume 1, Application Programming24592
Volume 2, System Programming24593
Volume 3, General-Purpose and System Instructions24594
Volume 4, 128-Bit Media Instructions26568
Volume 5, 64-Bit Media and x87 Floating-Point Instructions26569
More experienced readers may skip to “Key Optimizations” on page 6, which identifies the most
important optimizations, and to “What’s New on AMD Family 10h Processors” on page 6 for a quick
review of key new performance enhancement features introduced with AMD Family 10h processors.
Chapter 1Introduction1
Page 20
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
1.3Using This Guide
Each of the remaining chapters in this document focuses on a particular general area of relevance to
software optimization on AMD Family 10h processors. Each chapter is organized into a set of one or
more recommended related optimizations pertaining to a particular issue. These sections are divided
into three sections:
•Optimization—Specifies the recommended action required for achieving the optimization under
consideration.
•Application—Specifies the type of software for which the particular optimization is relevant (i.e.,
to 32-bit software or 64-bit software or to both).
•Rationale—Provides additional explanatory technical information regarding the particular
optimization. This section usually provides illustrative C, C++, or assembly code examples as
well.
The chapters that follow cover the following topics:
•Chapter 2, “C and C++ Source-Level Optimizations,” describes techniques that you can use to
optimize your C and C++ source code.
•Chapter 3, “General 64-Bit Optimizations,” presents general assembly-language optimizations
that can improve the performance of software designed to run in 64-bit mode. The optimizations
in this chapter apply only to 64-bit software.
•Chapter 4, “Instruction-Decoding Optimizations,” discusses optimizations designed to maximize
the number of instructions that the processor can decode at one time.
•Chapter 5, “Cache and Memory Optimizations,” discusses how to take advantage of the large L1
caches and high-bandwidth buses.
•Chapter 6, “Branch Optimizations,” discusses improving branch prediction and minimizing
branch penalties.
•Chapter 7, “Scheduling Optimizations.” discusses improving instruction scheduling in the
•Chapter 9, “Optimizing with SIMD Instructions,” discusses the 64-bit and 128-bit SIMD
instructions (SSE, SSE2, SSE3, SSE4a) used to encode floating-point and integer operations.
•Chapter 10, “x87 Floating-Point Optimizations,” discusses optimizations using the x87 assembly
instructions.
•Chapter 11, “Multiprocessor Considerations,” discusses processor/core selection and related
issues for applications running on multiprocessor/multicore cache coherent non-uniform memory
access (ccNUMA) configurations.
•Chapter 12, “Optimizing Secure Systems,” discusses ways to minimize the performance overhead
imposed by the virtualization of a guest.
2IntroductionChapter 1
Page 21
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
•Appendix A, “Microarchitecture of AMD Family 10h Processors,” discusses the internal design,
or microarchitecture, of the AMD Family 10h processor and provides information about
translation-lookaside buffers and other functional units that, while not part of the main processor,
are integrated on the chip.
•Appendix B, “Implementation of Write-Combining,” describes how AMD Family 10h processors
perform memory write-combining.
•Appendix C, “Instruction Latencies,” provides a complete listing of all AMD64 instructions with
each instruction’s decode type, execution latency, and—where applicable—the pipes and
throughput used in the floating-point unit.
•Appendix D, “Tools and APIs for AMD Family 10h ccNUMA Multiprocessor Systems” provides
information on tools for programming in NUMA environments.
•Appendix E, “NUMA Optimizations for I/O Devices” provides information on the association of
particular I/O devices with a specific nodes in a NUMA system.
•Appendix F, “Remarks on the RDTSC(P) Instruction” provides information on using the RDTSC
and RDTSCP instructions to load the value of the time stamp counter (TSC).
•Appendix G, “Guide to Instruction-Based Sampling on AMD Family 10h Processors” describes
the use of intruction-based sampling to monitor software performance.
1.3.1Special Information
Special information in this guide is marked as follows:
This symbol appears next to the most important, or key, optimizations.
1.3.2Numbering Systems
The following suffixes identify different numbering systems:
This suffixIdentifies a
bBinary number. For example, the binary equivalent of the number 5 is written 101b.
dDecimal number. Decimal numbers are followed by this suffix only when the possibility of
confusion exists. In general, decimal numbers are shown without a suffix.
hHexadecimal number. For example, the hexadecimal equivalent of the number 60 is
written 3Ch.
Chapter 1Introduction3
Page 22
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
1.3.3Typographic Notation
This guide uses the following typographic notations for certain types of information:
This type of textIdentifies
italicPlaceholders that represent information you must provide. Italicized text is also used
for the titles of publications and for emphasis.
monowidthProgram statements and function names.
1.4Important New Terms
This section defines several important terms and concepts used in this guide.
1.4.1Multi-Core Processors
The AMD Family 10h family of processors have multiple cores. A multi-core processor contains two
to four identical cores that share the processor’s L3 cache and Northbridge (see Appendix A). Within
a processor, each core can simultaneously run independent threads.
1.4.2Primitive Operations
AMD Family 10h processors perform four types of primitive operations:
•Integer (arithmetic or logic)
•Floating-point (arithmetic)
•Load
•Store
1.4.3Internal Instruction Formats
The AMD64 instruction set is complex. Instructions have variable-length encoding and many perform
multiple primitive operations. AMD
directly, but, instead, decode them internally into simpler fixed-length instructions called macro-ops.
Processor schedulers subsequently break down macro-ops into sequences of even simpler instructions
called micro-ops, each of which specifies a single primitive operation.
A macro-op is a fixed-length instruction that:
•Expresses, at most, one integer or floating-point operation and one load and/or store operation.
•Is the primary unit of work managed (that is, dispatched and retired) by the processor.
Family 10h processors do not execute these complex instructions
A micro-op is a fixed-length instruction that:
•Expresses one and only one of the primitive operations that the processor can perform (for
example, a load).
4IntroductionChapter 1
Page 23
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
•Is executed by the processor’s execution units.
Table 1 on page 5 summarizes the differences between AMD64 instructions, macro-ops, and microops.
Table 1.Instructions, Macro-ops and Micro-ops
ComparingAMD64 instructionsMacro-opsMicro-ops
ComplexityComplex
A single instruction may
specify one or more of
each of the following
operations:
• Integer or floating-point
• Load
•Store
Encoded lengthVariable (instructions are
different lengths)
Regularized
instruction fields
No (field locations and
definitions vary among
instructions)
Average
A single macro-op may
specify—at most—one
integer or floating-point
operation and one of the
following operations:
• Load
•Store
• Load and store to the
same address
Fixed (all macro-ops are
the same length)
Yes (field locations and
definitions are the same
for all macro-ops)
Simple
A single micro-op
specifies only one of the
following primitive
operations:
• Integer or floating-point
• Load
•Store
Fixed (all micro-ops are
the same length)
Yes (field locations and
definitions are the same
for all micro-ops)
1.4.4Types of Instructions
Instructions are classified according to how they are decoded by the processor. There are three types
of instructions:
Instruction TypeDescription
DirectPath SingleDecodes directly into one macro-op in microprocessor hardware.
DirectPath DoubleDecodes directly into two macro-ops in microprocessor hardware.
VectorPathDecodes into one or more (usually three or more) macro-ops using the on-chip
microcode-engine ROM (MROM).
Chapter 1Introduction5
Page 24
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
1.5Key Optimizations
While all of the optimizations in this guide help improve software performance, some of them have
more impact than others. Optimizations that offer the most improvement are called key optimizations.
This symbol appears next to the most important (key) optimizations.
1.5.1Implementation Guideline
Concentrate your efforts on implementing key optimizations before moving on to other optimizations.
Table 2 lists the key optimizations. These optimizations are discussed in detail in later sections of this
book.
Table 2.Optimizations by Rank
RankOptimization
1Load-Execute Instructions for Floating-Point or Integer Operands (See
section 4.2 on page 53.)
2Write-Combining (See section 5.6 on page 89.)
3Branches That Depend on Random Data (See section 6.3 on page 101.)
4Loop Unrolling (See section 7.2 on page 110.)
5Pointer Arithmetic in Loops (See section 7.6 on page 116.)
6Explicit Load Instructions (See section 9.2 on page 146.)
7Reuse of Dead Registers (See section 9.15 on page 165.)
8ccNUMA Optimizations (See section 11.1 on page 179.)
9Multithreading (See section 11.3 on page 190.)
10Prefetch and Streaming Instructions (See section 5.5 on page 81.)
11Memory and String Routines (See section 5.9 on page 92.)
12Loop Iteration Boundaries (See section 4.3 on page 56.)
13Floating-Point Scalar Conversions (See sections 9.16 on page 166.)
1.6What’s New on AMD Family 10h Processors
AMD Family 10h processors introduce several new features that can significantly enhance software
performance when compared to the previous AMD64 microprocessors. The following section
provides compiler/assembler designers and C/C++/assembly language programmers with a summary
of these performance improvements. Throughout this discussion, it is assumed that readers are
familiar with the software optimization guide for the previous AMD64 processors and the
terminology used there.
6IntroductionChapter 1
Page 25
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
1.6.1AMD Instruction Set Enhancements
The AMD Family 10h processor has been enhanced with the following new instructions:
•LZCNT, POPCNT—Advanced Bit Manipulation (ABM) instructions operate on general purpose
registers.
•MOVNTSS, MOVNTSD, EXTRQ, INSERTQ—SSE4a instructions operate on XMM registers.
Support for these instructions is implementation dependent. See the CPUID Specification, order#
25481, and the AMD64 Architecture Programmer’s Manual Updates Application Note, order# 33633,
for additional information.
1.6.2Floating-Point Improvements
Previous AMD64 processors supported 64-bit floating-point execution units. The new
AMD Family 10h processors add support for 128-bit floating-point execution units. As a result, the
throughput of both single-precision and double-precision floating-point SSEx vector operations has
improved by 2X over the previous generation of AMD processors.
Performance Guidelines for Vectorized Floating-Point SSEx Code
While 128-bit floating-point execution units imply better performance for vectorized floating-point
SSEx code, it is necessary to adhere to several performance guidelines to realize their full potential:
•Avoid writing less than 128 bits of an XMM register when using certain initializing and noninitializing operations.
A floating-point XMM register is viewed as one 128-bit register internally by the processor.
Writing to a 64-bit half of a 128-bit XMM register results in a merge dependency on the other 64bit half. Therefore the following replacements are advised on AMD Family 10h processors:
–Replace MOVLPX/MOVHPX reg, mem pairs with MOVUPX reg, mem, irrespective of the
alignment of the data. On AMD
Family 10h processors, the MOVUPX instruction is just as
efficient as MOVAPX for when data is aligned. Hence it is advised to use MOVUPX
regardless of the alignment.
– Replace MOVLPD reg, mem with MOVSD reg, mem.
– Replace MOVSD reg, reg with MOVAPD reg, reg.
However, there are also several instructions that initialize the lower 64 or 32 bits of an XMM
register and zero out the upper 64 or 96 bits and, thus, do not suffer from such merge
dependencies. Consider, for example, the following instructions:
MOVSD xmm, [mem64]
MOVSS xmm, [mem32]
When writing to a register during the course of a non-initializing operation on the register, there is
usually no additional performance loss due to partial register reads and writes. This is because in
Chapter 1Introduction7
Page 26
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
the typical case, the partial register that is being written is also a source to the operation. For
example, addsd xmm1, xmm2 does not suffer from merge dependencies.
There are often cases of non-initializing operations on a register, in which the partial register
being written by the operation is not a source for the operation. In these cases also, it is preferable
to avoid partial register writes. If it is not possible to avoid writing to a part of that register, then
you should schedule any prior operation on any part of that register well ahead of the point where
the partial write occurs.
Examples of non-initializing instructions that result in merge dependencies are SQRTSD,
CVTPI2PS, CVTSI2SD, CVTSS2SD, MOVLHPS, MOVHLPS, UNPCKLPD and
PUNPCKLQDQ.
For additional details on this optimization see “Partial-Register Writes” on page 60, “Explicit
Load Instructions” on page 146, “Unaligned and Aligned Data Access” on page 147, and “Reuse
of Dead Registers” on page 165.
•In the event of a load following a previous store to a given address for aligned floating-point
vector data, use 128-bit stores and 128-bit loads instead of MOVLPX/MOVHPX pairs for storing
and loading the data. This allows store-to-load forwarding to occur. Using MOVLPX/MOVHPX
pairs is still recommended for storing unaligned floating-point vector data. Additional details on
these restrictions can be obtained in “Store-to-Load Forwarding Restrictions” on page 74.
•To make use of the doubled throughput of both single-precision and double-precision floatingpoint SSEx vector operations, a compiler or an application developer can consider either
increasing the unrolling factor of loops that include such vector operations and/or performing
other code transformations to keep the floating-point pipeline fully utilized.
1.6.3Load-Execute Instructions for Unaligned Data
Use load-execute instructions instead of discrete load and execute instructions when performing SSE
floating-point/SSE integer/x87 computations on floating-point source operands. This is
recommended regardless of the alignment of packed data on AMD
Family 10h processors. (The use
of load-execute instructions under these circumstances was only recommended for aligned packed
data on the previous AMD64 processors.) This replacement is only possible if the misaligned
exception mask (MM) is set. See the AMD CPUID Specification, order# 25481, and the AMD64 Architecture Programmer’s Manual Updates Application Note, order# 33633,for additional
information on SSE misaligned access support. This optimization can be especially useful in
vectorized SSEx loops and may eliminate the need for loop peeling due to nonalignment. (See “LoadExecute Instructions for Floating-Point or Integer Operands” on page 53.)
1.6.4Instruction Fetching Improvements
The fetch window has changed from 16 bytes on previous AMD64 processors to 32 bytes on
AMD Family 10h processors. The 32-byte fetch, when combined with the 128-bit floating-point
execution unit, allows the processor to sustain a fetch/dispatch/retire sequence of three large
instructions per cycle.
8IntroductionChapter 1
Page 27
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Assembly language programmers can now group large instructions together without worrying about
an instruction possibly spanning the fetch window. In this regard, it is also advisable to align hot loops
to 32 bytes instead of 16 bytes, especially in the case of loops for large SSE instructions.
For additional details, readers can refer to “Loop Iteration Boundaries” on page 56.
1.6.5Instruction Decode and Floating-Point Pipe Improvements
Several integer and floating-point instructions have improved latencies and decode types on
AMD Family 10h processors. Furthermore, the FPU pipes utilized by several floating-point
instructions have changed. These changes can influence instruction choice and scheduling for
compilers and hand-written assembly code. A comprehensive listing of all AMD64 instructions with
their decode types, decode type changes from previous families of AMD processors, and execution
latencies and FPU pipe utilization data are available in Appendix C.
1.6.6Notable Performance Improvements
Several enhancements to the AMD64 architecture have resulted in significant performance
improvements in AMD Family 10h processors, including:
•Improved performance of shuffle instructions
•Improved data transfer between floating-point registers and general purpose registers
•Improved floating-point register to floating-point register moves
•Optimization of repeated move instructions
•More efficient PUSH/POP stack operations
•1-Gbyte paging
These are discussed in the following paragraphs and elsewhere in this document.
Improved Bandwidth Decode Type for Shuffle Instructions
The floating-point logic in AMD Family 10h processors uses three separate execution positions or
pipes called FADD, FMUL and FSTORE. This is illustrated in
Figure 8 on page 222 in Appendix A.
Current AMD Family 10h processors support two SSE logical/shuffle units, one in the FMUL pipe
and another in the FADD pipe, while previous AMD64 processors have only one SSE logical/shuffle
unit in the FMUL pipe. As a result, the SSE/SSE2 shuffle instructions can be processed at twice the
previous bandwidth on AMD Family 10h processors. Furthermore, the PSHUFD and SHUFPx
shuffle instructions are now DirectPath instructions instead of VectorPath instructions on
AMD
Family 10h processors and take advantage of the 128-bit floating point execution units. Hence,
these instructions get a further 2X boost in bandwidth, resulting in an overall improvement of 4X in
bandwidth compared to the previous generation of AMD processors.
It’s more efficient to use SHUFPx and PSHUFD instructions over combinations of more than one
MOVLHPS/MOVHLPS/UNPCKx/PUNPCKx instructions to do shuffle operations.
Chapter 1Introduction9
Page 28
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Data Transfer Between Floating-Point Registers and General Purpose Integer
Registers
We recommend using the MOVD instruction when moving data from an MMX™ or XMM register to
a GPR. However, when moving data from a GPR to an MMX or XMM floating-point register, it is
advisable to use separate store and load instructions to move the data from the source register to a
temporary location in memory and then from memory into the destination register, taking the memory
latency into account when scheduling them.
The performance of the CVTSS2SI, CVTTSS2SI, CVTSD2SI, CVTTSD2SI instructions that are
used to convert floating-point data to integer data has improved. For additional details see “FloatingPoint-to-Integer Conversion” on page 165.
Floating-Point Register-to-Register Moves
On previous AMD processors, floating-point register-to-register moves could only go through the
FADD and FMUL pipes. On AMD Family 10h processors, floating-point register-to-register moves
can also go through the FSTORE pipe, thereby improving overall throughput.
Repeated String Instructions
REP instructions have been optimized on AMD Family 10h processors. See “Repeated String
Instructions” on page 126 for details on how to take advantage of these optimizations.
Faster PUSH/POP with the Sideband Stack Optimizer
AMD Family 10h processors have added a sideband stack optimizer (SSO). This special circuitry
removes the dependency that arises during chains of PUSH and POP operations on the rSP register
and, thereby, improves the efficiency of the PUSH and POP instructions.
The SSO also improves the performance of CALL and RET instructions, among others. (See “Stack
Operations” on page 59.)
10IntroductionChapter 1
Page 29
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
1.6.7Large Page Support
AMD Family 10h processors now have better large page support, having incorporated new 1GB
paging and 2MB and 4KB paging improvements.
The L1 data TLB and L2 data TLB now support 1GB pages, a benefit to applications making large
data-set random accesses.
The L1 instruction TLB, L1 data TLB and L2 data TLB have increased the number of entries for
2MB pages. This improves the performance of software that uses 2MB code or data or code mixed
with data virtual pages.
The L1 data TLB has also increased the number of entries for 4KB pages.
For additional details on the actual number of TLB entries, see section A.10, “Translation-Lookaside
Buffer” on page 225.
For more information on 1-Gbyte pages see “Using 1-Gbyte Virtual Memory Pages” on page 96.
1.6.8AMD Virtualization™ Optimizations
Chapter 12, “Optimizing Secure Virtual Machines” covers optimizations that minimize the
performance overhead imposed by the virtualization of a guest in AMD Virtualization™ technology
(AMD-V™). Topics include:
•The advantages of using nested paging instead of shadow paging
•Guest page attribute table (PAT) configuration
•State swapping
•Economizing Interceptions
•Nested page and shadow page size
•TLB control and flushing in shadow pages
•Instruction Fetch for Intercepted (REP) INS instructions
•Sharing IOIO and MSR protection masks
•CPUID
•Time resources
•Paravirtualized resources
Chapter 1Introduction11
Page 30
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
12IntroductionChapter 1
Page 31
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Chapter 2C and C++ Source-Level
Optimizations
Although C and C++ compilers can often produce very efficient object code from naive source code,
careful attention to coding details can lead to even better object code and therefore to improved
performance. Many optimizations take advantage of the underlying mechanisms used by C and C++
compilers to translate source code into sequences of AMD64 instructions. This chapter includes
guidelines for writing C and C++ source code that yields an approximation to the most highly
efficient optimization.
This chapter covers the following topics:
TopicPage
Declarations of Floating-Point Values14
Using Arrays and Pointers14
Unrolling Small Loops17
Arrange Boolean Operands for Quick Expression Evaluation18
Expression Order in Compound Branch Conditions19
Long Logical Expressions in If Statements20
Dynamic Memory Allocation Consideration21
Unnecessary Store-to-Load Dependencies21
Matching Store and Load Size23
Use of Function Prototypes25
Use of const Type Qualifier26
Generic Loop Hoisting26
Local Static Functions28
Explicit Parallelism in Code29
Extracting Common Subexpressions30
Sorting and Padding C and C++ Structures31
Replacing Integer Division with Multiplication32
Frequently Dereferenced Pointer Arguments33
32-Bit Integral Data Types34
Sign of Integer Operands35
Accelerating Floating-Point Division and Square Root36
Speeding Up Branches Based on Comparisons Between Floats38
Improving Performance in Linux
Aligning Matrices41
®
Libraries
40
Chapter 2C and C++ Source-Level Optimizations13
Page 32
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
2.1Declarations of Floating-Point Values
Optimization
When working with single precision (float) values:
•Use the f or F suffix (for example, 3.14f) to specify a constant value of type float.
•Use function prototypes for all functions that accept arguments of type float.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
C and C++ compilers treat floating-point constants and arguments as double precision (double)
unless you specify otherwise. However, single precision floating-point values occupy half the
memory space as double precision values and can often provide the precision necessary for a given
computational problem.
This optimization also results in more efficient use of the XMM Streaming SIMD registers: four
single precision values can be packed into a single XMM register, compared to two double precision
values.
2.2Using Arrays and Pointers
Optimization
Use array notation instead of pointer notation when working with arrays.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
C allows the use of either the array operator ([]) or pointers to access the elements of an array.
However, the use of pointers in C makes work difficult for optimizers in C compilers. Without
14C and C++ Source-Level OptimizationsChapter 2
Page 33
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
detailed and aggressive pointer analysis, the compiler has to assume that writes through a pointer can
write to any location in memory, including storage allocated to other variables. (For example, *p and
*q can refer to the same memory location, while x[0] and x[2] cannot.) Pointers make it difficult for
compilers to detect the presence or absence of aliasing—with possible ambiguous access to a block of
memory. The compiler sometimes must assume aliasing in the presence of pointers, which limits the
opportunities for optimization. Array notation makes the task of the optimizer easier by reducing
possible aliasing.
Example
Avoid code, such as the following, which uses pointer notation:
Source-code transformations interact with a compiler’s code generator, making it difficult to control
the generated machine code from the source level. It is even possible that source-code transformations
aimed at improving performance may conflict with compiler optimizations. Depending on the
compiler and the specific source code, it is possible for pointer-style code to compile into machine
code that is faster than that generated from equivalent array-style code. Compare the performance of
your code after implementing a source-code transformation with the performance of the original code
to be sure that there is an improvement.
Some compilers provide proprietary declaration keywords that further allow the compiler to reduce
possible aliasing. See Compiler Usage Guidelines for 64-Bit Operating Systems on AMD64 Platforms Application Note, order# 32035, for details.
16C and C++ Source-Level OptimizationsChapter 2
Page 35
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
2.3Unrolling Small Loops
Optimization
Completely unroll loops that have a small fixed loop count and a small loop body.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Many compilers do not aggressively unroll loops. Manually unrolling loops can benefit performance,
especially if the loop body is small, making the loop overhead significant.
Unrolling loops increases the code size, which may decrease performance in rare cases.
Example
Avoid a small loop like this:
// 3D-transform: Multiply vector V by 4x4 transform matrix M.
for (i = 0; i < 4; i++) {
r[i] = 0;
for (j = 0; j < 4; j++) {
r[i] += m[j][i] * v[j];
}
}
Instead, replace it with its completely unrolled equivalent, as shown here:
For information on loop unrolling at the assembly-language level, see “Loop Unrolling” on page 110.
Chapter 2C and C++ Source-Level Optimizations17
Page 36
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
2.4Arrange Boolean Operands for Quick Expression
Evaluation
Optimization
In expressions that use the logical AND (&&) or logical OR (||) operator, arrange the operands for
quick evaluation of the expression:
If the expression uses this
operator
&& (logical AND)False
|| (logical OR)True
Then arrange the operands from left to right in decreasing
probability of being
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
C and C++ compilers guarantee short-circuit evaluation of the boolean operators && and ||. In an
expression that uses &&, the first operand to evaluate to false terminates the evaluation; subsequent
operands are not evaluated. In an expression that uses ||, the first operand to evaluate to true
terminates the evaluation.
When used to control program flow, expressions involving && and || are translated into a series of
conditional branches. This optimization minimizes the total number of conditions evaluated and
branches executed.
Example 1
In the following code, the operands of && are not arranged for quick expression evaluation because the
first operand is not the condition case most likely to be false (it is far less likely for an animal name to
begin with a ‘y’ than for it to have fewer than four characters):
char animalname[30];
char *p;
p = animalname;
if ((strlen(p) > 4) && (*p == 'y')) { ... }
Because the odds that the animal name begins with a ‘y’ are comparatively low, it is better to put that
operand first:
18C and C++ Source-Level OptimizationsChapter 2
Page 37
40546 Rev. 3.09 November 2008
if ((*p == 'y') && (strlen(p) > 4)) { ... }
Software Optimization Guide for AMD Family 10h Processors
Example 2
In the following code (assuming a uniform random distribution of i), the operands of || are not
arranged for quick expression evaluation because the first operand is not the condition most likely to
be true:
unsigned int i;
if ((i < 4) || (i & 1)) { ... }
Because it is more likely for the least-significant bit of i to be 1, it is better to put that operand first:
if ((i & 1) || (i < 4)) { ... }
2.5Expression Order in Compound Branch
Conditions
Optimization
In the most active areas of a program, order the expressions in compound branch conditions to take
advantage of short circuiting of compound conditional expressions.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Branch conditions in C programs often consist of compound conditions consisting of multiple
boolean expressions joined by the logical AND (&&) or logical OR (||) operators. C compilers
guarantee short-circuit evaluation of these operators. In a compound logical OR expression, the first
operand to evaluate to true terminates the evaluation, and subsequent operands are not evaluated at all.
Similarly, in a logical AND expression, the first operand to evaluate to false terminates the evaluation.
Hence, it is not always possible to swap the operands of logical OR and logical AND. This is
especially true when the evaluation of one of the operands causes a side effect. In most cases the order
of operands in such expressions is irrelevant.
When used to control conditional branches, expressions involving logical OR or logical AND are
translated into a series of conditional branches. The ordering of the conditional branches is a function
of the ordering of the expressions in the compound condition and can have a significant impact on
Chapter 2C and C++ Source-Level Optimizations19
Page 38
Software Optimization Guide for AMD Family 10h Processors
performance. It is impossible to give an easy, closed-form formula on how to order the conditions.
Overall performance is a function of the following factors:
•Probability of a branch misprediction for each of the branches generated
•Additional latency incurred due to a branch misprediction
•Cost of evaluating the conditions controlling each of the branches generated
•Amount of parallelism that can be extracted in evaluating the branch conditions
•Data stream consumed by an application (mostly due to the dependence of misprediction
probabilities on the nature of the incoming data in data-dependent branches)
It is recommended to experiment with the ordering of expressions in compound branch conditions in
the most active areas of a program (“hot spots,” consuming a great amount of execution time). Such
hot spots can be found through the use of profiling by feeding a typical data stream to the program
while doing the experiments.
40546 Rev. 3.09 November 2008
2.6Long Logical Expressions in If Statements
Optimization
In if statements, avoid long logical expressions that can generate dense conditional branches that
violate the guideline described in “Branch Alignment and Density” on page 99. When long logical
expressions are unavoidable, try to arrange them so that most of the implicit branches are not be
taken.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
High branch density can lead to some branches not being identified by the branch predictor (as
described in section “Branch Alignment and Density” on page 99). If these unpredicted branches are
not taken, they will not cause a misprediction penalty.
Preferred for Data that Falls Mostly Within the Range
if (a <= max && a >= min && b <= max && b >= min)
If most of the data falls within the range, the branches will not be taken, so the above code is
preferred. Otherwise, the following code is preferred.
20C and C++ Source-Level OptimizationsChapter 2
Page 39
40546 Rev. 3.09 November 2008
Preferred for Data that Does Not Fall Mostly Within the Range
if (a > max || a < min || b > max || b < min)
Software Optimization Guide for AMD Family 10h Processors
2.7Dynamic Memory Allocation Consideration
Optimization
Where this aligned pointer cannot be guaranteed, use the technique shown in the following code to
make the pointer 16-byte aligned, if needed.
Application
This optimization applies to:
•32-bit software
Examples
Dynamic memory allocation—accomplished through the use of the malloc library function in C—
should always return a pointer that is suitably aligned for the largest base type (16-byte alignment).
However, this may not always be the case. In this example, after memory allocation, use np instead of
p to access the data. The pointer p is still needed in order to deallocate the storage later.
A store-to-load dependency exists when data is stored to memory, only to be read back shortly
thereafter. For details, see
Chapter 2C and C++ Source-Level Optimizations21
“Store-to-Load Forwarding Restrictions” on page 74. The
Page 40
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
AMD Family 10h processor contains hardware to accelerate such store-to-load dependencies,
allowing the load to obtain the store data before it has been written to memory. However, avoiding
such dependencies and keeping the data in an internal register results in faster code.
It is especially important to avoid store-to-load dependencies if they are part of a long dependency
chain, as may occur in a recurrence computation. If the dependency occurs while operating on arrays,
many compilers are unable to optimize the code in a way that avoids the store-to-load dependency. In
some instances the language definition may prohibit the compiler from using code transformations
that would remove the store-to-load dependency. Therefore, it is recommended that the programmer
remove the dependency manually, for example, by introducing a temporary variable that can be kept
in a register. This can result in a significant performance increase.
Examples
Avoid
double x[VECLEN], y[VECLEN], z[VECLEN];
unsigned int k;
for (k = 1; k < VECLEN; k++) {
x[k] = x[k-1] + y[k];
}
for (k = 1; k < VECLEN; k++) {
x[k] = z[k] * (y[k] - x[k-1]);
}
Preferred
double x[VECLEN], y[VECLEN], z[VECLEN];
unsigned int k;
double t;
t = x[0];
for (k = 1; k < VECLEN; k++) {
t = t + y[k];
x[k] = t;
}
t = x[0];
for (k = 1; k < VECLEN; k++) {
t = z[k] * (y[k] - t);
x[k] = t;
}
22C and C++ Source-Level OptimizationsChapter 2
Page 41
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
2.9Matching Store and Load Size
Optimization
Align memory accesses and match addresses and sizes of stores and dependent loads.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
The AMD Family 10h processor contains a load-store buffer to speed up the forwarding of store data
to dependent loads. However, this store-to-load forwarding (STLF) inside the load-store buffer
occurs, in general, only when the addresses and sizes of the store and the dependent load match, and
when both memory accesses are aligned. For details, see “Store-to-Load Forwarding Restrictions” on
page 74.
It is impossible to control load and store activity at the source level in such a way as to avoid all cases
that violate restrictions placed on store-to-load-forwarding. In some instances it is possible to spot
such cases in the source code. Size mismatches can easily occur when different-size data items are
joined in a union. Address mismatches could be the result of pointer manipulation.
The following examples show a situation involving a union of different-size data items. The examples
show a user-defined unsigned 16.16 fixed-point type and two operations defined on this type.
Function fixed_add adds two fixed-point numbers, and function fixed_int extracts the integer
portion of a fixed-point number.
Listing shows an inappropriate implementation of fixed_int,
which, when used on the result of fixed_add, causes misalignment, address mismatch, or size
mismatch between memory operands, such that no store-to-load forwarding in the load-store buffer
takes place. The following examples shows how to properly implement
fixed_int in order to allow
store-to-load forwarding in the load-store buffer.
Examples
Avoid
typedef union {
unsigned int whole;
struct {
unsigned short frac; /* Lower 16 bits are fraction. */
unsigned short intg; /* Upper 16 bits are integer. */
} parts;
} FIXED_U_16_16;
Chapter 2C and C++ Source-Level Optimizations23
Page 42
Software Optimization Guide for AMD Family 10h Processors
Some more sophisticated compilers may generate optimal machine code even for the previous
example. These compilers provide various optional levels and types of optimizations that are
controlled by compiler program flags. When compiled at a moderate level of optimization, such
compilers may generate perfectly acceptable code from C++ code such as that listed above. For more
information, see Compiler Usage Guidelines for 64-Bit Operating Systems on AMD64 Platforms
Application Note, order# 32035.
Preferred
typedef union {
unsigned int whole;
struct {
unsigned short frac; /* Lower 16 bits are fraction. */
unsigned short intg; /* Upper 16 bits are integer. */
Prototypes can convey additional information to the compiler that might enable more aggressive
optimizations.
Chapter 2C and C++ Source-Level Optimizations25
Page 44
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
2.11Use of const Type Qualifier
Optimization
For objects whose values will not be changed, use the const type qualifier.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Using the const type qualifier makes code more robust and may enable the compiler to generate
higher-performance code. For example, under the C standard, a compiler is not required to allocate
storage for an object that is declared const, if its address is never used.
2.12Generic Loop Hoisting
Optimization
To improve the performance of inner loops, reduce redundant constant calculations (that is, loopinvariant calculations). This idea can also be extended to invariant control structures.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale and Examples
The following example demonstrates the use of an invariant condition in an if statement in a for
loop. The second listing shows the preferred optimization.
Avoid
for (i...) {
if (CONSTANT0) {
DoWork0(i); // Does not affect CONSTANT0.
}
else {
26C and C++ Source-Level OptimizationsChapter 2
Page 45
40546 Rev. 3.09 November 2008
DoWork1(i); // Does not affect CONSTANT0.
}
}
Software Optimization Guide for AMD Family 10h Processors
Preferred
if (CONSTANT0) {
for (i...) {
DoWork0(i);
}
}
else {
for (i...) {
DoWork1(i);
}
}
The preferred optimization in the preceding example tightens the inner loops by avoiding repetitious
evaluation of a known if control structure. Although the branch would be easily predicted, the extra
instructions and decode limitations imposed by branching are eliminated.
To generalize the preceding example further for multiple-constant control code, more work may be
needed to create the proper outer loop. Enumeration of the constant cases reduces this to a simple
switch statement.
Avoid
for (i...) {
if (CONSTANT0) {
DoWork0(i); // Does not affect CONSTANT0 or CONSTANT1.
}
else {
DoWork1(i); // Does not affect CONSTANT0 or CONSTANT1.
}
if (CONSTANT1) {
DoWork2(i); // Does not affect CONSTANT0 or CONSTANT1.
}
else {
DoWork3(i); // Does not affect CONSTANT0 or CONSTANT1.
}
}
Transform the loop in the preceding example (by using the switch statement) into:
Some introductory code is necessary to generate all the combinations for the switch constant and the
total amount of code has doubled. However, the inner loops are now free of if statements. In ideal
cases where the DoWorkn functions are inlined, the successive functions have greater overlap, leading
to greater parallelism than possible in the presence of intervening if statements.
The same idea can be applied to constant switch statements or to combinations of switch statements
and if statements inside of for loops. The method used to combine the input constants becomes
more complicated but benefits performance.
However, the number of inner loops can also substantially increase. If the number of inner loops is
prohibitively high, then only the most common cases must be dealt with directly, and the remaining
cases can fall back to the old code in the default clause of the switch statement. This situation is
typical of run-time generated code. While the performance of run-time generated code can be
improved by means similar to those presented here, it is much harder to maintain and developers must
do their own code-generation optimizations without the help of an available compiler.
2.13Local Static Functions
Optimization
Declare as static functions that are not used outside the file where they are defined.
Application
This optimization applies to:
•32-bit software
28C and C++ Source-Level OptimizationsChapter 2
Page 47
40546 Rev. 3.09 November 2008
•64-bit software
Software Optimization Guide for AMD Family 10h Processors
Rationale
Declaring a function as static forces internal linkage. Functions that are not declared as static
default to external linkage, which may inhibit certain optimizations—for example, aggressive
inlining—with some compilers. In C++, programmers can declare functions inside an anonymous
namespace to achieve the same local scoping effect.
2.14Explicit Parallelism in Code
Optimization
Where possible, break long dependency chains into several independent chains that can be executed
in parallel to take advantage of the execution units in each pipeline.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
It is especially important to break long x87, SSE, or SSE2 dependency chains into smaller executing
units in floating-point code, because of the longer latency of floating-point operations. Most
languages (including ANSI C) are bound by the guarantee that floating-point expressions can not be
reordered; compilers cannot usually perform such optimizations unless they offer a switch to allow
noncompliant reordering of floating-point expressions according to algebraic rules.
Reordered code that is algebraically identical to the original code does not necessarily produce
identical computational results due to the lack of associativity of floating-point operations. There are
well-known numerical considerations in applying these optimizations (consult a book on numerical
analysis). In some cases, reordered floating-point code may lead to unexpected results, but in the vast
majority of cases, the final result differs only in the least-significant bits.
Examples
Avoid
double a[100], sum;
int i;
sum = 0.0f;
Chapter 2C and C++ Source-Level Optimizations29
Page 48
Software Optimization Guide for AMD Family 10h Processors
for (i = 0; i < 100; i++) {
sum += a[i];
}
40546 Rev. 3.09 November 2008
Preferred
double a[100], sum1, sum2, sum3, sum4, sum;
int i;
sum1 = 0.0;
sum2 = 0.0;
sum3 = 0.0;
sum4 = 0.0;
for (i = 0; i < 100; i + 4) {
sum1 += a[i];
sum2 += a[i+1];
sum3 += a[i+2];
sum4 += a[i+3];
}
sum = (sum4 + sum3) + (sum1 + sum2);
Notice that the four-way unrolling is chosen to exploit the four-stage fully pipelined floating-point
adder. Each stage of the floating-point adder is occupied on every clock cycle, ensuring maximum
sustained utilization.
2.15Extracting Common Subexpressions
Optimization
Manually extract common subexpressions from floating-point expressions, where C compilers may
be unable to extract them due to the rules against reordering of floating-point expressions in the ANSI
standard.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Specifically, the compiler cannot rearrange the computation according to algebraic equivalencies
before extracting common subexpressions. Rearranging the expression may give different
computational results due to the lack of associativity of floating-point operations, but the results
usually differ in only the least-significant bits. However, since errors in the least significant bits can
be magnified by later operations to the extent that they completely invalidate the calculation, the
programmer should proceed with caution when implementing this sort of computation.
30C and C++ Source-Level OptimizationsChapter 2
Page 49
40546 Rev. 3.09 November 2008
Examples
Avoid
double a, b, c, d, e, f;
e = b * c / d;
f = b / d * a;
Preferred
double a, b, c, d, e, f, t;
t = b / d;
e = c * t;
f = a * t;
Avoid
double a, b, c, e, f;
Software Optimization Guide for AMD Family 10h Processors
e = a / c;
f = b / c;
Preferred
double a, b, c, e, f, t;
t = 1 / c;
e = a * t
f = b * t;
2.16Sorting and Padding C and C++ Structures
Optimization
Sort and pad C and C++ structures to achieve natural alignment.
Application
This optimization applies to:
•32-bit software
•64-bit software
Chapter 2C and C++ Source-Level Optimizations31
Page 50
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Rationale
In order to achieve better alignment for structures, many compilers have options that allow padding of
structures to make their sizes multiples of words, doublewords, or quadwords. In addition, to improve
the alignment of structure members, some compilers may allocate structure elements in an order that
differs from the order in which they are declared. Unfortunately, some compilers may not offer any of
these features, or their implementations might not work properly in all situations.
By sorting and padding structures at the source-code level, if the first member of a structure is
naturally aligned, then all other members are naturally aligned as well. This allows, for example,
arrays of structures to be perfectly aligned.
Sorting and Padding C and C++ Structures
To sort and pad a C or C++ structure, follow these steps:
1. Sort the structure members according to their type sizes, declaring members with larger type sizes
ahead of members with smaller type sizes.
2. Pad the structure so the size of the structure is a multiple of the largest member’s type size.
Examples
Avoid structure declarations in which the members are not declared in order of their type sizes and the
size of the structure is not a multiple of the size of the largest member’s type:
struct {
char a[5]; \\ Smallest type size (1 byte * 5)
long k; \\ 4 bytes in this example
double x; \\ Largest type size (8 bytes)
} baz;
Instead, declare the members according to their type sizes (largest to smallest) and add padding to
ensure that the size of the structure is a multiple of the largest member’s type size:
struct {
double x; \\ Largest type size (8 bytes)
long k; \\ 4 bytes in this example
char a[5]; \\ Smallest type size (1 byte * 5)
char pad[7]; \\ Make structure size a multiple of 8.
} baz;
2.17Replacing Integer Division with Multiplication
Optimization
Replace integer division with multiplication when there are multiple divisions in an expression. (This
is possible only if no overflow will occur during the computation of the product. The possibility of an
overflow can be determined by considering the possible ranges of the divisors.)
32C and C++ Source-Level OptimizationsChapter 2
Page 51
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Integer division is the slowest of all integer arithmetic operations.
Examples
Avoid code that uses two integer divisions:
int i, j, k, m;
m = i / j / k;
Instead, replace one of the integer divisions with the appropriate multiplication:
m = i / (j * k);
2.18Frequently Dereferenced Pointer Arguments
Optimization
Avoid dereferenced pointer arguments inside a function.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Because the compiler has no knowledge of aliasing between pointers, such dereferencing cannot be
“optimized away.” Since data may not be maintained in registers, memory traffic can significantly
increase.
Many compilers have an “assume no aliasing” optimization switch. This allows the compiler to
assume that two different pointers always have disjoint contents and does not require copying of
pointer arguments to local variables. If your compiler does not have this type of optimization, then
copy the data referenced by the pointer arguments to local variables at the start of the function and if
necessary copy them back at the end of the function. (Some compilers also provide keywords to
Chapter 2C and C++ Source-Level Optimizations33
Page 52
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
provide the same aliasing information to the compiler. For details, see Compiler Usage Guidelines for 64-Bit Operating Systems on AMD64 Platforms Application Note, order# 32035.)
Examples
Avoid
// Assumes pointers are different and q != r.
void isqrt(unsigned long a, unsigned long *q, unsigned long *r) {
*q = a;
if (a > 0) {
while (*q > (*r = a / *q)) {
*q = (*q + *r) >> 1;
}
}
*r = a - *q * *q;
}
Preferred
// Assumes pointers are different and q != r.
void isqrt(unsigned long a, unsigned long *q, unsigned long *r) {
unsigned long qq, rr;
qq = a;
if (a > 0) {
while (qq > (rr = a / qq)) {
qq = (qq + rr) >> 1;
}
}
rr = a - qq * qq;
*q = qq;
*r = rr;
}
2.1932-Bit Integral Data Types
Optimization
Use 32-bit integers instead of smaller sized integers (16-bit or 8-bit).
Application
This optimization applies to:
•32-bit software
•64-bit software
34C and C++ Source-Level OptimizationsChapter 2
Page 53
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Rationale
When choosing between 32-bit, 16-bit and 8-bit data types in cases where memory footprint is not a
concern, using 32-bit integer types in 32-bit software (32-bit or 64-bit integer types in 64-bit software) avoids possible register-merging false dependencies due to partial register writes. See
section 4.8, "Partial-Register Writes" on page 60 for details.
2.20Sign of Integer Operands
Optimization
Where there is a choice of using either a signed or an unsigned type, take into consideration that some
operations are faster with unsigned types while others are faster for signed types.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
In many cases, the type of data to be stored in an integer variable determines whether a signed or an
unsigned integer type is appropriate. For example, to record the weight of a person in pounds, no
negative numbers are required, so an unsigned type is appropriate. However, recording temperatures
in degrees Celsius may require both positive and negative numbers, so a signed type is needed.
Integer-to-floating-point conversion using integers larger than 16 bits is faster with signed types, as
the AMD64 architecture provides instructions for converting signed integers to floating-point but has
no instructions for converting unsigned integers. In a typical case, a 32-bit integer is converted by a
compiler to assembly as follows:
Examples
Avoid
double x; ====> mov [temp+4], 0
unsigned int i; mov eax, i
mov [temp], eax
x = i; fild QWORD PTR [temp]
fstp QWORD PTR [x]
The preceding code is slow not only because of the number of instructions, but also because a size
mismatch prevents store-to-load forwarding to the FILD instruction. Instead, use the following code:
Chapter 2C and C++ Source-Level Optimizations35
Page 54
Software Optimization Guide for AMD Family 10h Processors
Computing quotients and remainders in integer division by constants is faster when performed on
unsigned types. The following typical case is the compiler output for a 32-bit integer divided by 4:
Avoid
int i; ====> mov eax, i
cdq
i = i / 4; and edx, 3
add eax, edx
sar eax, 2
mov i, eax
Preferred
unsigned int i; ====> shr i, 2
i = i / 4;
In summary, use unsigned types for:
•Division and remainders
•Loop counters
•Array indexing
Use signed types for:
•Integer-to-floating-point conversion
2.21Accelerating Floating-Point Division and Square
Root
Optimization
In applications employing the heavy use of single precision division and square root operations, in
which the compiler maps floating-point operations to x87 instructions, the x87 FPU control word
register precision control specification bits (PC) can be set to single precision to improve
performance. (The processor defaults to double-extended precision. See AMD64 Architecture Programmer’s Manual, Volume 1, order# 24592, for details on the FPU control register.)
36C and C++ Source-Level OptimizationsChapter 2
Page 55
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Note: For hotspots that can be recoded in assembly language or SSE intrinsics, refer to section
9.7“Using SIMD Instructions for Fast Square Roots and Divisions” on page 150 for coding
suggestions.
Application
This optimization applies to any compiler that maps floating-point operations to x87 instructions.
This is generally true only for 32-bit compilers.
Rationale
Division and square root have a much longer latency than other floating-point operations, even though
the AMD Family 10h processors provide significant acceleration of these two operations. In some
application programs, these operations occur so often that they seriously impair performance. If the
code has hot spots that use single precision arithmetic only (that is, all computation involves data of
type float) and for some reason cannot be ported to SSE, SSE2, or SSE3 code, the following
technique may be used to improve performance.
The x87 FPU has a precision-control field as part of the FPU control word. The precision-control
setting determines rounding precision of instruction results and affects the basic arithmetic
operations, including division and the extraction of square root. Division and square root on the
AMD Family 10h processors are only computed to the number of bits necessary for the currently
selected precision. Setting precision control to single precision (versus the default of double-extended
precision) lowers the latency of those operations.
For example, the 32-bit version Microsoft® Visual C environment provides functions to manipulate
the FPU control word and, thus, the precision control bits. Note that these functions are not very fast,
so insert changes of precision control where doing so creates little overhead, such as outside of
computation-intensive loops. Otherwise, the overhead created by the function calls outweighs the
benefit of reducing the latencies of divide and square-root operations. The following example shows
how to set the precision control to single precision and later restore the original settings in the
Microsoft Visual C environment.
Examples
/* Prototype for _controlfp_s function */
#include <float.h>
unsigned int cw, orig_cw;
int err;
/* Get original FP control word and save it. */
err = _controlfp_s(&orig_cw, 0, 0);
if ( err ) /* handle error here */;
/* Set precision in FPU control word to single precision.
This reduces the latency of divide and square-root operations. */
Chapter 2C and C++ Source-Level Optimizations37
Page 56
Software Optimization Guide for AMD Family 10h Processors
err = _controlfp_s(&cw, _PC_24, MCW_PC);
if ( err ) /* handle error here */;
/* Restore original FPU control word */
err = _controlfp_s(&cw, orig_cw, MCW_PC);
if ( err ) /* handle error here */;
40546 Rev. 3.09 November 2008
2.22Speeding Up Branches Based on Comparisons
Between Floats
Optimization
Store operands of type float into a memory location and use integer comparison with the memory
location to perform fast branches in cases where compilers do not support fast floating-point
comparison instructions.
Application
This optimization applies to 32-bit software.
Rationale
Branches based on floating-point comparisons are often slow. AMD Family 10h processors support
the FCOMI, FUCOMI, FCOMIP, and FUCOMIP instructions that allow implementation of fast
branches based on comparisons between operands of type double or type float. However, some
compilers do not support generating these instructions.
Some compilers only implement branches based on floating-point comparisons by using FCOM or
FCOMP to compare the floating-point operands, followed by FSTSW AX in order to transfer the x87
condition-code flags into EAX. The subsequent branch is then based on the contents of the EAX
register. Although the AMD
FSTSW instruction, this process is still fairly slow.
Branches Dependent on Integer Comparisons Are Fast
One alternative for branches dependent upon the outcome of the comparison of operands of type
float is to store the operand(s) into a memory location and then perform an integer comparison with
that memory location. Branches dependent on integer comparisons are very fast. The replacement
code uses a load dependent on an immediately prior store. If the store is not doubleword-aligned, no
store-to-load-forwarding takes place, and the branch is still slow. Also, if there is a lot of activity in
the load-store queue, forwarding of the store data may be somewhat delayed, thus negating some of
the advantages of using the replacement code. It is recommended that you experiment with the
replacement code to test whether it actually provides a performance increase in the code at hand.
Family 10h processors have acceleration hardware to speed up the
38C and C++ Source-Level OptimizationsChapter 2
Page 57
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
The replacement code works well for comparisons against zero, including correct behavior when
encountering a negative zero as allowed by the IEEE-754 standard. It also works well for comparing
to positive constants. In that case, the user must first determine the integer representation of that
floating-point constant. This can be accomplished with the following C code snippet:
The replacement code is IEEE-754 compliant for all classes of floating-point operands except NaNs.
Examples
Initial definitions:
#define FLOAT2INTCAST(f) (*((int *)(&f)))
#define FLOAT2UINTCAST(f) (*((unsigned int *)(&f)))
Table 3.Comparisons Against Zero
Use this …Instead of this.
if (FLOAT2UINTCAST(f) > 0x80000000U)if (f < 0.0f)
if (FLOAT2INCAST(f) <= 0)if (f <= 0.0f)
if (FLOAT2INTCAST(f) > 0)if (f > 0.0f)
if (FLOAT2UINTCAST(f) <= 0x80000000U)if (f >= 0.0f)
Table 4.Comparisons Against Positive Constant
Use this …Instead of this.
if (FLOAT2INTCAST(f) < 0x40400000)if (f < 3.0f)
if (FLOAT2INTCAST(f) <= 0x40400000)if (f <= 3.0f)
if (FLOAT2INTCAST(f) > 0x40400000)if (f > 3.0f)
if (FLOAT2INTCAST(f) >= 0x40400000)if (f >= 3.0f)
Table 5.Comparisons Among Two Floats
Use this …Instead of this.
float t = f1 - f2;
if (FLOAT2UINTCAST(t) > 0x80000000U)
float t = f1 - f2;
if (FLOAT2INTCAST(t) <= 0)
float t = f1 - f2;
if (FLOAT2INTCAST(t) > 0)
float t = f1 - f2;
f (FLOAT2UINTCAST(f) <= 0x80000000U)
if (f1 < f2)
if (f1 <= f2)
if (f1 > f2)
if (f1 >= f2)
Chapter 2C and C++ Source-Level Optimizations39
Page 58
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
2.23Improving Performance in Linux® Libraries
Optimization
If symbol interposition is not important to a particular application, then you should control the
visibility of the symbols in a shared object in such a way as to optimize internal references to other
symbols in the library and minimize the symbol export table size.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Dynamically loadable libraries are a versatile feature of the Linux® operating system. These allow
one or more symbols in one library to override an identical symbol in another library. Known as
interposition, this ability makes customizations and probing seamless. Interposition is implemented
by means of a procedure linkage table (PLT). The PLT is so flexible that even references to an
overridden symbol inside its own library end up referencing the overriding symbol. However, the PLT
imposes a performance penalty by requiring all function calls to public global routines to go through
an extra step that increases the chances of cache misses and branch mispredictions. This is
particularly severe for C++ classes whose methods refer to other methods in the same class.
When using ld to link a shared object, include the command line option -Bsymbolic.
If using a version of gcc prior to 4.0 to link a shared object, add the option -Wl,-Bsymbolic to the
command-line. If using gcc 4.0 or later, add the option
line.
If finer control is desired, then it is possible to specify -fvisibility=hidden to gcc 4.0 or later and
then add __attribute__ ((visibility ("default"))) to each symbol that should be exported.
When building C++ shared objects, also consider using the -fvisibility-inlines-hidden option.
40C and C++ Source-Level OptimizationsChapter 2
-fvisibility=protected to the command-
Page 59
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
2.24Aligning Matrices
Optimization
When using multi-dimensional arrays or matrices, make sure that each row or 2nd-order dimension
starts at a 16-byte boundary.
Application
This optimization applies to:
• 32-bit software
• 64-bit software
Rationale
Instead of creating matrices with arbitrary dimensions, make sure that the size in bytes of the loworder dimension is a multiple of 16 and that it starts at a 16-byte boundary. By doing so, when
iterating over the elements of the matrix the compiler is presented with data properly aligned for lowcost vectorization.
For example, in:
double a [10][11],
b [10][11];
int i, j;
for (j = 0; j < 10; j++)
for (i = 0; i < 11; i++)
b [j][i] = a [j][i] * M_1_PI;
Declare the matrices in this way:
__declspec (align (16))
double a [10][ ((11 * sizeof (double) + 15) / 16) * 16 / sizeof (double)],
b [10][ ((11 * sizeof (double) + 15) / 16) * 16 / sizeof (double)];
int i, j;
for (j = 0; j < 10; j++)
for (i = 0; i < 11; i++)
b [j][i] = a [j][i] * M_1_PI;
However, be aware of cache-bank conflicts for best performance. For more information, see section
5.7, "L1 Data Cache Bank Conflicts" on page 90.
Chapter 2C and C++ Source-Level Optimizations41
Page 60
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
42C and C++ Source-Level OptimizationsChapter 2
Page 61
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Chapter 3General 64-Bit Optimizations
The AMD64 architecture provides a compatibility mode, which allows a 64-bit operating system to
run existing 16-bit and 32-bit applications, and a 64-bit mode, which provides 64-bit addressing and
expanded register resources to improve performance for recompiled 64-bit programs. This chapter
presents general optimizations to improve the performance of software designed to run in 64-bit
mode.
This chapter covers the following topics:
TopicPage
64-Bit Registers and Integer Arithmetic43
Using 64-bit Arithmetic for Large-Integer Multiplication45
128-Bit Media Instructions and Floating-Point Operations49
32-Bit Legacy GPRs and Small Unsigned Integers49
3.164-Bit Registers and Integer Arithmetic
Optimization
Use 64-bit registers for 64-bit integer arithmetic.
Rationale
Using 64-bit registers instead of their 32-bit equivalents can dramatically reduce the amount of code
necessary to perform 64-bit integer arithmetic.
Example 1
This code performs 64-bit addition using 32-bit registers:
; Add ECX:EBX to EDX:EAX, and place sum in EDX:EAX.
00000000 03 C3 add eax, ebx
00000002 13 D1 adc edx, ecx
Using 64-bit registers, the previous code can be replaced by one simple instruction (assuming that
RAX and RBX contain the 64-bit integer values to add):
00000000 48 03 C3 add rax, rbx
Although the preceding instruction requires one additional byte for the REX prefix, it is still one byte
shorter than the original code. More importantly, this instruction still has a latency of only one cycle,
uses two fewer registers, and occupies only one decode slot.
Chapter 3General 64-Bit Optimizations43
Page 62
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Example 2
To perform the low-order half of the product of two 64-bit integers using 32-bit registers, a procedure
such as the following is necessary:
Using 64-bit registers, the entire product can be produced with only one instruction:
; Multiply RAX by RBX. The 128-bit product is stored in RDX:RAX.
00000000 48 F7 EB imul rbx
Related Information
For more examples of 64-bit arithmetic using only 32-bit registers, see the example on page 47 and
“Efficient 64-Bit Integer Arithmetic in 32-Bit Mode” on page 129.
44General 64-Bit OptimizationsChapter 3
Page 63
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
3.2Using 64-bit Arithmetic for Large-Integer
Multiplication
Optimization
Use 64-bit arithmetic for integer multiplication that produces 128-bit or larger products.
Background
Large integer multiplications (those involving 128-bit or larger products) are utilized in a variety of
applications, such as cryptography software, which figure prominently in e-commerce applications
and secure transactions on the Internet. Processors cannot perform large-number multiplication
natively; they must break the operation into chunks that are permitted by their architecture (32-bit or
64-bit additions and multiplications).
Rationale
Using 64-bit rather than 32-bit integer operations dramatically reduces the number of additions and
multiplications required to compute large products. For example, computing a 1024-bit product using
64-bit arithmetic requires fewer than one quarter the number of instructions required when using
32-bit operations:
Comparing...32-bit arithmetic64-bit arithmetic
Number of multiplications25664
Number of additions with carry509125
Number of additions25563
In addition, the processor performs 64-bit additions just as fast as it performs 32-bit additions, and the
latency of 64-bit multiplications is only slightly higher than for 32-bit multiplications. (The processor
is capable of performing three independent 64-bit additions each clock cycle and a 64-bit
multiplication every other clock cycle.)
Example
Consider the multiplication of two unsigned 64-bit numbers a and b, represented in terms of 32-bit
components a1:a0 and b1:b0.
a = a1 * 232 + a0
b = b1 * 232 + b0
The product of a and b, calculated using the FOIL method of the polynomials above, can be expressed
in terms of products of the 32-bit components, as follows:
Chapter 3General 64-Bit Optimizations45
Page 64
Software Optimization Guide for AMD Family 10h Processors
Each of the products of the components of a and b (for example, a1 * b1) is composed of 64 bits—an
upper 32 bits and a lower 32 bits. It is convenient to represent these individual products as d, e, f, and
g, as follows:
a0 * b0 = d1:d0 = d1 * 232 + d0
a1 * b0 = e1:e0 = e1 * 232 + e0
a0 * b1 = f1:f0 = f1 * 232 + f0
a1 * b1 = g1:g0 = g1 * 232 + g0
Substitution into Formula 3.1 above yields the following equation:
mov eax,[esi+4] ; EAX = a1
mov edx,[edi+4] ; EDX = b1
mul edx ; EDX:EAX = a1*b1 = g1:g0
add ebp,eax ; EBP = e1 + f1 + g0 + carry
adc esp,edx ; ESP = g1 + carry
mov [ecx+8],ebp ; c2 = e1 + f1 + g0 + carry
mov [ecx+12],esp ; c3 = g1 + carry
;==============================================================================
; Restore the register state.
mov esp, [tempESP]
pop ebp
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
;==============================================================================
ret
_32bitalu_64x64 ENDP
_TEXT ENDS
END
40546 Rev. 3.09 November 2008
To improve performance and substantially reduce code size, the following procedure performs the
same 64-bit integer multiplication using 64-bit instead of 32-bit operations:
; 64bitalu_64x64(int *a, int *b, int *c);
;
; TO ASSEMBLE INTO *.obj DO THE FOLLOWING:
; ml64.exe -c 64bitalu_64x64.asm
;
_TEXT SEGMENT
64bitalu_64x64 PROC NEAR
;==============================================================================
; Parameters passed into routine:
; rcx = ->a
; rdx = ->b
; r8 = ->c
;==============================================================================
mov rax, [rcx] ; RAX = [a0]
mul [rdx] ; Multiply [a0] by [b0] such that
; RDX:RAX = [c1]:[c0].
mov [r8], rax ; Store 128-bit product of a and b.
mov [r8+8], rdx
;==============================================================================
ret
64bitalu_64x64 ENDP
END
48General 64-Bit OptimizationsChapter 3
Page 67
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
3.3128-Bit Media Instructions and Floating-Point
Operations
Optimization
Use 128-bit media (SSE, SSE2, SSE3, and SSE4a) instructions instead of x87 or 64-bit media
instructions for floating-point operations.
Rationale
In 64-bit mode, the processor provides eight additional XMM registers (XMM8–XMM15) for a total
of 16. These extra registers can substantially reduce register pressure in floating-point code written
using 128-bit media instructions.
Although the processor fully supports the x87 and 64-bit media instructions, there are only eight
registers available to these instructions (ST(0)–ST(7) or MMX0–MMX7, respectively). Additionally,
the x87 and 64-bit media instructions require cumbersome register manipulation and mode switches,
unlike SSE, SSE2, SSE3 and SSE4a.
For further information, see Chapter 9, “Optimizing with SIMD Instructions” on page 145.
3.432-Bit Legacy GPRs and Small Unsigned Integers
Optimization
Use the 32-bit legacy general-purpose registers (EAX through ESI) instead of their 64-bit extensions
to store unsigned integer values whose range never requires more than 32 bits, even if subsequent
statements use the 32-bit value in a 64-bit operation. (For example, use ECX instead of RCX until you
need to perform a 64-bit operation; then use RCX.)
Rationale
In 64-bit mode, the machine-language representation of many instructions that operate on unsigned
64-bit register operands requires a REX prefix byte, which increases the size of the code. However,
instructions that operate on a 32-bit legacy register operand do not require the prefix and have the
desirable side-effect of clearing the upper 32 bits of the extended register to zero. For example, using
the AND instruction on ECX clears the upper half of RCX.
Caution
Because the assembler also uses a REX prefix byte to encode the 32-bit sizes of the eight new 64-bit
general-purpose registers (R8D–R15D), you should only use one of the original eight generalpurpose registers (EAX through ESI) to implement this technique.
Chapter 3General 64-Bit Optimizations49
Page 68
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Example
The following example illustrates the unnecessary use of 64-bit registers to calculate the number of
bytes remaining to be copied by an aligned block-copy routine after copying the first few bytes having
addresses not meeting the routine’s 8-byte-alignment requirements. The first two statements, after the
program comments, use the 64-bit R10 register—presumably, because this value is later used to
adjust a 64-bit value in R8—even though it requires no more than four bits to represent the range of
values stored in R10. Using R10 instead of a smaller register requires a REX prefix byte (in this case,
49), which increases the size of the machine-language code.
; Input:
; R10 = source address (src)
; R8 = number of bytes to copy (count)
49 F7 DA neg r10 ; Subtract the source address from 2^64.
49 83 E2 07 and r10, 7 ; Determine how many bytes were copied separately.
4D 2B C2 sub r8, r10 ; Subtract the number of bytes already copied from
; the number of bytes to copy.
To improve code density, the following rewritten code uses ECX until it is absolutely necessary to use
RCX, eliminating two REX prefix bytes:
F7 D9 neg ecx ; Subtract the source address from 2^32 (the processor
; clears the high 32 bits of RCX).
83 E1 07 and ecx, 7 ; Determine how many bytes were copied separately.
4C 2B C1 sub r8, rcx ; Subtract the number of bytes already copied from
; the number of bytes to copy.
50General 64-Bit OptimizationsChapter 3
Page 69
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Chapter 4Instruction-Decoding
Optimizations
The optimizations in this chapter are designed to help maximize the number of instructions that the
processor can decode at one time.
The AMD Family 10h processor instruction fetcher reads 32-byte packets from the L1 instruction
cache. These packets are 32-byte aligned. The instruction bytes are then merged into a 32-byte pick
window. On each cycle, the in-order front-end engine selects up to three AMD64 instructions to
decode from the pick window.
This chapter covers the following topics:
TopicPage
DirectPath Instructions52
Load-Execute Instructions for Floating-Point or Integer Operands53
Loop Iteration Boundaries56
32/64-Bit vs. 16-Bit Forms of the LEA Instruction56
Take Advantage of x86 and AMD64 Complex Addressing Modes57
Short Instruction Encodings56
Stack Operations59
Partial-Register Writes60
Using LEAVE for Function Epilogues64
Alternatives to SHLD Instruction66
8-Bit Sign-Extended Immediate Values67
8-Bit Sign-Extended Displacements68
Code Padding with Operand-Size Override and Multibyte NOP68
Chapter 4Instruction-Decoding Optimizations51
Page 70
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
4.1DirectPath Instructions
Optimization
Use DirectPath instructions rather than VectorPath instructions. To determine the type of an
instruction—either DirectPath or VectorPath—see Appendix C, “Instruction Latencies.”
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
DirectPath instructions minimize the number of operations per AMD64 instruction, thus providing
for optimally efficient decode and execution. Up to three DirectPath Single instructions, or one and a
half DirectPath Double instructions, can be decoded per cycle. VectorPath instructions block the
decoding of DirectPath instructions.
The AMD Family 10h processor has been designed to execute the instructions most frequently
generated by compilers as DirectPath Single or DirectPath Double instructions. However, assembly
writers must still take into consideration the use of DirectPath versus VectorPath instructions.
Examples
The following example shows code for swapping two memory values. Although the second case uses
an extra instruction, it is preferred because it avoids VectorPath instructions.
Avoid code such as the following which uses a V
movzx eax, BYTE PTR [memA]
xchg [memB], al ; xchg mem8, reg8 is a VectorPath instruction
mov [memA], al
Instead, use an equivalent instruction sequence such as the following using DirectPath instructions.
;; All of the following are DirectPath instructions
movzx eax, BYTE PTR [memA]
movzx ebx, BYTE PTR [memB]
mov [memB], al
mov [memA], bl
ectorPath instruction.
52Instruction-Decoding OptimizationsChapter 4
Page 71
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
4.2Load-Execute Instructions for Floating-Point or
Integer Operands
A load-execute instruction is an instruction that loads a value from memory into a register and then
performs an operation on that value. Many general purpose instructions, such as ADD, SUB, AND,
etc., have load-execute forms:
ADD rax, QWORD PTR [foo]
This instruction loads the value foo from memory and then adds it to the value in the RAX register.
The work performed by a load-execute instruction can also be accomplished by using two discrete
instructions—a load instruction followed by an execute instruction. The following example employs
discrete load and execute stages:
MOV rbx, QWORD PTR [foo]
ADD rax, rbx
The first statement loads the value foo from memory into the RBX register. The second statement
adds the value in RBX to the value in RAX.
The following optimizations govern the use of load-execute instructions:
•Load-Execute Integer Instructions on page 53.
•Load-Execute SSE/SSE2/SSE3 Instructions with Floating-Point or Integer Operands on page 54.
•Load-Execute x87 Instructions with Integer Operands on page 55.
4.2.1Load-Execute Integer Instructions
Optimization
When performing integer computations, use load-execute instructions instead of discrete load
and execute instructions. Use discrete load and execute instructions only under one or more of the
wing circumstances:
follo
•to explicitly schedule load and execute operations
•to avoid scheduler stalls for longer e
•if the load target will be used multiple times in different instructions
xecuting instructions
Application
This optimization applies to:
•32-bit software
•64-bit software
Chapter 4Instruction-Decoding Optimizations53
Page 72
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Rationale
Most load-execute integer instructions are DirectPath decodable and can be decoded at the rate of
three per cycle. Splitting a load-execute integer instruction into two separate instructions reduces
decoding bandwidth and increases register pressure, which results in lower performance.
4.2.2Load-Execute SSE/SSE2/SSE3 Instructions with Floating-Point
or Integer Operands
Optimization
When performing floating-point computation using floating-point or integer source operands,
use load-execute instructions instead of discrete load and execute instructions.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Using load-execute floating-point instructions that take floating-point or integer operands improves
performance for the following reasons:
•Denser code allows more work to be held in the instruction cache.
•Denser code generates fewer internal macro-ops, allowing the floating-point scheduler to hold
more work, which increases the chances of extracting parallelism from the code.
The use of load-execute packed SSE instructions instead of distinct load and e
xecute instructions
improves performance in cases in which data might not be aligned on a 16-byte boundary. However,
this requires setting the misaligned exception mask (MXCSR[17]). Setting this bit disables general
protection exceptions for unaligned loads in SSE load-execute instructions. See also “Unaligned and
Aligned Data Access” on page 147.
Example
Avoid code such as the following, which uses discrete load and execute SSE instructions:
Instead, use code such as the following, which uses a load-execute SSE floating-point instruction:
movss xmm0, [float_var1]
mulss xmm0, [float_var2]
54Instruction-Decoding OptimizationsChapter 4
Page 73
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
4.2.3Load-Execute x87 Instructions with Integer Operands
Optimization
Avoid x87 load-execute floating-point instructions that take integer operands (FIADD, FICOM,
FICOMP, FIDIV, FIDIVR, FIMUL, FISUB, and FISUBR). When performing floating-point
computations using integer source operands, use discrete load (FILD) and execute instructions
instead.
However, when performing floating-point computations using floating point operands, use loadexecute instructions instead of discrete load and execute instructions.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
The load-execute floating-point instructions that take integer operands take longer to execute than an
FILD followed by the same register-execute arithmetic instruction. In some situations, this
optimization can also reduce execution time, if FILD can be scheduled several instructions ahead of
the arithmetic instruction to cover the FILD latency.
Example
Avoid code such as the following, which uses load-execute floating-point instructions that take
integer operands:
Instead, use code such as the following, which uses discrete load and execute instructions:
fild DWORD PTR [bar] ; Push bar onto FP stack.
fild DWORD PTR [baz] ; Push baz onto FP stack.
fld QWORD PTR [foo] ; Push foo onto FP stack.
fmulp st(2), st ; Multiply and pop [ST(1) = foo * bar, ST(0) = baz].
faddp st(1), st ; Add and pop [ST(0) = baz + (foo * bar)].
Chapter 4Instruction-Decoding Optimizations55
Page 74
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
4.3Loop Iteration Boundaries
Optimization
Fit an entire loop iteration into the smallest number of 32-byte-aligned blocks. In program “hot spots”
(as determined by either profiling or loop-nesting analysis), loop iteration boundaries should be
placed at or near the beginning of code windows that are 32-byte aligned. The smaller the basic block,
the more beneficial this optimization will be.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
AMD Family 10h processors can fetch 32 bytes of instruction data per cycle, but will not fetch across
a 32-byte-aligned boundary. Aligning branch targets maximizes the number of instructions in the pick
window and preserves instruction-cache space in branch-intensive code outside such hot spots.
4.432/64-Bit vs. 16-Bit Forms of the LEA Instruction
Optimization
Use the 32-bit or 64-bit forms of the Load Effective Address (LEA) instruction rather than the 16-bit
form.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
The 32-bit and 64-bit LEA instructions are implemented as DirectPath operations with an execution
latency of only two cycles. The 16-bit LEA instruction, however, is a VectorPath instruction, which
lowers the decode bandwidth and has a longer execution latency.
56Instruction-Decoding OptimizationsChapter 4
Page 75
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
4.5Take Advantage of x86 and AMD64 Complex
Addressing Modes
Optimization
When porting from other architectures, remember that the x86 architecture provides many complex
addressing modes. By building the effective address in one instruction, the instruction count can
sometimes be reduced, leading to better code density and greater decode bandwidth. Refer to the
section on effective addresses in the AMD64 Architecture Programmer's Manual, Volume 1: Application Programming, order# 24592 for more detailed information on how effective addresses
are formed.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Building the effective address sometimes seems to require numerous instructions when there is a base
address (such as the base of an array), an index and a displacement (if applicable). However, the x86
architecture can often handle all of this information in one instruction. This can reduce code size and
results in fewer instructions to decode. As always, attention should be paid to total instruction length,
latencies and whether or not the instruction choices are DirectPath (fastest) or VectorPath (slower).
Example
The first instruction sequence of five instructions having a total latency of 8 can be replaced by one
instruction.
Use instruction forms with shorter encodings rather than those with longer encodings. For example,
use 8-bit displacements instead of 32-bit displacements, and use the single-byte form of simple
integer instructions instead of the 2-byte opcode-ModR/M form.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Using shorter instructions increases the number of instructions that can fit into the L1 instruction
cache and increases the average decode rate.
Example
Avoid the use of instructions having longer encodings, such as the following:
Software Optimization Guide for AMD Family 10h Processors
4.7Stack Operations
Optimization
When saving or restoring registers in function prologues or epilogues, or when passing arguments
through the stack, use PUSH or POP instructions to improve performance and to reduce code size.
When deallocating stack space at function exit, use RET imm to improve performance and to reduce
code size.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
In spite of the implicit dependency between several successive PUSH or POP instructions on the
stack-pointer (which PUSH and POP modify), special circuitry (the Sideband Stack Optimizer) tracks
the value that the stack-pointer assumes, allowing parallel execution of more than one PUSH or POP.
This is also true of instructions that reference the stack-pointer, either implicitly or explicitly,
including:
•Near CALL
•Near RET
•LEAVE
•Instructions that specify the stack-pointer as a source register.
•Instructions that specify the stack-pointer in the addressing mode of a memory operand without
an index register.
However, the Sideband Stack Optimizer cannot break the dependency between the aforementioned
instructions and other instructions that refer either implicitly or explicitly to the stack-pointer,
including:
•LEA instructions that specify the stack-pointer.
•Instructions that specify the stack-pointer as a destination register.
•Instructions that specify the stack-pointer in the addressing mode of a memory operand with an
index register.
•VectorPath instructions that specify the stack-pointer.
Chapter 4Instruction-Decoding Optimizations59
Page 78
Software Optimization Guide for AMD Family 10h Processors
Examples
Avoid
sub esp, 20
mov [esp + 16], edi
mov [esp + 12], esi
...
mov [esp], eax
call ...
...
mov esi, [esp + 12]
mov edi, [esp + 16]
add esp, 20
ret
Preferred
sub esp, 8
push edi
push esi
...
push eax
call ...
...
pop esi
pop edi
add esp, 8
ret
40546 Rev. 3.09 November 2008
4.8Partial-Register Writes
Optimization
When writing to a register for the purpose of initialization, avoid instructions that
•write less than 32 bits of a general purpose integer register.
•write less than 128 bits of an XMM register.
When writing to a register during the course of a non-initializing operation on the register,
•avoid partial register writes.
•schedule any prior operations on the target register well ahead of the point in the code where the
partial write is to occur.
Application
This optimization applies to:
•32-bit software
60Instruction-Decoding OptimizationsChapter 4
Page 79
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
•64-bit software
Rationale
In order to handle partial register writes, the processor’s execution core implements a data merging
scheme. In the execution unit, an instruction that writes part of a register merges the modified portion
with the current state of the other part of the register. This creates a false dependency on the most
recent instruction that writes to any part of the register.
When writing to a register for the purpose of register initialization, it is usually possible to avoid false
dependencies by careful instruction selection. For example, rather than initializing a part of a floatingpoint 128-bit XMM register, initialize the whole 128-bit register.
When writing to a register during the course of a non-initializing operation on the register, there is
usually no additional performance loss due to partial register reads and writes. This is because, in the
typical case, the partial register being written to is also a source operand to the operation.
For example, the following instruction does not suffer from merge dependencies:
addsd, xmm1, xmm2
However, in some cases of non-initializing operations on a register, it is preferable to avoid partial
register writes and replace them with more efficient operations. In these cases, the partial register
being written by the operation is not a source for the operation. Examples are provided below.
If it is not possible to avoid writing to a part of that register, you should schedule any such prior
operation on any part of the register well ahead of the point where the partial write occurs. Such cases
are also listed in the examples.
A general purpose integer register is viewed as a 64-bit register internal to the processor. A floatingpoint XMM register is viewed as one 128-bit register internal to the processor.
Current generation processors cannot write a 64-bit half of a 128-bit XMM register without having a
merge dependency on the other 64-bit half. Additionally, current generation processors cannot write a
32-bit portion of a 128-bit XMM register without having a merge dependency on other bits of that
register.
However, there are several instructions that initialize the lower 64 bits or 32 bits of an XMM register
that also zero out the upper 64 or 96 bits and, thus, do not suffer from merge dependencies. For
example, the following instructions do not have merge dependencies:
movsd xmm, [mem64]
movss xmm, [mem32]
Integer operations that write to the lower 32 bits of a general purpose integer register do not have a
false merge dependency because they zero out the upper 32 bits. But operations that write to portions
of a general purpose integer register narrower than 32 bits should be avoided.
Chapter 4Instruction-Decoding Optimizations61
Page 80
Software Optimization Guide for AMD Family 10h Processors
Example 1
Avoid
MOV al, bl
Preferred
MOVZX eax, bl
Example 2
Avoid
MOV al, [ebx]
Preferred
MOVZX eax, byte ptr [ebx]
40546 Rev. 3.09 November 2008
Example 3
Avoid
MOV al, 01h
Preferred
MOV eax, 00000001h
Example 4
The following recommendation only applies when both instruction operands of the MOVSS instruction
are registers.
Avoid
MOVSS xmm1, xmm2
Preferred
MOVAPS xmm1, xmm2
62Instruction-Decoding OptimizationsChapter 4
Page 81
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Example 5
While this example uses instructions with double-precision data type, the principle also applies to
instructions with single-precision data types.
Avoid
MOVLPD xmm1, QWORD PTR[eax] ; ; Address may or may not be
; 16-byte aligned.
MOVHPD xmm1, QWORD PTR[eax+8];
Preferred
MOVUPD xmm1, XMMWORD PTR[eax] ;
Example 6
Avoid
MOVLPD xmm1, QWORD PTR [eax]
MOVHPD xmm1, QWORD PTR [eax] ; Same memory location as used for MOVLPD.
Preferred
MOVDDUP xmm1, QWORD PTR [eax];
Example 7
The following recommendation only applies when both operands of the MOVSD instruction are
registers. If the source operand of the MOVSD instruction is a memory operand, the high-order 64 bits
of the destination register are zeroed out, thereby avoiding any merge dependency on the destination
register.
Avoid
MOVSD xmm1, xmm2
Preferred
MOVAPD xmm1, xmm2
Chapter 4Instruction-Decoding Optimizations63
Page 82
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Example 8
When the source operand of the MOVSD instruction is a memory operand, the high-order 64 bits of the
destination register are zeroed out, thereby avoiding any merge dependency on the destination
register.
Avoid
MOVLPD xmm1, mem64
Preferred
MOVSD xmm1, mem64
Example 9
The SQRTSD instruction writes only the lower 64 bits of xmm1 and so should be moved well below
the write from the MULPD. In addition to SQRTSD, this example also applies for other instructions
such as CVTPI2PS, CVTSI2SD, CVTSS2SD, UNPCKLPD, PUNPCKLQDQ, MOVLHPS and
MOVHLPS.
Avoid
MULPD xmm1, xmm3
SQRTSD xmm1, xmm2
Preferred
MULPD xmm1, xmm3
.....schedule some other unrelated instructions here
SQRTSD xmm1, xmm2
4.9Using LEAVE for Function Epilogues
Optimization
The recommended optimization for function epilogues depends on whether the function allocates
local variables.
If the functionThen
Allocates local variables.Replace the traditional function epilogue with the LEAVE instruction.
Does not allocate local variables or
does not have a frame-pointer.
Do not use function prologues or epilogues. Access function
arguments and local variables through rSP.
64Instruction-Decoding OptimizationsChapter 4
Page 83
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Functions That Allocate Local Variables
The LEAVE instruction is a single-byte instruction and saves 2 bytes of code space over the
traditional epilogue. Replacing the traditional sequence with LEAVE also preserves decode
bandwidth.
Functions That Do Not Allocate Local Variables
Accessing function arguments and local variables directly through ESP frees EBP for use as a
general-purpose register.
Background
The function arguments and local variables inside a function are referenced through a so-called frame
pointer. In AMD64 code, the base pointer register (rBP) is customarily used as a frame pointer. You
set up the frame pointer at the beginning of the function using a function prologue:
push ebp ; Save old frame pointer.
mov ebp, esp ; Initialize new frame pointer.
sub esp, n ; Allocate space for local variables (only if the
; function allocates local variables).
Function arguments on the stack can now be accessed at positive offsets relative to rBP, and local
variables are accessible at negative offsets relative to rBP.
Example
The traditional function epilogue looks like this:
mov esp, ebp ; Deallocate local variables (only if space was allocated).
pop ebp ; Restore old frame pointer.
Replace the traditional function epilogue with a single LEAVE instruction:
leave
Chapter 4Instruction-Decoding Optimizations65
Page 84
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
4.10Alternatives to SHLD Instruction
Optimization
Where register pressure is low, replace the SHLD instruction with alternative code using ADD and
ADC, or SHR and LEA.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Using alternative code in place of SHLD achieves lower overall latency and requires fewer execution
resources. The 32-bit and 64-bit forms of ADD, ADC, SHR, and LEA (except 16-bit form) are
DirectPath instructions, while SHLD is a VectorPath instruction. Use of the replacement code
optimizes decode bandwidth because it potentially enables the simultaneous decoding of a third
DirectPath instruction. However, the replacement code may increase register pressure because it
destroys the contents of one register (reg2 in the following examples) whereas the register is
preserved by SHLD.
Example 1
Replace this instruction:
shld reg32a, reg32b, 1 ; Operands are 32-bit registers.
Software Optimization Guide for AMD Family 10h Processors
Example 3
Replace this instruction:
shld reg1, reg2, 3
with this code sequence:
shr reg2, 29
lea reg1, [reg1*8+reg2]
4.118-Bit Sign-Extended Immediate Values
Optimization
Use 8-bit sign-extended immediate values instead of larger-size values.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Using 8-bit sign-extended immediate values improves code density with no negative effects on the
processor.
Example
Consider this instruction:
add bx, -5
Avoid encoding it as:
81 C3 FF FB
Instead, encode it as:
83 C3 FB
Chapter 4Instruction-Decoding Optimizations67
Page 86
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
4.128-Bit Sign-Extended Displacements
Optimization
Use 8-bit sign-extended displacements for conditional branches.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Using short, 8-bit sign-extended displacements for conditional branches improves code density with
no negative effects on the processor. See “Branch Alignment and Density” on page 99 for more
details on optimizing branches.
4.13Code Padding with Operand-Size Override and
Multibyte NOP
Optimization
Use the multibyte NOP instruction (0F 1Fh) to align code and space out branches..
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Occasionally it is necessary to insert neutral code fillers into the code stream (for example, for codealignment purposes or to space out branches). Because this filler code is executable, it should take up
as few execution resources as possible, should not diminish decode density, and should not modify
any processor state other than to advance the instruction pointer (rIP). Although there are several
possible multibyte NOP-equivalent instructions that do not change the processor state (other than
rIP), combinations of the operand-size override and the multibyte NOP instruction are more efficient.
These NOP instructions are only available on AMD Athlon™ and later processors. For processors
older than Athlon, use the standard NOP (opcode 090h) in combination with up to three operand size
68Instruction-Decoding OptimizationsChapter 4
Page 87
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
override prefixes (opcode 66h). The use of more than three legacy prefixes limits decoder
performance.
Chapter 4Instruction-Decoding Optimizations69
Page 88
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Example
Assign code-padding sequences like these and use them to align code and space out branches. These
sequences are suitable for both 32-bit and 64-bit code, and you can use them on the AMD Family 10h
processors:
In certain rare situations, padding of up to 31 bytes can improve performance by aligning “hot”
branch targets. For example, run-time profile information may reveal that a forward branch is very
often taken. In these cases, generate padding by combining a minimum number of the large NOP
instructions used in the above code-padding sequences.
For x87 floating-point instructions, a better single-byte padding exists. See “Align and Pack
DirectPath x87 Instructions” on page 173. For aligning loop tops, see “Loop Iteration Boundaries” on
page 56.
70Instruction-Decoding OptimizationsChapter 4
Page 89
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Chapter 5Cache and Memory Optimizations
The optimizations in this chapter take advantage of the large L1 caches and high-bandwidth buses of
AMD Family 10h processors.
This chapter covers the following topics:
TopicPage
Memory-Size Mismatches71
Natural Alignment of Data Objects73
Store-to-Load Forwarding Restrictions74
Prefetch and Streaming Instructions81
Write-Combining89
L1 Data Cache Bank Conflicts90
Placing Code and Data in the Same 64-Byte Cache Line91
Memory and String Routines92
Stack Considerations94
Cache Issues When Writing Instruction Bytes to Memory95
Interleave Loads and Stores96
Using 1-Gbyte Virtual Memory Pages96
5.1Memory-Size Mismatches
Optimization
Avoid memory-size mismatches when different instructions operate on the same data. When one
instruction stores and another instruction subsequently loads the same data, align instruction operands
an
d keep the loads/stores of each operand the same size.
Application
This optimization applies to:
•32-bit software
•64-bit software
Examples—Store-to-Load-Forwarding Stalls
The following code examples result in a store-to-load-forwarding stall:
Chapter 5Cache and Memory Optimizations71
Page 90
Software Optimization Guide for AMD Family 10h Processors
Avoid (64-bit)
foo DQ ? ; Assume foo is 8-byte aligned.
...
mov DWORD PTR foo, eax ; Store a DWORD to foo.
mov DWORD PTR foo+4, ebx ; Now store to foo+4.
mov rcx, QWORD PTR foo ; Load a QWORD from foo.
Avoid (32-bit)
foo DQ ? ; Assume foo is 4-byte aligned.
...
mov DWORD PTR foo, eax ; Store a DWORD in foo.
mov DWORD PTR foo+4, edx ; Store a DWORD in foo+4.
fld QWORD PTR foo ; Load a QWORD from foo.
Preferred If the Stores and Loads are Close Together, Option 2
movd eax, mm0
punpckhdq mm0, mm0
movd edx, mm0
5.2Natural Alignment of Data Objects
Optimization
Make sure data objects are naturally aligned. An object is naturally aligned if it is located at an
address that is a multiple of its size.
Chapter 5Cache and Memory Optimizations73
Page 92
Software Optimization Guide for AMD Family 10h Processors
Locate this type of objectAt an address evenly divisible by
Word2
Doubleword4
Quadword8
Ten-byte (for example, TBYTE or REAL10)8 (instead of 10)
Double quadword16
40546 Rev. 3.09 November 2008
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
A misaligned store or load operation suffers a minimum one-cycle penalty in the processor’s loadstore pipeline. Also, using misaligned loads and stores increase the likelihood of encountering a storeto-load forwarding pitfall, especially when operating in long mode (64-bit software). (For a more
detailed discussion of store-to-load forwarding issues, see “Store-to-Load Forwarding Restrictions”
on page 74.)
In addition, if the Alignment Mask bit is set in Control Register 0 (CR0), an unaligned memory
reference may cause an alignment check exception. For more information on this topic, see the
AMD64 Architecture Programmer’s Manual, Volume 2, order# 24593.
5.3Store-to-Load Forwarding Restrictions
Optimization
Maintain consistent operand sizes across all loads and stores. Preferably use doubleword, quadword,
or 128-bit operand sizes. Avoid store-to-load forwarding pitfalls, such as
•narrow-to-wide forwarding cases.
•mismatched addresses for stores and loads.
•misaligned data references.
•loading data from anywhere in the same doubleword of memory other than the identical start
addresses of the stores when using word or byte stores
74Cache and Memory OptimizationsChapter 5
Page 93
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Store-to-load forwarding refers to the process of a load reading (forwarding) data from the store
buffer. Where this is possible, it can lead to a performance improvement because the load does not
have to wait for the recently written (stored) data to be written to cache and then read back out again.
There are circumstances under which AMD Family 10h processor load-store (LS) architecture does
not allow data to be read from a store in the store buffer. In these cases, it is impossible to load the
needed data into a register until the store has retired out of the store buffer and written to the data
cache. A store-buffer entry cannot retire and write to the data cache until every instruction before the
store has completed and retired from the reorder buffer. The implication of this restriction is that all
instructions in the reorder buffer, up to and including the store, must complete and retire out of the
reorder buffer before the load can complete. Effectively, the load has a false dependency on every
instruction up to the store.
Due to the significant depth of the LS buffer of AMD Family 10h processors, any load that is
dependent on a store that cannot bypass data through the LS buffer may experience significant delays
of up to tens of clock cycles, where the exact delay is a function of pipeline conditions.
The following sections describe store-to-load forwarding examples.
A load is not allowed to read data from the store-buffer entry if any of the following conditions occur:
•The start address of the load does not match the start address of the store.
•The load operand size is greater than the store operand size.
•Either the load or the store is misaligned. See “Natural Alignment of Data Objects” on page 73 for
additional information on alignment recommendations.
•A high byte (or word) store and a low byte (or word) store in the same aligned doubleword are
followed by either a low or high byte (or word) load.
The following sections describe common-case scenarios to avoid. In these scenarios, a load has a true
dependency on an LS2-buffered store, but cannot read (forward) data from a store-buffer entry.
Load Operand Size Greater than the Store Operand Size
If the following conditions are present, there is a narrow-to-wide store-buffer data-forwarding
restriction:
Chapter 5Cache and Memory Optimizations75
Page 94
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
•The operand size of the store data is smaller than the operand size of the load data.
•The range of addresses spanned by the store data covers some subrange of the addresses spanned
by the load data.
Examples
Avoid
mov eax, 10h
mov WORD PTR [eax], bx ; Word store
...
mov ecx, DWORD PTR [eax] ; Doubleword load--cannot forward upper byte
; from store buffer
Avoid
MOV eax, 10h
MOV BYTE PTR [eax+3], bl ; Byte store
...
MOV ecx, DWORD PTR [eax] ; Doubleword load--cannot forward upper byte
; from store buffer
Avoid
MOV eax, 10h
MOVSD QWORD PTR [eax], xmm0 ; Quadword store
MOVSD QWORD PTR [eax+8], xmm1 ; Quadword store
...
MOVAPD xmm2, XMMWORD PTR [eax] ; Octal Word load--cannot forward upper and
; lower Quadwords from store buffer.
Preferred
MOV eax, 10h
MOVAPD xmm3, xmm0 ; Assumes XMM3 is available and will not be detrimental
; to register pressure
SHUFPD xmm3, xmm1, 0
MOVAPD XMMWORD PTR [eax], xmm3
....
MOVAPD xmm2, XMMWORD PTR [eax] ; Octal Word load--can forward from
; Octal word store from store buffer
76Cache and Memory OptimizationsChapter 5
Page 95
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
Mismatched Store and Load Addresses
A data-forwarding restriction exists if the start address of the store data does not match the start
address of the load data.
In general, wide stores can forward data to narrow loads if the start address of the load matches that of
the store and neither store nor load is misaligned. However, store-to-load forwarding cannot occur if
the start addresses of the load and store do not match, with the exception that stores to an aligned 128bit location can forward to loads of 64 bits or less, starting at its upper 64-bit quadword.
Examples
Avoid
movq [foo], mm1 ; Store upper and lower half.
...
add eax, [foo] ; Fine
add edx, [foo+4] ; Not good!
Preferred
movd [foo], mm1 ; Store lower half.
punpckhdq mm1, mm1 ; Copy upper half into lower half.
movd [foo+4], mm1 ; Store lower half.
...
add eax, [foo] ; Fine
add edx, [foo+4] ; Fine
Acceptable
mov eax, 10h
movapd XMMWORD PTR [eax], xmm0 ; Store upper and lower half.
...
movsd xmm1, QWORD PTR [eax] ; Fine
movsd xmm2, QWORD PTR [eax+8] ; Load of upper 64 bits, OK.
If the following condition is present, there is a misaligned store-buffer data-forwarding restriction:
•The store or load address is misaligned. For example, a quadword store is not aligned to a
quadword boundary.
A common case of misaligned store-data forwarding involves the passing of misaligned quadword
floating-point data on the doubleword-aligned integer stack. Avoid the type of code shown in the
following example:
mov esp, 24h
fstp QWORD PTR [esp] ; ESP = 24
Chapter 5Cache and Memory Optimizations77
Page 96
Software Optimization Guide for AMD Family 10h Processors
... ; Store occurs to quadword misaligned address.
fld QWORD PTR [esp] ; Quadword load cannot forward from quadword
; misaligned ‘FSTP[ESP]’ store operation.
40546 Rev. 3.09 November 2008
Forwarding Restriction from Distinct Stores on Distinct Bytes (or Words)
to a Subsequent Load on One of the Same Bytes (or Words) Within the
Same Aligned Doubleword Location
When there are two or more distinct stores to distinct bytes (or words) inside the same aligned
doubleword memory location, it may not be possible to forward the data from the stores to a
subsequent load from one of the same byte (or word) locations. Therefore, it is recommended to use
doubleword, quad word or 128-bit operand sizes to allow store-to-load forwarding.
However, when there is only a single store and a single load to a byte (or word) inside an aligned
doubleword location, store-to-load forwarding is allowed to occur as long as all of the other
conditions listed previously in this section for store-to-load forwarding are satisfied.
Examples
In all of the examples below, the operations in between the store and the load indicated by the ... are
assumed not to write to any part of the aligned doubleword under consideration.
Allowed
mov eax, 10h
mov BYTE PTR [eax], bl ; Low-byte store to an aligned doubleword
...
mov dl, BYTE PTR[eax] ; low byte load CAN forward from low byte store
Allowed
mov eax, 10h
mov WORD PTR [eax], bx ; Low-word store to an aligned doubleword
...
mov dl, BYTE PTR[eax] ; low byte load CAN forward from low word store
Allowed
mov eax, 10h
mov DWORD PTR [eax], ebx ; doubleword store to an aligned doubleword
...
mov dx, WORD PTR[eax] ; low word load CAN forward from doubleword store
Avoid
mov eax, 10h
mov BYTE PTR[eax], bl ; Low-byte store to an aligned doubleword
mov BYTE PTR[eax+1], bh ; High-byte store to an aligned doubleword
78Cache and Memory OptimizationsChapter 5
Page 97
40546 Rev. 3.09 November 2008
...
mov dl, BYTE PTR [eax] ; low byte load cannot forward from low byte store
Software Optimization Guide for AMD Family 10h Processors
mov eax, 10h
mov BYTE PTR[eax], bl ; Low-byte store to an aligned doubleword
mov BYTE PTR[eax+1], bh ; High-byte store to an aligned doubleword
...
mov dl, BYTE PTR [eax] ; low byte load cannot forward from low byte store
mov dh, BYTE PTR [eax+1]; high byte load CAN forward from high byte store
Preferred
mov eax, 10h
mov WORD PTR [eax], bx ;
...
mov dx, WORD PTR [eax] ;
Summary of Store-to-Load-Forwarding Pitfalls to Avoid
The following list summarizes the situations that require care to handle store-to-load forwarding
cases:
•Avoid narrow-to-wide forwarding cases.
•Avoid mismatched addresses for stores and loads.
•Avoid misaligned data references.
•When using word or byte stores, avoid having two or more distinct stores to distinct bytes (or
words) inside the same aligned doubleword memory location followed by a subsequent load from
one of the same byte (or word) locations.
•Maintain consistent operand sizes across all loads and stores. Preferably use doubleword,
quadword, or 128-bit operand sizes.
Chapter 5Cache and Memory Optimizations79
Page 98
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
5.4Good Practices for Avoiding False Store-to-Load
Forwarding
Optimization
Choose linear addresses for the source and destination operands of REP MOVS/CMPS that are not an
exact multiple of 4K pages away from each other.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
As mentioned in the previous section, store-to-load forwarding occurs when the store address matches
the load address. This address match is split into two stages. In the first stage, bits 4:11 of the store
and the load addresses are matched. In addition the double word mask of the store and load addresses
is matched. The double word mask indicates whether the load/store pair is accessing the same double
word in a 16-byte bank. If both these parameters match, then a store-to-load forward is initiated. In
the second stage the remaining bits 12:47 of the store and load addresses is matched. If the remaining
bits match, then the STLF is considered as a true STLF and is allowed to proceed. Otherwise it is
considered as a false STLF and the load is cancelled and retried.
The previous section deals with true STLF and describes the practices to follow to promote it. This
section deals with the cases of false STLF and what the developer needs to do to avoid these from
occurring in the first place, thereby avoiding the later penalty of STLF cancellation.
Example
For REP MOVS/CMPS, choose linear addresses that avoid conflicts.
REP stands for the repeat function. This function repeats or iterates its associated string instruction as
many times as specified in the counter register (rCX) and terminates the repetition when the value in
rCX reaches 0. For example, REP MOVS moves a string from a source address to a destination
address a specified number of times. In the event that bits 4:11 of the linear address of the store
address in the first iteration match the load address in the second iteration, a store-to-load forward
may be initiated.
When the destination address of an iteration is located at an exact multiple of 4K pages away from the
source address of the next iteration, an STLF will be initiated. When the remaining address bits are
found to be mismatched later, the STLF is cancelled and the load has to be retried. This results in a
significant penalty of wasted DC bandwidth due to having to retry loads multiple times.
80Cache and Memory OptimizationsChapter 5
Page 99
40546 Rev. 3.09 November 2008
Software Optimization Guide for AMD Family 10h Processors
For example, a REP MOVS instruction suffers from these inefficiencies if RSI is 0x1ffeee000000 and
RDI is 0x1ffeee401000.
5.5Prefetch and Streaming Instructions
Optimization
Where appropriate, use one of the prefetch instructions to increase the effective bandwidth of
AMD Family 10h processors.
Application
This optimization applies to:
•32-bit software
•64-bit software
Rationale
Prefetch instructions take advantage of the high bus bandwidth of AMD Family 10h processors to
hide latencies when fetching data from system memory. A prefetch instruction initiates a read request
of a specified address and reads the entire cache line that contains that address.
AMD Family 10h processors perform three types of prefetches:
Prefetch typeDescription
LoadReads the data into the L1 data cache; the data is later evicted to the L2 cache. The
following instructions perform load prefetches: PREFETCH, PREFETCHT0,
PREFETCHT1, and PREFETCHT2.
StoreReads the data into the L1 data cache and marks the data as modified; the data is
later evicted to the L2 cache. The PREFETCHW instruction performs a store prefetch.
NontemporalThe PREFETCHNTA instruction performs a nontemporal prefetch. The data is read
into the L1 data cache; to avoid cache pollution, when a PREFETCHNTA misses in
the L2 cache and reads from memory, the data is never evicted to the L2 cache. When
a PREFETCHNTA hits in the L2 cache, the data is evicted back to the L2 cache.
The prefetch instructions can be used anywhere, in any type of code. The use of prefetch instructions
is not affected by the values of Control Register 0 (CR0) bits, such as CR0.EM and CR0.TS.
Prefetching versus Preloading
In code that makes irregular memory accesses rather than seq
uential accesses, an ordinary MOV
instruction is the best way to load data. But in situations where sequential addresses are read, prefetch
instructions can improve performance. Prefetch instructions only update the L1 data cache and do not
update an architectural register.
Chapter 5Cache and Memory Optimizations81
Page 100
Software Optimization Guide for AMD Family 10h Processors
40546 Rev. 3.09 November 2008
Unit-Stride Access
Large data sets typically require unit-stride access to ensure that all data pulled in by a prefetch
instruction are actually used. Large data sets make use of all data that are read from memory, rather
than using only a sparse subset of the memory. If necessary, you should reorganize algorithms or data
structures to allow unit-stride access. For a definition of unit-stride access, see “Definitions” on
page 88.
Hardware Prefetcher Optimizations
Previous AMD64 processors prefetched data into the L2 cache. In AMD Family 10h processors, the
data hardware prefetcher loads data into the L1 cache. This hides the L2 cache access latency and
offers significant performance improvement. However, this slightly increases the risk of thrashing
and cache pollution, as discussed later in Section 5.5.
The hardware prefetcher in AMD Family 10h processors is a unit-stride prefetcher which trains on L1
cache misses and propagates on L1 cache accesses (hits and misses). Any two consecutive cache line
misses can train a hardware prefetch stream, and this generates a preset number of initial prefetch
requests. Any subsequent unit-stride accesses propagate the stream, causing more cache lines to be
prefetched. For example, if the preset value is 3 and there are L1 misses to lines I and I+1, the
prefetcher issues prefetch requests for lines I+2, I+3, and I+4. Subsequently, if a hit or miss to line
I+2 is seen, a prefetch request for I+5 is issued, and so on.
Adaptive Prefetching
In AMD Family 10h processors, a hardware optimization called adaptive prefetching is implemented
to improve the timeliness of the prefetches. This mechanism basically kicks in if the demand stream
catches up with the prefetch stream, and it adjusts the prefetch distance dynamically to maintain a
good prefetch distance. Here, a good prefetch distance is defined as the number of cachelines the
prefetch stream needs to stay ahead, such that the demand stream hits on prefetched lines in the L1
cache. Again, the hardware has a preset maximum fetch-ahead distance that controls this dynamic
scheme. As part of this adaptive scheme, the hardware prefetcher in AMD Family 10h processors
maintains pending prefetch request counters and adjustable distance counters. This helps the
prefetcher to scale with different memory technologies.
Note that in AMD Family 10h processors, the hardware prefetcher trains on software prefetch
requests (including the NTA type).
Contraindications for Prefetching
There are situations in which careless software prefetching can hurt performance.
•Thrashing—This is potentially the worst scenario. Thrashing occurs if more than two arrays are
prefetched in parallel and the addresses are separated by whole multiples of 32K bytes (the L1
cache size divided by the associativity). When this occurs, some of the prefetched data evicts
other prefetched data before it can be used. This is inefficient even without prefetching—which
simply makes the situation worse. Thrashing can be particularly bad if PREFETCHNTA is used.
82Cache and Memory OptimizationsChapter 5
Loading...
+ hidden pages
You need points to download manuals.
1 point = 1 manual.
You can buy points or you can get point for every manual you upload.