{'fraction_non_alphanumeric': 0.03611363341548839, 'fraction_numerical': 0.017252561940588922, 'mean_word_length': 4.856643622274557, 'pattern_counts': {'":': 0, '<': 0, '<?xml version=': 0, '>': 1, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
Enabling High-Capacity, Latency-Tolerant, and Highly-Concurrent GPU Register Files via Software/Hardware Cooperation

Graphics Processing Units (GPUs) employ large register files to accommodate all active threads and accelerate context switching. Unfortunately, register files are a scalability bottleneck for future GPUs due to long access latency, high power consumption, and large silicon area provisioning. Prior work proposes hierarchical register file to reduce the register file power consumption by caching registers in a smaller register file cache. Unfortunately, this approach does not improve register access latency due to the low hit rate in the register file cache. In this paper, we propose the Latency-Tolerant Register File (LTRF) architecture to achieve low latency in a two-level hierarchical structure while keeping power consumption low. We observe that compile-time interval analysis enables us to divide GPU program execution into intervals with an accurate estimate of a warp's aggregate register working-set within each interval. The key idea of LTRF is to prefetch the estimated register working-set from the main register file to the register file cache under software control, at the beginning of each interval, and overlap the prefetch latency with the execution of other warps. We observe that register bank conflicts while prefetching the registers could greatly reduce the effectiveness of LTRF. Therefore, we devise a compile-time register renumbering technique to reduce the likelihood of register bank conflicts. Our experimental results show that LTRF enables high-capacity yet long-latency main GPU register files, paving the way for various optimizations. As an example optimization, we implement the main register file with emerging high-density high-latency memory technologies, enabling 8X larger capacity and improving overall GPU performance by 34%.

. While such proposals increase capacity without sacrificing power consumption, they typically result in higher register access latencies.
Register file caching [49,50] is a promising approach to enhancing capacity while lowering power consumption and effective access latency. Unfortunately, existing proposals for register file caching do not achieve high enough hit rates in the register cache due to three key problems. First, the high degree of thread-level parallelism (TLP) in GPUs causes threads to displace each other's registers in the cache. Second, registers house temporary values that are often renamed, which reduces temporal locality in the cache. Third, because register names are not spatially correlated, there is no spatial locality in a register cache. Due to these reasons, register file caching is ineffective at hiding latency in GPUs ( § 7).
Our goal is to improve the effectiveness of register file caching in GPUs. To this end, we observe that registers can be effectively prefetched into the register cache using compile-time interval analysis to hide the long access latency of the main register file. An interval is a subgraph in a program's control-flow graph that has a single entry point. Intervals have been widely used by optimizing compilers to identify loops [58]. We use interval analysis and software prefetching to fetch the entire set of required registers of an interval into the register cache and thus avoid the main register file access latency during the execution of the interval.
We propose the Latency-Tolerant Register File (LTRF), a two-level hierarchical register file that employs a low-latency/low-power first-level register file cache backed up by a high-latency/high-capacity second-level main register file. LTRF uses a compiler-driven software mechanism to prefetch a warp's register working-set into the register cache at the start of an interval. By fetching all registers in the working-set together and overlapping the prefetch latency of one warp with the execution of another, LTRF hides a substantial fraction of the access latency of the main register file during the execution of the interval.
To accelerate register prefetching operations, it is crucial to avoid main register file bank conflicts. As the main register file banks are single ported, the bank conflicts increase the prefetch latency significantly. To resolve the main register file bank conflicts, we also devise a compile-time register renumbering technique on top of LTRF, while preserving the correctness of the program.
By using LTRF, we enable high-capacity yet long-latency main register files, paving the way for various optimizations. As an example optimization, we implement the main register file with high-density emerging memory technologies, e.g., domain wall memory [6,9,116,142,164,178,182], enabling 8× larger capacity and improving overall GPU performance by 31% while reducing register file power consumption by 46%. In contrast, the state-of-the-art register file caching schemes reduce GPU performance by 14%, on average, if the register file is enlarged by 8×, as prior designs do not focus on tolerating the latency of the main register file. This paper makes the following contributions: • We show that prior proposals for register file caching do not achieve high enough hit rates to effectively hide the long latencies of large main register files ( § 7). • We introduce LTRF, a latency-tolerant hierarchical register file design, which enables high-capacity yet long-latency main register files. The key idea is to 1) estimate the register working set of a program's execution during an interval, using compile-time interval analysis, 2) prefetch the estimated register working-set from the main register file to the register file cache under software control, at the beginning of each interval, and overlap the prefetch latency with the execution of other warps. • We devise a compile-time register-renumbering technique to mitigate the overhead of register bank conflicts in register prefetching operations. • Our evaluations show that an optimized version of LTRF, when implemented with an 8× larger yet 6.3× slower main register file, improves overall GPU throughput by 31%, on average (up to 86%). LTRF performance is within 5% of an ideal 8×-capacity main register file that has no latency overhead. Figure 1 illustrates a conventional GPU register file architecture [106] in a streaming multiprocessor (SM). To accommodate a large number of active threads, a GPU employs a register file of megabytes in size. For example, GP100 (NVIDIA Pascal) has a register file of 14.3 megabytes in total [139]. The register file is heavily banked (16 banks) and it allows concurrent accesses from many threads (up to 512 threads). Each bank stores registers from multiple warps. When the GPU issues an instruction, an operand collector concurrently accesses and gathers data associated with each thread in the issued warp's instruction through an arbiter and a large and wide crossbar, as shown in Figure 1. The warp scheduler arbitrates among ready warps (i.e., a warp whose operands are collected) and issues the warp's instruction to the SIMD units.  In this section, we demonstrate the increasing demand for larger register file capacity, analyze shortcomings of prior register caching mechanisms for GPUs, and motivate the case for a design that provides high capacity without significantly increasing power consumption, on-chip die area, or access latency exposed to the GPU core.

Factors that Limit GPU Performance
When a warp encounters a long-latency memory instruction, the GPU selects another ready warp to be scheduled for execution, in order to prevent the GPU core from stalling. While the applications with high TLP are more likely to contain more ready warps and are able to hide long-latency stalls more effectively, these applications with high TLP demand a large register file in order to realize their maximum TLP. To illustrate the impact of the register file size on an application's TLP, we recompile 35 workloads in CUDA SDK [15], Rodinia [31], and Parboil [174] benchmark suites with the maxregcount attribute (i.e., the attribute that enables the use of the maximum number of registers for each GPU function, i.e., 64 and 256 for Fermi and Maxwell respectively) enabled in the NVIDIA GPU compiler, nvcc. Doing so enables us to measure the number of registers applications would require if there were no register file size constraints. Table 1 reports the average and maximum register file capacity needed for our benchmarks to achieve the maximum TLP provided by the two GPU products. This experiment shows that a larger register file would directly translate into a larger number of executing threads, thereby increasing TLP, on average. The table corroborates our intuition that TLP is indeed limited by the number of registers and many applications benefit from compiler optimization when given a larger register file [115,126,194]. The results also show that the recent version of the CUDA compiler used for Maxwell employs more aggressive compiler optimization techniques (e.g., loop unrolling) and as such enhances register usage and TLP compared to Fermi.

Register File Scalability
While modern GPUs integrate more execution resources with increases in silicon density and memory bandwidth in each chip generation, the register file accounts for an increasingly larger fraction of on-chip storage, as shown in Figure 2. For NVIDIA Pascal [139], more than 60% of the on-chip storage area, amounting to 14.3 MB is dedicated to the register file. GPU register files face the difficult challenge of optimizing latency, bandwidth, and power consumption, while having maximal capacity [1,49,50,59,65,69,70,99,104,108,109,116,160,161,185,187,188]. Larger register files are slower, take up more silicon area and consume more power. Increasing concurrency by adding more banks exacerbates complexity and power consumption with the addition of a larger crossbar. Prior work attempts to reduce the power consumption of the register file while keeping the register access latency almost unchanged. As a result, the reduction in the power consumption is limited by the access latency of the register file. In this section, we measure the impact of various register file design parameters and configurations on register file access latency and overall GPU throughput.  Table 2 illustrates register file designs with varying parameters, including cell technology, number of banks, bank size, and network topology, relative to a baseline high performance SRAM-based design shown in Configuration #1. The table also presents results for emerging memory cell technologies that enable a larger trade-off space between area, power and latency. We use high-performance (HP) CMOS, low-standby-power (LSTP) CMOS, tunnel-field-effect transistors (TFET), and domain-wall memory (DWM) for the cell technology [6,9,23,41,48,101,104,116,123,125,142,151,164,167,179,182,183,189]. To obtain these results, we first use CACTI [125] (the non-pipelined register file bank models) and NVSim [41] to extract timing, area and power, and then feed them as parameters to GPGPU-Sim [15] to measure the average register file access latencies. The results include queuing delays incurred due to bank conflicts (our system configuration is presented in § 6). Note that we use the flattened butterfly topology [85] to reduce the overhead of the crossbar network when we increase the number of banks by 8× in our implementations. We make two key observations from Table 2. First, register file designs (such as design #7) that minimize area and power consumption while optimizing for capacity (i.e., bits/area) exhibit higher access latency. Second, while some alternative cell technologies (e.g., DWM [116,182]) can dramatically improve capacity and power consumption, they incur prohibitively long access latencies (e.g., as long as 6.3× compared to the baseline register file).
To illustrate the potential benefit of using a large register file, Figure 3(a) plots performance (in IPC) for a high-capacity register file in the ideal case, which has the same latency as the baseline register file from Table2. We categorize our workloads into two groups: register-insensitive and register-sensitive. Register-insensitive workloads are the ones where the register file size is not the bottleneck for higher TLP; i.e., increasing the register file size does not improve TLP. We observe that increasing the register file size from 256KB to 2MB without increasing the register file access latency, improves IPC by 10%-95% (37%, on average) for register-sensitive workloads. We find that the IPC improvements are due to both more registers per thread and more warps executing in parallel. Figure 3(b) plots performance (in IPC) for a high-capacity register file implemented using TFET-SRAM, normalized to the baseline register file from Table 2. 1 We observe that when real latencies are modeled, much of the gain from higher capacity and TLP is offset by higher latency, and overall performance reduces despite the higher register file capacity. We conclude that register file access latency is important for performance and should be kept in check while increasing register file capacity.

Register File Caching
One method to increase the size of the register file while keeping access latency low is to cache registers in a smaller structure, i.e., register file caching. Although there is significant previous work on register file caches for CPUs [21,37,39,73,122,136,140,166,177,197,198], and vector processors [24,77,155], register file caching has not been thoroughly investigated in GPU designs. Gebhart et al. [49] are the first to introduce register file caches for GPUs to filter some of the accesses to the main register file and thus reduce the dynamic access energy of the main register file. The authors' design works almost the same way as a conventional cache structure and exploits temporal locality. However, as Figure 4 shows, for a 16KB register cache, the register cache hit rate is low: between 8% and 30%.
We find that the hardware register cache hit rate is low due to the following reasons: (1) Different warps can displace each other's registers in the cache due to the high warp switching rate in GPUs. This thrashing effect is also observed in SMs' local data caches [10, 12-14, 49, 63, 64, 71, 72, 78, 80, 81, 98, 103, 118, 120, 121, 135, 153, 159, 162, 163, 180, 192, 193]. (2) We find that many registers are used to only communicate results between a few instructions. As a result, these registers do not have good temporal locality. (3) There is no notion of "spatial locality" in register accesses (i.e., there is no logical order among different registers). Follow-up work [50] proposes a software-managed hierarchical register file (SHRF) that aims to reduce data movement between the main register file and the register cache. However, as the main objective is to reduce dynamic energy consumption of the baseline large monolithic register file, the authors [50] aim to reduce the    [49] and software [50] register file caches.
total number of accesses to the main register file, regardless of whether or not those accesses occur during the execution of a warp. In particular, SHRF reduces the extra register file accesses caused by register file cache write-back/reloads, by adding specialized instructions, aided by a new register allocation mechanism, to manage register movement. However, Figure 4 shows that the software approach does not significantly improve the hit rate compared to a baseline hardware register cache [49] as it mostly focuses on reducing the number of background (i.e., write-back/reload) register accesses, rather than accesses that are needed by program instructions.

Summary and Goals
In this work, we leverage two observations we have provided in this section. First, the register file is one of the limiting factors in the scalability of GPUs in terms of TLP. Second, making the register file considerably larger is very difficult without sacrificing either latency or power consumption. Register caching can reduce the register access latency and thus enable aggressive power optimization techniques without degrading GPU performance. However, register caching has not been thoroughly studied in the context of GPUs and the existing schemes mainly aim to reduce power consumption, rather than completely hide the main register file access latency. Therefore, these designs are inefficient as they do not offer high register cache hit rates. In fact, they hurt performance if used with considerably slow main register files (see § 7).
In this paper, we aim to architect a latency-tolerant hierarchical register file for GPUs that can have very high capacity. Our goal is to 1) enable very high-capacity yet also high-latency main register files, while improving performance, and thus 2) open the design space for many power/area optimization techniques in the main register file that likely increase the register access latency (and thus would otherwise be unacceptable).

