arrow_back Championship Value Prediction 1 Public Traces
Dataset usage instructions
Description
The following traces are 135 "public" execution traces that were generated for the Championship Value Prediction 1 (CVP-1) that took place with the International Symposium on Computer Architecture (ISCA) 2018. There traces were generated by Arthur Perais using Qualcomm Datacenter Technologies (QDT) internal tools, and contain instructions executed by workloads of interest to CPU design. As a result, the traces only contain partial information and are anonymized, in the sense that the program from which a given trace was generated is not available. All traces used in CVP-1 were released to the public domain after CVP-1.
This release contains :
- ./public_traces : 135 traces containing 30M instructions, known as the public traces as this is what was released to CVP-1 contestants to develop their model
License
Attribution 4.0 International (CC BY 4.0)
What Should I Use Those Traces for ?
The traces only contain partial information. As a result, they may not be adapted to your needs. Following are typical use cases for traces and whether the traces are adapted for that use. Each particular use is then detailed below. Some issues are fixed by an existing trace reader (https://gricad-gitlab.univ-grenoble-alpes.fr/tima/sls/projects/cvptracereader) that can serve as a base example on how to use the traces for your own purpose.
| I want to... | CVP-1 Traces Adapted ? | Caveats | Fixable (online means when reading the trace, offline means traces could be updated with fixed information) | Example of fix available | |:----------------------------------------------------:|:----------------------:|:----------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------:|:------------------------:| | do a functional conditional branch prediction study | Yes | - | - | - | | do a functional indirect branch prediction study | Yes, but | A handful of instruction have incorrect target information in the traces, ind branches are not well categorized | Yes, partially (online and offline) : target is fixable, categorization can be done mostly correctly | Yes for fixing targets (online) | | do a functional value prediction study | Yes | - | - | - | | do a functional cache study | Yes, but | Encoded memory access size is not sufficient, and only virtual addresses are in the trace | Yes, partially (online and offline) : correct memory accesses can generally be inferred | Yes (online) | | feed the trace to a timing simulator | Yes, but | Need to handle load/store with pre/post-increment addressing, need to annotate dependency on flag register | Yes, partially (online and offline) : correct addressing mode can generally be inferred, flags can be annotated | Yes (online) | | do a prefetching study | Yes, but | Same as timing simulator, and only virtual addresses are in the trace | Same as timing simulator | Same as timing simulator | | consider system activity in my study | Yes, but | Privilege changes are not annotated | Yes, partially (online and offline) : Obvious syscall/sysret can be annotated | No
I Want to Use the Traces for Functional Branch Prediction Studies
The traces have correct information for conditional branches (taken, target, type). However, consider that the traces have instructions from userspace as well as kernel space and only consider virtual addresses. As a result, multiple processes may use the same PC for different branch instructions.
I Want to Use the Traces for Functional Indirect Branch Prediction Studies
The traces have mostly correct information. The main issue is that the target of indirect calls to x30 (identified in the trace as uncondIndirectbranchInstClass, source register 30, destination register 30) is incorrect in the traces, as it is always the PC of the call instruction + 4 (that is, the return address stored in the ARMv8 link register). The example trace reader corrects this by keeping a window of two instructions, and overwriting the target of such indirect calls to x30 with the PC of the next instruction from the trace. Note that this fix will fail if the trace goes into an interrupt handler or exception exactly at the indirect call to x30.
Moreover, the traces do not differentiate between indirect calls, returns, and regular indirect branches. To categorize, one might look at the source and destination registers, although correct categorization is not guaranteed since an indirect branch to the link register may not be a return :
- If there is a destination register, it is a call (and the destination register has to be x30)
- If there is no destination register and the source is x30 (ARMv8 link register), it is likely a return.
- If there is no destination register and the source is not x30, it is likely a regular indirect branch.
I Want to Use the Traces for a Functional Value Prediction Study
This is what the traces were made for, so no issue here. Beware of instructions that have multiple destination registers or that have 16B destination registers.
I Want to Use the Traces for a Functional Cache Study
The issue is that the trace only contains the transfer size, which is the memory access size for one source or destination register. For instance, ldr x2, [x3, #8]! has two destinations registers (2 and 3), but does a single memory access, hence the true memory access size is the one encoded in the trace, 8, times the number of registers getting data from memory, hence 8. Conversely, ldp x2, x3, [x4] also has two destinations registers (2 and 3 as well), but it does two memory accesses, and so the true access size is the one encoded in the trace, 8, times the numbers of registers getting data from memory, hence 16. A similar issue exists for store instructions. As a result, without post processing, the access size associated to memory instructions is not always correct. This is especially problematic if it looks like the instruction is a regular load accessing the last bytes of a cache line, when in reality, it is a load pair that is also accessing the first bytes of the next cache line.
The example trace reader corrects this by attempting to infer the addressing mode of load/store instructions, Note that this inference is best effort as it is not always possible to get the exact answer. This allows to differentiate regular load/store from load pair/store pair, for which the true memory access size is 2x the one encoded in the trace.
Note however that the traces only have virtual addresses, and that ASID/EL is not available, such that it is impossible to tell if : - Two memory instructions accessing different virtual addresses are actually accessing the same physical address - Two memory instructions accessing the same virtual address are actually accessing different physical addresses
I Want to Feed the Traces to a Timing Simulator
The main issue comes from ARMv8 pre/post increment addressing modes. Those mode update the base register used to compute the effective address with an immediate as part of the memory instruction. For instance, ldr x2, [x3, #8]! will load into x2 the content of memory location x3 + 8, but it will also update x3 with x3 + 8. Thus, the instructions has two destination registers, but only x2 gets data from memory. As a result, there is no reason for the base register update to have the same execution latency as the memory access. In other words, in a timing simulator, x3 should be available after a single cycle (or the ALU latency) while x2 should be available after the memory hierarchy access latency. Making x3 available after the memory hierarchy latency (even best case load-to-use) will significantly hinder performance, as was discovered when revamping the CVP-1 simulator for CVP-2.
For instance, consider a naive memcpy implementation (x3 points to source array, x4 to destination array, x5 is 8 past end of source array) :
start:
ldr x1, [x3], #8
str x1, [x4], #8
sub x6, x3, x5
cbnz x6, start
If the increment of x3 takes load-to-use cycle, then memcpy will only load 8B every load-to-use cycles, whereas the expectation would be loading 8B each cycle. To address this issue, the trace reader attempts to infer the addressing mode of load/store instructions, Note that this inference is best effort as it is not always possible to get the exact answer. This allows to mark the destination register that is the base address register as such for memory instructions using BaseUpdate addressing, so that a timing simulator can apply the correct latencies.
A second issue is that since the flag register is not a general purpose register, it is not present as a source/destination register in the trace. To properly establish dependencies, the tracer adds the flag register as the destination of all integer instructions with zero destination registers, and as source of all conditional branches. This is slightly pessimistic as syscall instructions will be marked as generating the flags, but those are fairly rare.
A third issue which is covered by the addressing mode reverse engineering and in the "I Want to Use the Traces for a Functional Cache Study" section is that for accurate Store To Load Forwarding in a timing simulator, the correct access size of instructions should be available, which is not always the case in the trace as the traces only contain the transfer size.
I Want to Do a Prefetching Study
Those studies usually require some amount of timing, hence the issues are the same as with "I Want to Feed the Traces to a Timing Simulator". This compounds with the fact that only virtual addresses are available in the traces.
I Want to Consider System Activity in my Study
The traces contain both userspace and kernelspace information. In fact, instructions from multiple userspace processes may exist in the same trace. Unfortunately, privilege changing instructions (syscall/sysret) are not annotated, and the ASID/EL associated to each instruction is not present in the trace. This means that there is no obvious generic way to delineate privilege boundary crossings and process boundary crossings. Obvious ones may be annotated e.g. when a non branch instruction is followed by an instruction with PC different from PC + 4. However, this could also simply be an interrupt or exception handling for a page fault and not an actual system call.
I Want to Use the Traces but I Do Not Know here to Start : Trace Format
An exemple trace reader is provided here : https://gricad-gitlab.univ-grenoble-alpes.fr/tima/sls/projects/cvptracereader.
Trace Format
The traces contain ARMv8 instructions that have been stripped from information to anonymize the traces. Each instruction is encoded in the trace using the following binary format :
| Field | Size | Comment | |:------------------------------------------:|:--------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------:| | PC | 8 bytes | PC only (no ASID/EL), virtual address | | InstClass | 1 byte | - | | Effective Memory Address (Load/Store only) | 8 bytes | Virtual address | | Access size (Load/Store only) | 1 byte | Access size of a single register. If instruction loads multiple registers, multiply by that number. | | Branch direction (branch only) | 1 byte | Also present for unconditional branches | | Branch Target (taken branch only) | 8 bytes | Virtual address. Incorrect for indirect calls that jump to x30 (Armv8-A link register), needs to be fixed by the reader. | | Number of input registers | 1 byte | | | Identifier of input registers | 1 byte each | | | Number of output registers | 1 byte | | | Identifier of output registers | 1 byte each | | | Value of output registers | 8 bytes if general purpose, 16 bytes if SIMD | |
InstClass
enum InstClass : uint8_t
{
aluInstClass = 0,
loadInstClass = 1,
storeInstClass = 2,
condBranchInstClass = 3,
uncondDirectBranchInstClass = 4,
uncondIndirectBranchInstClass = 5,
fpInstClass = 6,
slowAluInstClass = 7,
undefInstClass = 8
};
Register Identifiers
| Identifiers | Register Type | |:-----------:|:---------------:| | 0 to 31 | General Purpose | | 32 to 63 | SIMD | | 64 | Flags (NZVC) |
#Missing/Incorrect Information
As the traces have been anonymized, important information has been removed, which prevents the traces from being useful in all cases. The trace reader attempts to infer back some of the missing information.
Missing Flag Register Source/Destination
Since the flag register is not a general purpose register, it is not present as a source/destination register in the trace. To properly establish dependencies, the tracer adds the flag register as the destination of all integer instructions with zero destination registers, and as source of all conditional branches. This is slightly pessimistic as syscall instructions will be marked as generating the flags, but those are fairly rare.
Indirect Call to ARMv8 Link Register (x30)
The target of indirect calls to x30 (identified in the trace as uncondIndirectbranchInstClass, source register 30, destination register 30) is incorrect in the traces, as it is always the PC of the call instruction + 4 (that is, the return address stored in the link register).
The trace reader corrects this by keeping a window of two instructions, and overwriting the target of such indirect calls to x30 with the PC of the next instruction from the trace. Note that this fix will fail if the trace goes into an interrupt handler or exception exactly at the indirect call to x30.
Memory Access Size and Addressing Mode
A first issue is that the trace only contains the transfer size, which is the memory access size for one source or destination register. For instance, ldr x2, [x3, #8]! has two destinations registers (2 and 3), but does a single memory access, hence the true memory access size is the one encoded in the trace, 8, times the number of registers getting data from memory, hence 8. Conversely, ldp x2, x3, [x4] also has two destinations registers (2 and 3 as well), but it does two memory accesses, and so the true access size is the one encoded in the trace, 8, times the numbers of registers getting data from memory, hence 16. A similar issue exists for store instructions.
A second issue is that the trace does not contain the addressing mode, which is problematic as ARMv8 features a pre/post-increment addressing mode that updates the base register used to compute the effective address with an immediate as part of the memory instruction. For instance, ldr x2, [x3, #8]! will load into x2 the content of memory location x3 + 8, but it will also update x3 with x3 + 8. Thus, the instructions has two destination registers, but only x2 gets data from memory. As a result, there is no reason for the base register update to have the same execution latency as the memory access. In other words, in a timing simulator, x3 should be available after a single cycle (or the ALU latency) while x2 should be available after the memory hierarchy access latency.
To address the two issues, the trace reader attempts to infer the addressing mode of load/store instructions, Note that this inference is best effort as it is not always possible to get the exact answer. This allows to differentiate regular load/store from load pair/store pair, for which the true memory access size is 2x the one encoded in the trace. This also allows to mark the destination register that is the base address register as such for memory instructions using BaseUpdate addressing, so that a timing simulator can apply the correct latencies.
The following heuristic (also commented in trace reader) is used :
Loads
- Case 1 : If a load has a single destination register
- Case 1.1 : Load has single source register, then the load is using
BaseImmediateOffset. True access size = 1 x trace access size. - Case 1.2 : Load has two source registers
- Case 1.2.1 : Destination register is among source registers, then load is using
BaseRegOffsetaddressing and happens to overwrite one of the two source regs. This is not base update addressing. True access size = 1 x trace access size. - Case 1.2.2 : Destination register is not among source registers, then load is using
BaseRegOffsetaddressing. True access size = 1 x trace access size.
- Case 1.2.1 : Destination register is among source registers, then load is using
- Case 2 : If a load has two destinations registers
- Case 2.1 : Load has a single source register that is also a destination register
- Case 2.1.1 : If the value of said source/destination register is within
[EA+minImmOffset;EA+maxImmOffset], then it is likely usingBaseUpdateaddressing. True access size = 1 x trace access size. - Case 2.2.1 : If the value of said source/destination register is not within
[EA+minImmOffset;EA+maxImmOffset], then this is likely a load pair usingBaseImmediateOffsetaddressing. True access size = 2 x trace access size.
- Case 2.1.1 : If the value of said source/destination register is within
- Case 2.2 : If the source register is not also a destination register, then this is a load pair using
BaseImmediateOffsetaddressing. True access size = 2 x trace access size. - Case 3 : If a load has three destination registers, then the load is a load pair and uses
BaseUpdateaddressing. True access size = 2 x trace access size. - Case 4 : If a load has zero destination registers, then it is a prefetch instruction and is marked as such by using
Prefetch"addressing mode".
Stores
- Case 1 : If a store has a single source register.
- Case 1.1 If access size is 64B, it is DCZVA (zeroing cache line). Note that no alignment is required for the address, hence we have to fix the address of the first byte so this does not become a cache line crossing access. Addressing mode is marked as
DCZVAin the trace reader. True access size = 1 x trace access size - Case 1.2 : If a store has a single source register and not 64B access size, then we mark it as
BaseImmediateOffset. True access size = 1 x trace access size. - Case 2 : If a store has two source registers, it is either
- Case 2.1 : No destination register, the store is
BaseImmediateOffset. True access size = 1 x trace access size. - Case 2.2 : Single destination register that is not also a source, the store is
BaseImmediateOffset. True access size = 1 x trace access size. - Case 2.3 : Single destination register that is also a source, the store is
BaseUpdate. True access size = 1 x trace access size. - Case 3: If a store has three source registers
- Case 3.1 : Store has one destination that is also in the sources, it is
BaseUpdatestore pair. True access size = 2 x trace access size. - Case 3.2 : Store has no destination
- Case 3.2.1 : If value of base reg is within
[EA+minImmOffset;EA+maxImmOffset], this is a store pair usingBaseImmediateOffset. True access size = 2 x trace access size. - Case 3.2.2 : If value of base reg is not within
[EA+minImmOffset;EA+maxImmOffset], this is a regular storeBaseRegOffset. True access size = 1 x trace access size.
- Case 3.2.1 : If value of base reg is within
Instruction Categorization
The trace makes obvious efforts to categorize instructions broadly. This makes finding privilege boundaries hard, because syscall/sysreturn and exceptions are not marked. This reader does not improve on this particular aspect, but a general heuristic would be to mark any aluOp whose next instruction is not PC + 4 as a syscall or sysreturn (at least a privilege changing instruction).