LATENCY-TOLERANT REGISTER FILE
To make the register file very high capacity and at the same time latency-tolerant, we propose a new register file caching mechanism that aims to 1) bring the warps' registers into the register file cache before they are accessed by the warps (i.e., register prefetching) and 2) service all register accesses from the register file cache. As a result, the warps see the latency of a fast register cache and not the slow main register file. We find that a near-perfect register prefetching mechanism can be implemented based on two key observations. First, the register working-set is known at compile-time as there is no indirection or aliasing in register accesses. Second, long register access latency can be hidden by the execution of other active warps.
LTRF takes advantage of these two observations that enable a new register prefetching scheme. § 3.1 provides an overview of our register prefetching scheme. § 3.2 and § 3.3 provide an overview of the architectural and compiler support required for our software-driven prefetching scheme, respectively.

Register Prefetching Scheme
We define a prefetch operation to specify which registers should be prefetched from the main register file. A prefetch operation brings the register working-set of a subgraph of the application control flow graph (CFG) into the register cache. The working-set is composed of the registers that, depending on the dynamic control flow, might be accessed between two prefetch operations. We call subgraphs of the CFG created by prefetch operations (bounded by prefetch operations) prefetch subgraphs. Finding an optimal placement of prefetch operations is not only impossible in polynomial time, but also requires information available only during runtime because of dynamic warp interleavings. We propose a heuristic algorithm that employs the concept of intervals [58], subgraphs of the CFG with a single entry point, which offers compile-time analysis within a reasonable amount of time. We modify the classic interval analysis algorithm, used to find the subgraphs of the CFG with a single entry point, and introduce the concept of register-intervals as suitable prefetch subgraphs for prefetching registers. A register-interval is a subgraph of the CFG that 1) has a single control flow entry point and 2) requires, at most, a given number of registers.
Our scheme brings the register working-set into the cache at the beginning of each register-interval and guarantees that all register accesses made inside that register-interval will be serviced from the register file cache. See § 3.2 and 3.3 for more details about the proposed register prefetching scheme.

Architectural Support
To reduce the register file cache size, we limit the number of active warps that run concurrently and maintain a pool of inactive warps; the inactive warps remain dormant and are not allocated space in the register file cache. Furthermore, we partition our register file cache and allocate each partition to an active warp, thus preventing active warps from contending for register file cache space, and thus from evicting each other's registers. We size the dedicated caching space for each warp according to the maximum number of registers the warp can access throughout the execution of a prefetch subgraph. This parameter also sets an upper bound for the size of a prefetch subgraph working-set. By ensuring no register cache evictions occur during the execution of a prefetch subgraph, we guarantee that register movement happens only with prefetch operations or when a warp becomes active/inactive. We deploy a two-level warp scheduler, similar to the one used in [49,134], to schedule execution of active warps. The scheduler issues instructions from active warps in a fair manner (e.g., round-robin). Whenever a warp encounters a long latency operation, such as a data cache miss, it becomes inactive and gets replaced by another one from the active pool. The two-level scheduler enables the use of a smaller register file cache that needs to accommodate only the working-sets of the active warps, and a warp's register working-set is swapped in and out of the register file cache as warp becomes active/inactive.
Reducing the number of active warps provides two positive benefits: it 1) does not limit TLP since inactive warps still maintain live state in the main register file, and thus can be quickly activated, 2) can potentially improve performance by reducing the L1 data cache thrashing effect and by preventing all warps from stalling at the same time [78,80,134,153,163]. In LTRF, warp activations are not cost-free as the register working-set of the inactive warp needs to be prefetched before the warp becomes active. Hence, if we cannot hide the warp activation latency, we might negatively affect performance. In § 7.2, we quantitatively show that this is not the case. LTRF requires a small number of active warps to hide the warp activation latency, allowing a GPU to tolerate higher latency accesses to the main register file. 2 Prefetch operations use bit-vectors to identify the registers that should be cached for each prefetch subgraph, enabling support for various cache sizes. The prefetch bit-vector size is equal to the maximum number of registers the CUDA compiler can allocate to a thread. For example, in the latest CUDA versions, the compiler can allocate up to 256 registers to each thread, requiring a 256-bit vector for each prefetch operation. The instruction fetch unit needs to know in advance when it is going to process a prefetch bit-vector. We consider two approaches. The first embeds an extra bit in each instruction to indicate whether a prefetch bit-vector follows that instruction. Prior work [50] has similar requirements and the authors show that, in general, the cost of embedding the extra bit is negligible. The second approach is to add an explicit instruction that is always followed by the bit-vector. 3 When a warp becomes inactive, we must keep track of which registers should be written back and refetched once the warp becomes active again. In LTRF, we simply write back and refetch the entire register working-set of the active prefetch subgraph.
In order to improve the efficiency of the basic LTRF design, we devise operand-liveness aware LTRF (called LTRF+), which considers the liveness of the registers to save register file cache space. The key idea of LTRF+ is to avoid writing-back/re-fetching dead registers. To this end, each read operand has to be extended with an additional bit, called the dead operand bit as defined in [49], which indicates whether the corresponding operand will be dead after the execution of the corresponding instruction. This information can be conservatively known at compile-time, using static liveness analysis. These bits are used to update the liveness bit vector. The liveness bit vector keeps track of the liveness status of all registers at the current point of execution. A register becomes live when it is written to and dead when an instruction indicates it is dead via the dead operand bit. When a warp becomes inactive, LTRF+ writes back only the live registers to the main register file. When a warp becomes active, LTRF+ fetches only the live registers from the main register file. LTRF+ does not read the dead registers from the main register file since their first access, if any, will be a write, and LTRF+ needs to only allocate space for them in the register file cache.

Compiler Support
When a warp reaches the beginning of a prefetch subgraph, it is paused until all of its working-set registers are loaded into the register cache. Therefore, prefetch operations may have long latencies that can potentially impose large performance overheads, and hence, they should happen infrequently. In order to address this issue, we introduce register-intervals as effective prefetch subgraphs and partition the CFG into register-intervals. A register-interval is a subgraph of the CFG with only two constraints. First, it needs to have only one control flow entry point. Second, the number of registers used in a register-interval should not exceed the size of a partition in the register cache. 4 The primary difference between register-intervals and other similar concepts, such as strands [50], is that complex control flow structures (e.g., backward branches) are allowed inside a register-interval and they do not cause the termination of the register-interval. By relaxing such constraints, register-intervals provide two main benefits. First, register-intervals can have more static instructions and thus the number of prefetch operations can be minimized. Second, our mechanism aims to fit a loop within a single register-interval in order to increase the dynamic length of the register-intervals.
We employ classic interval analysis methods [58] to form register-intervals. The original interval concept [58], used in classic compiler algorithms, partitions the CFG into smaller disjoint subgraphs, each with exactly one entry point. These intervals are typically used to identify loops and determine if the CFG is reducible. We constrain the formation algorithm to guarantee that the register working-set of each interval can fit into a register file cache partition. As a result, the register-intervals constructed by our algorithm might be smaller than the intervals formed by the original algorithm and may terminate at arbitrary points. Thus, we modified the original algorithm to construct intervals at arbitrary starting points.
Our register-interval formation algorithm is a multi-pass algorithm. Algorithm 1 shows the first pass. The algorithm tries to compose register-intervals with as many basic blocks as possible. Therefore, it initializes the first register-interval with the entry basic block (line 8) and iteratively attempts to add subsequent blocks to it (lines 9-25). A candidate block must satisfy two conditions to be successfully added: 1) it must be entered only from the current register-interval, 2) the register file cache space allocated for a warp must be enough to house both the active registers already in the register-interval and the ones added by the new block. The algorithm stops when it cannot find any basic blocks that meet these conditions (line 13). After it finishes the first register-interval, it creates new register-intervals out of all the basic blocks with incoming edges from that register-interval (lines [18][19][20][21][22][23][24]. When a register-interval is completely formed, all of the basic blocks that have incoming edges from that register-interval become new register-intervals' headers. If a single basic block's active registers do not fit into the remaining register file cache space for that register-interval, the basic block is split across two or more register-intervals (lines [30][31][32][33][34][35][36][37]. We also split the basic blocks at function calls (each function call becomes a separate register-interval). Algorithm 1 is not multi-pass and ends when all basic blocks of the control flow graph are assigned to register-intervals. After executing Algorithm 1, the CFG is transformed into a Register-Interval CFG where the nodes represent the register-intervals rather than basic blocks. Algorithm 2 shows the second pass of our register-interval formation algorithm. This pass reduces the Register-Interval CFG into a smaller number of register-intervals. It works similarly to the first pass, with the difference that it never splits register-intervals. Instead, it merges two register-intervals if 1) one of them can be reached only from the other and 2) the union of their register working-sets still fits into the allocated register file cache space (lines 12-15). The second pass is repeated until the CFG can not be reduced anymore. After each pass of Algorithm 2, there are two possible scenarios: 1) the graph is not reduced, and 2) the graph is reduced. In the first scenario, Algorithm 2 terminates. In the second scenario, Algorithm 2 runs again on the reduced register-interval control flow graph. However, this cannot last forever, as in the worst-case (worst case in terms of algorithm execution time), the graph will be reduced to a one-node graph, which cannot be reduced more.

Input: Application Control Flow Graph (CFG)
Output: Register-Interval CFG 1: Initialize: 2: for each basic block : BB do 3: BB.input_list ← empty() // List of all register in the register cache at the begining of BB 4: BB.register-interval ← Unknown 5: end for 6: Working-Set ← empty() 7: entry_block.register-interval ← new register-interval() // Each CFG has an entry basic block 8: Working-Set.insert(entry_block) 9: while (!Working-Set.empty()) do 10: BB ← a basic block from Working-Set 11: TRAVERSE(BB) 12: i ← BB.register-interval 13: while (∃ basic block h for which h.register-interval==Unknown & all of h predecessors belong to i & union(output_list of all S predecessors).size()≤N) // N is the maximum number of registers allowed in the registerinterval (i.e., size of a partition in the register file cache) do 14: h.register-interval ← i 15: h.input_list ← union(output_list of all h predecessors) 16: TRAVERSE(h) 17: end while 18: for each S ∈ i.successors() do 19: if (S.register-interval==Unknown) then 20: S.register-interval ← new register-interval() 21: S.input_list ← empty() 22: Working-Set.insert(S) 23: end if 24: end for 25: end while 26: procedure traverse(BB) 27: register_list ← BB.input_list 28: for each instruction in BB do 29: update register_list 30: if (register_list.size()>N) then 31: cut BB and introduce a new basic block : BB1 32: BB1.register-interval ← new register-interval() 33: BB1.input_list ← empty() 34: Working-Set.insert(BB1) 35: BB.output_list ← register_list // List of all registers in the register file cache at the end of BB The only control flow constraint imposed by intervals is that a node can only join an interval when all of the incoming edges to the node come from that interval. As a result, backward edges and thus loop headers always create new intervals. 5 This key feature of intervals makes them ideal subgraphs for our purpose. By starting a Input: Register-Interval CFG Output: Reduced Register-Interval CFG 1: Initialize: 2: for each register-interval : i do 3: i.register-interval ← Unknown 4: end for 5: Working-Set ← empty() 6: entry_register-interval.next_level_register-interval ← new next_level_register-interval() 7: Working-Set.insert(entry_register-interval) 8: while (!Working-Set.empty()) do 9: i ← a register-interval from Working-Set 10: ii ← i.next_level_register-interval 11: ii.register_list ← i.register_list 12: while (∃ register-interval h for which h.next_level_register-interval==Unknown & all of h predecessors belong to ii & union(register_list of all h predecessors).size()≤N) // N is the maximum number of registers allowed in the register-interval (i.e., size of a partition in the register file cache) do 13: h.next_level_register-interval ← ii 14: ii.register_list ← union(ii.register_list & h.register_list) 15: end while 16: for each S ∈ ii.successors() do 17: if (S.next_level_register-interval==Unknown) then The primary role of the second pass is to prevent the mentioned control flow constraint from splitting large register-intervals into multiple smaller ones. As an example, consider the two nested loops in Figure 5. Assuming the entire register working-set of the graph fits in the register file cache, in the first pass, basic block "A" forms register-interval 1. Basic block "B" cannot be merged with register-interval 1 as it has another incoming edge from basic block "C". Therefore, basic block "B" forms a new register-interval, named register-interval 2. Basic block "C" can be merged into register-interval 2 as basic block "C" has only one incoming edge from register-interval 2. As a result, the innermost loop becomes a separate register-interval but it cannot be merged into the outermost loop. In the second pass, register-interval 1 can be merged into register-interval 2 as register-interval 1 has only one incoming edge from register-interval 2. Thus, the whole outermost loop can be reduced to a single register-interval. Each repetition of the second pass of the algorithm reduces the depth of a nested loop by one if the resulting register working-set is small enough to fit in the register file cache.
We open source the C implementation of Algorithms 1 and 2 in [53].

RESOLVING REGISTER BANK CONFLICTS
Register prefetching latency affects LTRF effectiveness because long latencies call for a higher number of active warps and larger register-cache capacities. Therefore, it is crucial to reduce the register prefetching latency in order to minimize LTRF's register-cache size. One of the main factors that greatly affects register prefetching latency is register bank conflicts during prefetching operations. When several registers of a register-interval reside in the same register bank, they cause register prefetching to happen serially, significantly increasing the register prefetching latency.
To evaluate the probability of register bank conflicts while LTRF prefetches registers' contents, we calculate the number of registers that reside in the same register bank in each register-interval. We assume that the maximum number of allowed registers in each register-interval is 16, and there are 16 main register file banks. A register-interval has no register bank conflict if all registers of its register working-set reside in different main register file banks. A register-interval has N register bank conflicts if at most N+1 registers reside in the same register bank. Figure 6 shows the distribution of the number of register bank conflicts in register-intervals for different register-insensitive and register-sensitive workloads. We make two key observations. First, about 60% and 80% of register-intervals have at least one register bank conflict for register-insensitive and register-sensitive applications, respectively. Second, we observe up to three register bank conflicts for our workloads. We conclude that we can greatly reduce the prefetching latency by resolving register-bank conflicts.
In the rest of this section, § 4.1 describes the register-bank conflict problem in more detail and provides the basic insight of our approach, which is careful register bank assignment to resolve register bank conflicts, § 4.2 presents our proposed mechanism to resolve register bank conflicts in register-intervals, and § 4.3 elaborates on our mechanism using an example.

Register Bank Assignment
Register allocation is one of the most important optimization phases in compilers that directly affects the performance of the generated code [4,25,29,55,147]. As the register file in a GPU is multi-banked, the register allocation mechanism should attempt to allocate the registers to different banks in order to benefit from multibanking. Prior attempts on compilers produce bank-conflict-free register accesses by performing a register bank assignment pass in addition to register allocation [91,143,200]. Prior works build a Register Conflict Graph whose nodes are live ranges of the program and two nodes are connected if there is a conflict between them, e.g., two source operands of an instruction. The problem we seek to solve is different. Our primary goal is to perform a bank assignment wherein all registers in the working-set of each register-interval reside in different register banks, hence decreasing the register prefetching latency. To specify our problem, it is necessary to define a key concept called register-liverange. A register-live-range is a chain of common uses of a specific register which specifies the liveness of the register in register-intervals. The main purpose of introducing register-live-ranges is to track the liveness of values and registers across different register-intervals. In other words, register-live-ranges enable us to guarantee the correctness of the final register bank assignment with respect to the live values in registers. The input to our problem is a set of register-live-ranges specified by the register-interval CFG. Our problem is to map each register-live-range to one of register banks in such a way that those with common register-intervals reside in different register banks.
We propose a new register bank assignment technique, called register renumbering, as a compiler optimization pass after the register allocation and register-interval formation phases. Our solution is analogous to prior bank assignment proposals. As mentioned before, register-live-ranges can be shared between different register-intervals and it is not straightforward to assign a register bank to each register-live-range. Hence, how we model our problem is important. We use graph coloring as a simplification abstraction. We model our bank assignment problem as a graph coloring problem which restricts register-live-ranges to reside in different register banks if they have a live value in the same register-intervals. The next section explains our mechanism in more detail. Figure 7 presents an overview of the compiler support of LTRF, including the register renumbering mechanism. As discussed before, the main goal of the register renumbering technique is to minimize register bank conflicts during prefetch operations as much as possible. This technique is employed at compile-time after register allocation and register-interval formation phases. The output of this optimization pass is register-intervals with minimal register bank conflicts. In some cases, register bank conflicts cannot be resolved unless we spill some of the register-live-ranges to main memory. We do not produce any spill code in the register renumbering phase, since the register prefetching latency would be increased significantly due to accessing the register-live-ranges spilled in main memory.

Register Renumbering Mechanism
We model our problem as a graph coloring problem. To this end, we build an appropriate graph, called the Interval Conflict Graph (ICG), containing all constraints of our problem. In ICG, the nodes are register-live-ranges, and the edges represent conflicts between adjacent nodes. In other words, two nodes are connected if the two corresponding register-live-ranges have live values in same register-intervals. If we assume the number of available colors is equal to the number of register banks in a register file, a valid ICG coloring is one where all adjacent nodes have different colors, i.e., conflicting register-live-ranges receive different colors. This coloring represents our desired bank assignment with minimal register bank conflicts.
Our register renumbering mechanism has four phases (see Figure 7): (1) Building Register-Live-Ranges. This phase identifies register-live-ranges as the chains of definitions and uses of registers. We then identify which register-live-range is included in which register-interval(s). Fig. 7. Overview of LTRF compiler support.

Input LTRF Compiler Passes Output
(2) Building ICG. Nodes of ICG are register-live-ranges and there is an edge between two nodes if they are included in at least one common register-interval. (3) Coloring ICG. This phase colors the ICG with colors, where is the number of register banks. The problem of k-coloring a graph is NP-complete [38,87]. Therefore, we use a heuristic algorithm for graph coloring to create the colored graph in a reasonable amount of time. The heuristic algorithm that we employ is devised by Chaitin et al. [29]. The algorithm requires ( + ) time, where is the number of register-liver-ranges and is the number of edges in the ICG [29]. This algorithm does its best to color the graph in a balanced manner, i.e., colors are almost equally used. This key feature of this algorithm enables us to perform a balanced bank assignment that minimizes the register bank conflicts. (4) Renumbering. The final phase of the algorithm is renumbering all registers based on their ICG color.
In this phase, we assume no register has been allocated to register-live-ranges and we have a set of free registers. Whenever we want to allocate a register to a register-live-range, we choose one of the free registers of the register bank specified by the colored ICG. This approach guarantees the correctness of the code as register-live-ranges contain all uses of a specific register. Note that each color in the colored ICG represents one of the register banks.

A Walk-Through Example
We elaborate on our register renumbering mechanism using a simple example. Listing 1 and Figure 8 show a PTX code example and the corresponding register-interval CFG, respectively. This code example compares two arrays with 100 elements and sets a register, i.e., R6, if all corresponding elements of the two arrays are the same. For the sake of simplicity, we assume that each register-interval can have up to four registers, and there are four main register banks, each containing two registers. Figure 8 shows that the prefetch operation of register-interval #2 prefetches four registers, including R0, R1, R4, and R5, which results in register bank conflicts as R0 and R1 are in register bank #0 and R4 and R5 are in register bank #2 (See the right side of Figure 8). As a result, performing the prefetch operation in register-interval #2 needs two serial register bank accesses. A similar scenario happens in register-interval #3. To resolve the register bank conflicts, we perform our register renumbering pass on this code. Figure 9 (bottom left side) shows the ICG of the example code in Listing 1. The ICG shows that, for example, R0 and R1 cannot reside in the same register bank as R0 and R1 are both live at the beginning of register-interval #2. We color the ICG with colors, i.e., four colors in the example. We consider green, blue, yellow, and red colors for the main register banks #0, #1, #2, and #3, respectively (See Figure 9 on top). Figure 9 (middle) shows the colored ICG specifying which bank each register should reside in to minimize register bank conflicts. We renumber each register according to its ICG color. Figure 9 (right side) shows the ICG after renumbering. For example, R1 is renumbered to R2 to resolve its bank conflict with R0. Figure 10 shows the CFG of program after register

HARDWARE IMPLEMENTATION
In this section, we discuss the hardware implementation of LTRF in detail.

Register File Microarchitecture
Register File Cache. Figure 11 illustrates an example LTRF architecture. We show our added components to the baseline register file architecture in orange color. The register file cache is composed of # _ _ banks (e.g., 16 banks in the figure) where each bank hosts # _ registers (e.g., 8 1024-bit registers in the figure). LTRF interleaves registers belonging to a single warp across the cache banks, and hence, each register bank houses no more than one register of a warp. Register file cache banks are connected to the operand collectors via a crossbar. Warp Control Block. A key structure in LTRF design is the Warp Control Block (WCB), shown in Figure 12. The purpose of the WCB is to maintain metadata for each warp required for controlling the register prefetching process and finding the position of the architectural registers in the register cache. To this end, WCB is composed of the register cache address table, the working-set bit-vector, and the liveness bit-vector. The register cache address table is a 256-entry table per warp that keeps the register file cache bank number for each warp's architectural registers. The register cache address table has as many entries as the maximum number of architectural registers allocated to a warp. All cached registers of a warp have the same offset in all register file cache banks. Thus, for each register, the table only needs to keep track of the ⌈log 2 # _ _ ⌉-bit (e.g., 4-bit in Figure 12) index of the register file cache bank number where that register is located. WCB also contains one ⌈log 2 # _ ⌉-bit (e.g., 3-bit in Figure 12) entry to track the offset of that warp's registers inside the banks (called warp-offset address). The working-set bit-vector holds a valid bit for each register to indicate whether it has already been prefetched during the prefetch phase. Since most of the instructions have two read operands, we provide two read ports for each register cache address table. Any instruction that operates on more than two operands must fetch the register file cache addresses of all operands over multiple cycles.
In LTRF+, which considers the liveness of operands, each warp maintains a liveness bit-vector that keeps track of the liveness of each architectural register in the WCB, as depicted in Figure 12. This vector is initially cleared (i.e., all registers are marked as dead) when the warp starts execution, and it is updated as the warp executes ( § 3.2). Operand Collector Modifications. We augment each operand collector (Figure 11, right) with ⌈log 2 # _ _ ⌉-bit (e.g., 4 bits in the figure) bank number and ⌈log 2 # _ ⌉-bit (e.g., 3 bits in the figure) warp-offset address to determine the location of each architectural register in the register file cache. Register File Cache Access. As multiple warps may still try to access the same bank at any given cycle, we use an arbiter, as in conventional GPU register files, to arbitrate between accesses to register file cache banks and to resolve bank conflicts. When an operand collector is allocated to a warp, it probes the register cache address table in the corresponding WCB to get the locations of registers inside the register cache. After reading the registers' locations, the operand collector participates in the arbitration phase to 1) resolve bank conflicts and 2) access the register file cache to read the operands.  Fig. 11. LTRF architecture. Figure assumes 8

Software-Triggered Prefetch Mechanism
Executing Prefetch Operations. When a warp reaches a prefetch operation, the GPU must load the warp's registers into the register file cache as indicated by the prefetch bit-vector. Initially, the prefetch bit-vector is decoded into a list of indices (IDs) of registers that need to be loaded. Once the register indices are identified, they must be allocated space in the register file cache, and the warp's register cache address table in the WCB must be properly filled. After allocating register file cache space, the registers can be read from the main register file to fill the register cache. When a register is prefetched completely, the corresponding valid bit in the WCB is set. After all registers indicated by the prefetch bit-vector are prefetched, the warp becomes ready to execute, and all subsequent register accesses of that warp are served from the register file cache. In LTRF+, whenever a warp performs a prefetch operation, it queries the liveness bit-vector and prefetches only the registers that are marked as live. For dead registers, it is sufficient to allocate the register file cache space, without fetching data. Register File Cache Space Allocation. Every cached register in a register-interval must be assigned a place in the register file cache. In our design, this mechanism is equivalent to allocating one register file cache bank for each cached register as we interleave the registers of a single warp across banks to minimize register file cache bank conflicts. We employ the Address Allocation Unit, depicted in Figure 13, for each warp to implement this mechanism. The Address Allocation Unit is composed of two queues: the unused queue keeps track of free banks, while the occupied queue keeps track of allocated banks. Initially, the unused queue is full, and the occupied queue is empty. On an allocation, we allocate the head of the unused queue to the new register and move that entry to the occupied queue. On a deallocation, we move the deallocated register entry back to the unused queue. The same mechanism is used to allocate warp-offset addresses to warps. There, we use a global Address Allocation Unit that is shared by all warps. Interconnect. We use an # _ -bit arbiter (e.g., 8-bit arbiter in Figure 11, left) to arbitrate among warps to fill the register file cache. Registers are loaded into the register file cache from the main register file via a crossbar network. In order to design this crossbar, we calculate the number of accesses to the main register file in LTRF and the baseline architecture. Our experimental results show that LTRF reduces the number of accesses to the main register file by 4×-6× (as most of the accesses are serviced through the register file cache) and the bandwidth of the 1024-bit crossbar in the baseline architecture without register caching [15,100] is utilized by up to 85%. As a result, we can reduce the bandwidth of the main register file crossbar by 4× without hurting performance. On the downside, the narrower crossbar would exhibit a traversal latency 4× larger (from 1 cycle to 4 cycles) than a wide crossbar in typical scenarios, and far larger latency when the crossbar is saturated and queuing effects become dominant. However, due to the latency-tolerant nature of our design and the fact that ...  register file access latencies are dominated by bank access times rather than crossbar traversals, we find that the longer traversal latency of a narrower crossbar is not a significant performance issue. Because warp registers are interleaved across the register cache banks, LTRF improves the parallelism of accesses in the interconnect. Warp Stall. A warp that is stalled becomes inactive and loses its slots in the register file cache. In that case, it must perform three steps. First, it writes back its live registers to the main register file. Second, it releases its register file cache slots. Third, it clears all the valid bits in the register cache address table of the WCB. Whenever a warp goes from being inactive to active, and it finds itself in the middle of a register-interval, it must refetch all its specified registers in its working-set bit-vector that are still live from the main register file. This is done using the warp's working-set and liveness bit-vectors in WCB.

Overheads
Code Size. LTRF increases the average code size by 7% if only prefetch bit-vectors are inserted into the code and 9% if the bit-vectors are accompanied by prefetch instructions. Note that, in order to be able to only insert the bit-vectors into the code, the ISA has to be redesigned and an extra bit has to be embedded into all instructions, as explained in §3.2. To measure the effect of the increased code size on the GPU performance, we execute the original and the modified programs on the baseline architecture using GPGPU-Sim [15]. Our experimental results show that the larger code size results in 0.2% average (up to 1%) performance degradation, which is negligible. We do not add a new instruction to pass liveness information to hardware. We use the same mechanism as the prior work [49], which embed liveness information in the instruction using free spaces in the instruction code. As a result, marking dead registers does not increase the code footprint and the number of executed instructions. Storage Cost. LTRF requires a WCB for every warp, shown in Figure 12. Each WCB contains one 5-bit entry per architectural register, 3-bit for the warp-offset address, and working-set and liveness bit-vectors, each with one bit per register. The total storage overhead of the WCB for each SM in an example modern architecture, which supports 64 warps with 256 registers per warp, is 114880 bits (64 × (256 × 5 + 3 + 256 + 256)), around 5% of the area consumed by the 256KB baseline register file. Latency Overhead. According to our analysis with CACTI [125], the WCB can be accessed within one extra clock cycle. Hence, it adds negligible performance overhead in accessing the registers. Area/Power Cost. In order to measure the area and power overheads of LTRF, we functionally model all the added components (i.e., WCB, the additional crossbar, address allocation units, the arbiter, additional entries in the operand collectors, and register file cache) in GPU-Wattch [100]. In total, LTRF occupies 16% more area than our baseline GPU register file (i.e., Configuration #1 in Table 2) using the same main register file size and technology. In terms of power consumption, despite the added structures, LTRF consumes 23% less power compared to the baseline register file. LTRF's improvement in power consumption is due to reducing the number of accesses to the main register file by 4×-6×.

METHODOLOGY
Simulation. We evaluate our techniques using the GPGPU-Sim V3.2.2 [15] cycle-level simulator for GPUs. Table  3 provides the details of our baseline GPU configuration. We model our baseline after an NVIDIA Maxwell-like architecture [138]. We modify the microarchitecture of the conventional register file in GPGPU-Sim to implement the LTRF microarchitecture depicted in Figure 11. We use the compiler in GPGPU-Sim to implement our software prefetching mechanism. To this end, we process the CFG of the register-allocated PTX code to form the register-intervals 6 and insert prefetch bit-vectors at the start of each register-interval. Benchmarks. We run 35 benchmarks from CUDA SDK [15], Rodinia [31], and Parboil [174] benchmark suites and classify them into two groups, register-sensitive and register-insensitive, based on whether or not the register file limits the achievable TLP. Enlarging GPU register file improves the thread-level parallelism of registersensitive applications. However, not all register-sensitive applications benefit the same from higher thread-level parallelism in terms of GPU performance (see § 7.1 for more detail). We randomly select nine workloads from the register-sensitive group, and five workloads from the register-insensitive one. Comparison Points. We evaluate (1) a baseline (BL) architecture that models a GPU with a conventional noncached register file. To provide a fair comparison of this baseline to other register file cache based designs, we add the amount of space dedicated for the register file cache in LTRF (16KB) to the main register file capacity in the BL architecture, (2) a design with a 16KB register file cache (RFC) without any prefetching mechanisms, similar to the architecture proposed in [49], (3) LTRF with a 16KB register file cache. (4) LTRF , an enhanced version of LTRF in which, we reduce the register bank conflicts in performing prefetch operations using a compiler pass after register-interval creation pass ( § 4). (5) a GPU with an Ideal register file architecture that allows us to increase the register file capacity to any size (i.e., 8× in our evaluations) with no latency overhead. Please note that all these designs have 24 SMs and 64 warps per SM. Design Points. To show the benefits of LTRF, we select different memory technologies (i.e., TFET [104] and DWM [182]) as use-cases. The reason for selecting different memory technologies is that they let us evaluate a wider range of access latencies; we believe that using different memory technologies is one of the concrete potential futuristic use-cases since these technologies are finding their ways into commercial products due to limitations of existing technologies in scaling to smaller dimensions [89,94,95,149,178]. Yet, our optimizations do not depend on the existence of a new memory technology. We can have large register files where LTRF would be useful even in conventional technologies by paving the way for different register file optimization techniques, such as register file compression [99,145,146,187], register file virtualization [65,82,88,185], and register file power-gating [1,59,79,119,156,157]. We increase the register file size from 256KB to 2MB by using the register file configurations #6 and #7 from Table 2. Configuration #6 allows us to increase the register file size by 8× while keeping the power consumption almost unchanged. Configuration #7, on the other hand, results in less power/area consumption compared to the baseline SRAM-based 256KB register file. We use these design points as realistic baselines for our performance analysis. We carefully model the register prefetching latency. Three important factors that affect prefetching operation delay are (1) the register bank access latency, (2) the register bank conflict, and (3) the transferring latency. We model the register bank access latency of each memory technology using CACTI [125] and NVSim [41]. We then import the calculated numbers to the GPGPU-Sim [15] to consider the effect of register bank conflicts, as well. Regarding the transferring latency, we use the booksim simulator [66] embedded in the GPGPU-Sim. Performance Metrics. We use IPC as the performance metric to evaluate different register file designs. We evaluate our compiler algorithms by measuring the size of the generated register-intervals.

EVALUATION
We present the effectiveness of five different mechanisms: BL, RFC, LTRF, LTRF , and Ideal. § 7.1 shows the overall effect of LTRF on GPU performance. § 7.2 analyzes the effectiveness of LTRF at tolerating the latency of the main register file. § 7.3 evaluates the number of register blocking events in LTRF and LTRF . § 7.4 provides sensitivity analysis on the size of the register file cache. § 7.5 analyzes the number of instructions in register-intervals. § 7.6 provides a comparison between LTRF and other software-managed register caching schemes.

Overall Effect on GPU Performance
To evaluate the effect of larger register files on GPU performance, we increase the register file size from 256KB to 2MB by using the register file configurations #6 and #7 from Table 2. Figure 14 compares the normalized IPC of BL, RFC, LTRF, and LTRF designs when used on top of these two configurations. In this figure, we normalize the IPC results to the IPC results of the baseline architecture of configuration #1 in Table 2, without any register caching, with one modification: we add the register file cache capacity (i.e., 16KB) used in the other Register-Insensitive Register-Sensitive  Table 2, normalized to the baseline architecture of configuration #1 with 16KB additional register file capacity.
mechanisms to the 256KB register file size of configuration #1. Ideal bars show the IPC of an idealized version of configuration #1 with 8× the register file capacity but no increase in latency (i.e., access latency remains constant after increasing register file size by 8×). We make three major observations. First, LTRF provides almost the same IPC performance as the Ideal design when we employ configuration #6. LTRF improves IPC by 32%, on average. The IPC improvement of LTRF is due to two main reasons. (1) The larger register file enables both more registers per thread and more warps executing in parallel. (2) LTRF effectively tolerates the higher access latency of configuration #6. Second, for the register-insensitive workloads that do not benefit from a larger register file (e.g., btree and kmeans), the performance overhead of increasing the register file size is minimal if we use LTRF and LTRF as opposed to RFC. Third, LTRF improves the performance of LTRF by an average 3.8% and 4.8% for configurations #6 and #7, respectively. These results clearly show the positive effect of reducing the number of register blocking events on LTRF performance. Fourth, LTRF and LTRF effectively enable the use of configuration #7, which reduces the register file area by 75%. For this configuration, LTRF and LTRF improve performance by 28% and 34% over the baseline, on average, respectively. We conclude that LTRF enables a high-capacity and high-latency main register file while providing high performance.

Effect of LTRF on Register File Access Latency
To show the effectiveness of LTRF at tolerating register file access latency, we define a new metric: the maximum tolerable register file access latency. This is the relative latency 7 of the main register file that leads to at most 5% performance (IPC) loss for each workload we examine. Note that this metric is different for each design, depending on the latency tolerance of the design. We increase the main register file access latency while keeping the main register file size constant. Figure 15 compares the maximum tolerable register file access latency of different designs for various benchmarks. We make three major observations. First, the maximum tolerable register file access latency for LTRF is 5.3×, on average. This result indicates that LTRF can 1) effectively bring the registers to the register file cache before they are accessed and 2) hide the latency of the register access to the main register file by executing other active warps. Second, the maximum tolerable register file access latency for LTRF , in which, we minimize register bank conflicts, is 6.9×, on average, indicating that resolving register bank conflicts can effectively improve the opportunity of LTRF in tolerating the register file access latency. Third, the maximum tolerable register file access latency for RFC is 2.1×, on average, which shows that the register file cache hit rate in the RFC design is not large enough to hide main register file access latencies that are greater than 2.1×.
We conclude that LTRF and LTRF are able to tolerate long main register file access latencies. Thus, they can enable aggressive optimizations that increase register file capacity in exchange for higher access latency.

Register Bank Conflicts in LTRF vs. LTRF
To evaluate the effect of LTRF on resolving register bank conflicts, we compare the number of register bank conflicts for different workloads using LTRF and LTRF . We perform the experiments using various numbers of registers allowed in each register-interval, i.e., 8,16, and 32, with a fixed number of register banks (i.e., 16 banks). Figure 16(a-f) illustrates the results.
We make two key observations. First, on average, 58%, 23%, and 9.4% of prefetch operations experience conflictfree register bank access in LTRF using 8, 16, and 32 registers allowed in each register-interval, respectively. Our compile-time register renumbering technique enables an average of 95%, 88%, 24% of prefetch operations to have conflict-free register bank access when 8, 16, and 32 registers allowed in each register-interval, respectively. Second, our register renumbering technique reduces the maximum number of register bank conflicts in a prefetch operation from 3, 3, and 3 to 1, 1, and 2 for 8, 16, and 32 registers allowed in each register-interval, respectively. We conclude that our compile-time register renumbering technique embedded in LTRF is very effective at resolving register bank conflicts, and paves the way for increasing the number of registers allowed in each register-interval.

Sensitivity to Register File Cache Size
We explore the effect of the register file cache size on performance in two ways: (1) varying the number of registers allowed in each register-interval (default is 16), (2) varying the number of active warps with allocated storage space in the register cache. Figure 17 reports the average IPC when we vary the number of registers allowed in each register-interval for LTRF and LTRF . We make four observations. First, when the number of registers allowed in each register-interval is 8, the effectiveness of LTRF degrades significantly, as the main register file access latency increases. This is mainly because a small number of registers results in a small register-interval size. Hence, prefetch operations become more frequent, and hiding their latency becomes more difficult, especially  for slow main register files. Second, LTRF and LTRF work almost the same when the number of registers allowed in each register-interval is 8 as we do not have much register bank conflict in this case. Third, increasing the number of registers allowed in each register-interval does not necessarily translate to better performance for LTRF. This is mainly because more registers result in more main register file bank conflicts during the prefetch operation, increasing prefetch latency. Therefore, larger register-interval sizes may not always be enough to hide larger prefetch latencies. Fourth, increasing the number of registers allowed in each register-interval can result in better performance of LTRF as LTRF attempts to reduce the number of register bank conflicts to the minimum possible value (i.e., one register bank conflict when the number of registers allowed in each register-interval is 32).  Figure 18 illustrates LTRF performance sensitivity to the number of warps that have dedicated register file cache space, while keeping the dedicated space per warp constant. We make three observations. First, as the number of active warps increases from 4 to 8, IPC improves by 45.6% and 27.4% for the slowest main register file using LTRF and LTRF , respectively. Second, increasing the number of active warps by more than 8 does not have a significant impact on LTRF and LTRF performance. Third, the performance improvement of LTRF over LTRF is larger for smaller number of active warps. We conclude that 8 active warps, which is the default configuration in LTRF, seems enough. Hence, LTRF does not impose significant performance cost by limiting the number of active warps. We conclude that, by making the performance impact of a slower register file more tolerable, LTRF enables a large design space to architects, where tradeoffs between power, area, and latency of the register file can be explored more freely to optimize system-level goals.

Register-Interval Length
Register-intervals should be as long as possible to minimize the number of prefetch operations. We measured both the real and the optimal register-interval lengths. The real register-interval length is the number of dynamic instructions within each register-interval. The optimal register-interval length is the number of consecutive dynamic instructions in a kernel's execution trace that consume at most the maximum number of allowed registers in the register cache. In other words, the optimal length exposes the limitations caused by the controlflow constraints imposed on register-intervals. Table 4 reports the average, minimum, and maximum lengths of the real and optimal register-intervals. We make two observations. First, the real register-interval length is 89% of the optimal register-interval length, on average. Second, the minimum and maximum lengths of real register-intervals are 78% and 85% of the ones in optimal register-intervals, respectively. We conclude that the control flow constraints in creating the register-intervals do not greatly limit the register-interval length. Table 4. The average, minimum, and maximum lengths of real and optimal register-intervals, in terms of dynamic instructions, for 35 workloads in CUDA SDK [15], Rodinia [31], and Parboil [174] benchmark suites.

LTRF vs. SW-Managed Hierarchical Register Files
To distinguish the benefits of our key ideas from other software-based approaches, we evaluate the maximum tolerable register file access latency of two additional designs: 1) a software-managed hierarchical register file (SHRF) similar to [50] and 2) a version of LTRF that performs prefetch operations at the end of strands [50], rather than register-intervals. SHRF [50] aims to reduce the number of background register swap operations between the main register file and the register file cache to provide energy efficiency and uses traditional register allocation/spilling techniques. SHRF uses strands, which are more constrained CFG subgraphs than registerintervals, since long/variable-latency operations (e.g., cache misses) and backward branches are disallowed within a strand to guarantee that the warp does not get descheduled until the end of the strand [50]. Figure 19 reports the normalized IPC, averaged across our workloads (see § 6), as the main register file access latency increases. We make two observations. First, SHRF performs similarly to RFC and can tolerate latencies by up to 2× the baseline latency. Second, LTRF can tolerate only 3× higher register file latency if it uses strands instead of register-intervals, as opposed to the 5.3× higher main register file latency tolerated by our LTRF design that uses register-intervals. LTRF performs better using register-intervals because strands' CFG subgraphs are more constrained, and typically much smaller than the CFG subgraphs of register-intervals, increasing the number of prefetch operations, register writebacks, and register re-fetches. In particular, while the length of a register-interval is usually limited by the size of the register working-set, a strand is typically terminated due to unrelated control flow constraints, and as a result, the strand's register working-set is often smaller than the available register file cache space. We conclude that using register-intervals to place the prefetch operations is essential for LTRF performance.

Impact of Number of Warps per SM
We have already studied the impact of changing the number of active warps on LTRF's effectiveness in § 7.4. In this section, we study how LTRF works when we change the total number of warps per SM. To this end, we compare "the maximum tolerable register file access latency" metric for LTRF and BL designs using various numbers of warps per SM. We consider four different numbers of warps per SM in this experiment: 16, 32, 64, and 128. Figure 20 reports the results. We make two key observations. First, LTRF improvement compared to BL is higher when we use lower numbers of warps per SM. This is due to the fact that the BL design significantly loses its ability to tolerate register file access latencies when the number of warps per SM is low. However, LTRF still can work with low number of warps, since it relies on register file caching and register prefetching in addition to thread-level parallelism to tolerate register file access latency. Second, we observe almost the same results when we increase the number of warps from 64 to 128, indicating potential saturation of latency tolerance as the number of warps per SM increases. We conclude that LTRF is effective using various numbers of warps.

RELATED WORK
To our knowledge, this paper is the first to design a latency-tolerant register file architecture for GPUs by (1) prefetching the entire register working set of a warp from the main register file to the register file cache using the notion of register-intervals, and (2) overlapping the prefetch latency with the execution of other active warps. LTRF opens a window for many optimizations in the main register file that greatly increase the effective capacity at the expense of higher access latency. We have already compared LTRF extensively to various hardware and software register caching proposals for GPUs [49,50] in § 7. In this section, we describe other related work in register file caching and register file scalability. Register File Caching. Few works have explored hardware-and software-managed hierarchical register files for GPUs [7,49,50]. These works focus on other objectives, such as energy efficiency, rather than latency-tolerance, and expose the higher latencies of slow register files to the execution. Regless [88] is a concurrent work that slices the computation graph into regions and allocates operand storage for the regions to replace the register file with a small operand staging unit. However, Regless targets power reduction rather than latency tolerance as the main objective.
Register file caching and hierarchical register files have been widely investigated for CPU architectures. Most of those works focus on superscalar or VLIW processors [21,24,39,136,140,166,197,198]. Such architectures are often able to hide the larger access latency of the slower register file levels via instruction level parallelism. As a result, the main focus of this line of work has been on efficient ways of integrating hierarchical register files into deep out-of-order pipelines and orchestrating the interactions between rename/issue mechanisms and register movements among different levels [21,140]. However, these techniques are usually not applicable to GPUs as GPUs have limited support for instruction-level parallelism. Another line of work focuses on software-managed hierarchical files with different ISA-visible register banks that have different sizes and speeds [37,73,77,177] where the compiler orchestrates register placement and movement. The CRAY-1 system [155] is an example architecture that implements a compiler-controlled hierarchical register file where software instructions explicitly manage the register movement between the two levels. Such techniques are suitable mainly for VLIW/vector processors and are not effective when used with GPUs where dynamic thread interleavings are unknown at compile-time as the GPU compiler is not able to schedule register movements to overlap them with the execution of other threads. Register File Scalability. There are many techniques that improve the scalability or efficiency of the register files. These techniques employ dimming and power-gating [1,59], compression [99], new memory technologies [2, 68-70, 104, 108, 109, 116, 188, 195], and virtualization [45,65,82,185]. All these techniques likely cause an increase in register file access latency. LTRF can be synergistically combined with these techniques and can enable them to tolerate the long register file access latencies. Hence, we believe LTRF is a substrate that enables optimizations in GPU register files, which might otherwise not always be desirable, efficient, or high performance. Prefetching. Data prefetching techniques in memory systems aim to improve the overall performance by fetching data from the lower levels of the memory hierarchy to the higher levels ahead of its first access [5, 8, 11, 16-20, 22, 26-28, 30, 32-36, 40, 42, 43, 46, 47, 52, 54, 60-62, 67, 72, 74-76, 83, 84, 86, 90, 92, 96, 97, 102, 105, 107, 110-114, 117, 124, 128, 141, 144, 148, 154, 165, 168-173, 175, 176, 181, 184, 186, 190, 191, 196, 199, 201]. Some methods aim to reduce I/O request response time by prefetching data from disk to memory or NAND flash based solid state disks (SSDs) as a second-level disk cache. By analyzing the access pattern of disk requests, these methods predict the stream of blocks that will be accessed in the future [11,27,28,40,52,67,110,144,171,172,175,181].
We propose LTRF, a new latency-tolerant hierarchical register file design for GPUs. The key mechanism of LTRF is a near-perfect register prefetching scheme that divides the application control flow graph into register-intervals and brings the entire register working set of a warp from the main register file to the register cache at the beginning of each register-interval. As a result, a warp experiences the fast register cache access latency, rather than the long access latency of the large main register file. We devise a compile-time register renumbering technique on top of LTRF to resolve register bank conflicts. An example evaluation result shows that LTRF combined with register renumbering technique enables us to implement the main register file with emerging high-density high-latency memory technologies, enabling 8× larger register file capacity and improving overall GPU performance by 34%. We believe that LTRF paves the way for many power/area optimization techniques in the main register file that likely increase the register access latency. We conclude that, by making the performance impact of a slower register file more tolerable, LTRF enables a large design space to architects, where tradeoffs between power, area, and latency of the register file can be explored more freely to optimize system-level goals.