<?xml-model href='http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng' schematypens='http://relaxng.org/ns/structure/1.0'?><TEI xmlns="http://www.tei-c.org/ns/1.0">
	<teiHeader>
		<fileDesc>
			<titleStmt><title level='a'>Ripple: Profile-Guided Instruction Cache Replacement for Data Center Applications</title></titleStmt>
			<publicationStmt>
				<publisher></publisher>
				<date>06/01/2021</date>
			</publicationStmt>
			<sourceDesc>
				<bibl> 
					<idno type="par_id">10301616</idno>
					<idno type="doi">10.1109/ISCA52012.2021.00063</idno>
					<title level='j'>48th Annual International Symposium on Computer Architecture (ISCA)</title>
<idno></idno>
<biblScope unit="volume"></biblScope>
<biblScope unit="issue"></biblScope>					

					<author>Tanvir Ahmed Khan</author><author>Dexin Zhang</author><author>Akshitha Sriraman</author><author>Joseph Devietti</author><author>Gilles Pokam</author><author>Heiner Litz</author><author>Baris Kasikci</author>
				</bibl>
			</sourceDesc>
		</fileDesc>
		<profileDesc>
			<abstract><ab><![CDATA[Modern data center applications exhibit deep software stacks, resulting in large instruction footprints that frequently cause instruction cache misses degrading performance, cost, and energy efficiency. Although numerous mechanisms have been proposed to mitigate instruction cache misses, they still fall short of ideal cache behavior, and furthermore, introduce significant hardware overheads. We first investigate why existing I-cache miss mitigation mechanisms achieve sub-optimal performance for data center applications. We find that widely-studied instruction prefetchers fall short due to wasteful prefetch-induced cache line evictions that are not handled by existing replacement policies. Existing replacement policies are unable to mitigate wasteful evictions since they lack complete knowledge of a data center application's complex program behavior.To make existing replacement policies aware of these evictioninducing program behaviors, we propose Ripple, a novel softwareonly technique that profiles programs and uses program context to inform the underlying replacement policy about efficient replacement decisions. Ripple carefully identifies program contexts that lead to I-cache misses and sparingly injects "cache line eviction" instructions in suitable program locations at link time. We evaluate Ripple using nine popular data center applications and demonstrate that Ripple enables any replacement policy to achieve speedup that is closer to that of an ideal I-cache. Specifically, Ripple achieves an average performance improvement of 1.6% (up to 2.13%) over prior work due to a mean 19% (up to 28.6%) I-cache miss reduction.]]></ab></abstract>
		</profileDesc>
	</teiHeader>
	<text><body xmlns="http://www.tei-c.org/ns/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink">
<div xmlns="http://www.tei-c.org/ns/1.0"><head>I. INTRODUCTION</head><p>Modern data center applications are becoming increasingly complex. These applications are composed of deep and complex software stacks that include various kernel and networking modules, compression elements, serialization code, and remote procedure call libraries. Such complex code stacks often have intricate inter-dependencies, causing millions of unique instructions to be executed to serve a single user request. As a result, modern data center applications face instruction working set sizes that are several orders of magnitude larger than the instruction cache (I-cache) sizes supported by today's processors <ref type="bibr">[13,</ref><ref type="bibr">46]</ref>.</p><p>Large instruction working sets precipitate frequent I-cache misses that cannot be effectively hidden by modern out-oforder mechanisms, manifesting as glaring stalls in the critical path of execution <ref type="bibr">[60]</ref>. Such stalls deteriorate application performance at scale, costing millions of dollars and consuming significant energy <ref type="bibr">[13,</ref><ref type="bibr">95]</ref>. Hence, eliminating instruction misses to achieve even single-digit percent speedups can yield immense performance-per-watt benefits <ref type="bibr">[95]</ref>.</p><p>I-cache miss reduction mechanisms have been extensively studied in the past. Several prior works proposed next-line <ref type="bibr">[9,</ref><ref type="bibr">89,</ref><ref type="bibr">92]</ref>, branch-predictor-guided <ref type="bibr">[60,</ref><ref type="bibr">61,</ref><ref type="bibr">82]</ref>, or history-based <ref type="bibr">[25,</ref><ref type="bibr">26,</ref><ref type="bibr">31,</ref><ref type="bibr">51,</ref><ref type="bibr">59,</ref><ref type="bibr">70,</ref><ref type="bibr">77,</ref><ref type="bibr">83]</ref> hardware instruction prefetchers and others designed software mechanisms to perform code layout optimizations for improving instruction locality <ref type="bibr">[17,</ref><ref type="bibr">64,</ref><ref type="bibr">67,</ref><ref type="bibr">[74]</ref><ref type="bibr">[75]</ref><ref type="bibr">[76]</ref>. Although these techniques are promising, they <ref type="bibr">(1)</ref> require additional hardware support to be implemented on existing processors and (2) fall short of the ideal I-cache behavior, i.e., an I-cache that incurs no misses. To completely eliminate I-cache misses, it is critical to first understand: why do existing I-cache miss mitigation mechanisms achieve sub-optimal performance for data center applications? How can we further close the performance gap to achieve near-ideal application speedup?</p><p>To this end, we comprehensively investigate why existing I-cache miss mitigation techniques fall short of an ideal Icache, and precipitate significant I-cache Misses Per Kilo Instruction (MPKI) in data center applications ( &#167;II). Our investigation finds that the most widely-studied I-cache miss mitigation technique, instruction prefetching, still falls short of ideal I-cache behavior. In particular, existing prefetchers perform many unnecessary prefetches, polluting the I-cache, causing wasteful evictions. Since wasteful evictions can be avoided by effective cache replacement policies, we study previous proposals such as the Global History Reuse Predictor (GHRP) <ref type="bibr">[7]</ref> (the only replacement policy specifically targeting the I-cache, to the best of our knowledge) as well as additional techniques that were originally proposed for data caches, such as Hawkeye <ref type="bibr">[40]</ref>/Harmony <ref type="bibr">[41]</ref>, SRRIP <ref type="bibr">[43]</ref>, and DRRIP <ref type="bibr">[43]</ref>.</p><p>Driven by our investigation results, we propose Ripple, a profile-guided technique to optimize I-cache replacement policy decisions for data center applications. Ripple first performs an offline analysis of the basic blocks (i.e., sequence of instructions without a branch) executed by a data center application, recorded via efficient hardware tracing (e.g., Intel's Processor Trace <ref type="bibr">[19,</ref><ref type="bibr">58]</ref>). For each basic block, Ripple then determines the cache line that an ideal replacement policy would evict based on the recorded basic block trace. Ripple computes basic blocks whose executions likely signal a future eviction for an ideal replacement policy. If this likelihood is above a certain threshold (which we explore and determine empirically in &#167;III), Ripple injects an invalidation instruction to evict the victim cache line. Intel recently introduced such an invalidation instruction -CLDemote, and hence Ripple can readily be implemented on upcoming processors.</p><p>We evaluate Ripple in combination with I-cache prefetching mechanisms, and show that Ripple yields on average 1.6% (up to 2.13%) improvement over prior work as it reduces Icache misses by on average 19% (up to 28.6%). As Ripple is primarily a software-based technique, it can be implemented on top of any replacement policy that already exists in hardware. In particular, we evaluate two variants of Ripple. Ripple-Least Recently Used (LRU) is optimized for highest performance and reduces I-cache MPKI by up to 28.6% over previous proposals, including Hawkeye/Harmony, DRRIP, SRRIP, and GHRP. On the other hand, Ripple-Random is optimized for lowest storage overhead, eliminating all meta data storage overheads, while outperforming prior work by up to 19%. Ripple executes only 2.2% extra dynamic instructions and inserts only 3.4% new static instructions on average. In summary, we show that Ripple provides significant performance gains compared to the state-ofthe-art I-cache miss mitigation mechanisms while minimizing the meta data storage overheads of the replacement policy.</p><p>In summary, we make the following contributions:</p><p>&#8226; A detailed analysis of why existing I-cache miss mitigation mechanisms fall short for data center applications &#8226; Profile-guided replacement: A software mechanism that uses program behavior to inform replacement decisions &#8226; Ripple: A novel profile-guided instruction cache miss mitigation mechanism that can readily work on any existing replacement policy &#8226; An evaluation demonstrating Ripple's efficacy at achieving near-ideal application speedup.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>II. WHY DO EXISTING I-CACHE MISS MITIGATION</head><p>TECHNIQUES FALL SHORT?</p><p>In this section, we analyze why existing techniques to mitigate I-cache misses fall short, precipitating high miss rates in data center applications. We first present background information on the data center applications we study ( &#167;II-A). We then perform a limit study to determine the maximum speedup that can be obtained with an ideal I-cache for applications with large instruction footprints ( &#167;II-B). Next, we evaluate existing prefetching mechanisms, including nextline prefetcher and FDIP <ref type="bibr">[82]</ref>, to analyze why these techniques achieve sub-optimal performance ( &#167;II-C). Finally, we analyze existing cache replacement policies, including LRU, Harmony, DRRIP, SRRIP, and GHRP, to quantify their performance gap with the optimal replacement policy ( &#167;II-D). This analysis provides the foundation for Ripple, a novel prefetch-aware Icache replacement policy that achieves high performance with minimal hardware overheads. </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>A. Background on evaluated applications</head><p>We study nine widely-used real-world data center applications that suffer from substantial I-cache misses <ref type="bibr">[55]</ref>these applications lose 23-80% of their pipeline slots due to frequent I-cache misses. We study three HHVM applications from Facebook's OSS-performance benchmark suite <ref type="bibr">[5]</ref>, including drupal <ref type="bibr">[103]</ref> (a PHP content management system), mediawiki <ref type="bibr">[104]</ref> (a wiki engine), and wordpress <ref type="bibr">[105]</ref> (a popular content management system). We investigate three Java applications from the DaCapo benchmark suite <ref type="bibr">[16]</ref>, including cassandra <ref type="bibr">[1]</ref> (a NoSQL database used by companies like Netflix), kafka <ref type="bibr">[102]</ref> (a stream processing system used by companies like Uber), and tomcat <ref type="bibr">[2]</ref> (Apache's implementation of Java Servlet and Websocket). From the Java Renaissance <ref type="bibr">[79]</ref> benchmark suite, we analyze Finagle-Chirper (Twitter's microblogging service) and Finagle-HTTP <ref type="bibr">[3]</ref> (Twitter's HTTP server). We also study Verilator <ref type="bibr">[4,</ref><ref type="bibr">10]</ref> (used by cloud companies for hardware simulation). We describe our complete experimental setup and simulation parameters in &#167;IV.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>B. Ideal I-cache: The theoretical upper bound</head><p>An out-of-order processor's performance greatly depends on how effectively it can supply itself with instructions. Therefore, these processors use fast dedicated I-caches that can typically be accessed in 3-4 cycles <ref type="bibr">[93]</ref>. To maintain a low access latency, modern processors typically have small I-cache sizes (e.g., 32KB) that are overwhelmed by data center applications' multi-megabyte instruction footprints <ref type="bibr">[11,</ref><ref type="bibr">13,</ref><ref type="bibr">46,</ref><ref type="bibr">76]</ref> incurring frequent I-cache misses. To evaluate the true cost of these I-cache misses as well as the potential gain of I-cache optimizations, we explore the speedup that can be obtained for data center applications with an ideal I-cache that incurs no misses. Similar to prior work <ref type="bibr">[13,</ref><ref type="bibr">55]</ref>, we compute the speedup relative to a baseline cache configuration with no prefetching and with an LRU replacement policy. As shown in Fig. <ref type="figure">1</ref>, an ideal I-cache can provide between 11-47% (average of 17.7%) speedup over the baseline cache configuration.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>C. Why do modern instruction prefetchers fall short?</head><p>Prior works <ref type="bibr">[13,</ref><ref type="bibr">24,</ref><ref type="bibr">55,</ref><ref type="bibr">82]</ref>  Fig. <ref type="figure">2</ref>: Fetch directed instruction prefetching (FDIP) speedup over an LRU baseline without any prefetching: FDIP provides 13.4% mean speedup with LRU replacement policy. However, with an ideal cache replacement policy FDIP can provide 16.6% average speedup which is much closer to ideal cache speedup.</p><p>Prefetching (FDIP) <ref type="bibr">[82]</ref> is the state-of-the art mechanism that is implemented on multiple real-world processors <ref type="bibr">[32,</ref><ref type="bibr">78,</ref><ref type="bibr">85,</ref><ref type="bibr">97]</ref> due to its performance and moderate implementation complexity. Fig. <ref type="figure">2</ref> shows FDIP's speedup over the baseline I-cache configuration without any prefetching. Both FDIP and baseline configurations use the LRU replacement policy. As shown, FDIP+LRU provides between 8-44% (average of 13.4%) speedup over the baseline. This represents a 4.3% performance loss over the ideal cache speedup (17.7%).</p><p>To analyze why FDIP falls short of delivering ideal performance, we equip the I-cache with a prefetch-aware ideal replacement policy. In particular, when leveraging a revised version of the Demand-MIN prefetch-aware replacement policy <ref type="bibr">[41]</ref>, we find that the speedup increases to on average 16.6% falling short of the ideal cache by just 1.14%. In other words, FDIP with prefetch-aware ideal replacement policy outperforms FDIP with LRU by 3.16%. This observation highlights the importance of combining state-of-the-art I-cache prefetching mechanisms with better replacement policies.</p><p>To confirm the generality of our observation, we repeat the above experiment with a standard Next-Line Prefetcher (NLP) <ref type="bibr">[92]</ref>. We find that the combination of NLP prefetching with ideal cache replacement results in a 3.87% speedup over the NLP baseline without a perfect replacement policy.</p><p>To understand the key reasons behind the near-ideal speedups provided by the prefetch-aware ideal replacement policy, we first briefly describe how the policy works and then summarize the key reasons for near-ideal speedups <ref type="bibr">[41]</ref>. We also quantify the speedups that an ideal replacement policy can provide for the data center applications we evaluate.</p><p>Prefetch-aware ideal replacement policy. Our ideal prefetchaware replacement policy is based on a revised version of Demand-MIN <ref type="bibr">[41]</ref>. In its revised form, Demand-MIN evicts the cache line that is prefetched farthest in the future if there is no earlier demand access to that line. If there exists no such prefetch for a given cache set, Demand-MIN evicts the line whose demand access is farthest in the future. We now detail and quantify two observations that were originally made by Demand-MIN: (1) evicting inaccurately prefetched cache lines reduces I-cache misses and ( <ref type="formula">2</ref>) not evicting hard-to-prefetch cache lines reduces I-cache misses.</p><p>Observation #1: Early eviction of inaccurately prefetched cache lines reduces I-cache misses. The ideal replacement policy can evict inaccurately prefetched cache lines (i.e., ones that will not be used) early, improving performance. Like most practical prefetchers, FDIP inaccurately prefetches many cache lines as its decisions are guided by a branch predictor, which occasionally mispredicts branch outcomes. However, the ideal replacement policy has knowledge of all future accesses, so it can immediately evict inaccurately prefetched cache lines, minimizing their negative performance impact. Across our nine data center applications, the ideal cache replacement policy combined with FDIP, provides 1.35% average speedup (out of 3.16% total speedup of FDIP+ideal over FDIP+LRU) relative to an LRU-based baseline replacement policy (also combined with FDIP) due to the early eviction of inaccurately-prefetched cache lines.</p><p>Observation #2: Not evicting hard-to-prefetch cache lines reduces I-cache misses. An ideal replacement policy can keep hard-to-prefetch cache lines in the cache while evicting easy-toprefetch lines. Cache lines that cannot be prefetched with good accuracy or at all, are considered hard-to-prefetch cache lines. For example, FDIP is guided by the branch predictor. A cache line that will be prefetched based on the outcome of a branch, may not be prefetched if the predictor cannot easily predict the branch outcome (e.g., due to an indirect branch)-in that case, the line is hard-to-prefetch. Easy-to-prefetch cache lines are cache lines that the prefetcher is often able to prefetch accurately. For example, a cache line that FDIP can prefetch based on the outcome of a direct unconditional branch is an easy-to-prefetch cache line. Since the ideal replacement policy has knowledge of all accesses and prefetches, it can (1) accurately identify hard-to-prefetch and easy-to-prefetch lines for any given prefetching policy and (2) prioritize the eviction of easy-to-prefetch lines over hard-to-prefetch lines. Across our nine data center applications, the ideal cache replacement policy combined with FDIP, provides 1.81% average speedup (out of 3.16% total speedup of FDIP+ideal over FDIP+LRU) relative to an LRU-based baseline replacement policy (also combined with FDIP) due to not evicting hard-to-prefetch lines.</p><p>Summary: Exploiting the above observations for an optimized prefetch-aware replacement policy requires knowledge about future instruction sequences that are likely to be executed. We find that this information can be provided by the static controlflow analysis based on execution profiles and instruction traces. As described in &#167;IV, Ripple leverages these analysis techniques and performs well-informed replacement decisions in concert with the prefetcher, to achieve near-ideal performance.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>D. Why do existing replacement policies fall short?</head><p>In the previous section, we demonstrated that a prefetchaware ideal cache replacement policy can provide on average 3.16% speedup relative to a baseline LRU replacement policy. In this section, we explore the extent to which existing replacement policies close this speedup gap. As there exist only few works on I-cache replacement policies apart from GHRP <ref type="bibr">[7]</ref>, we also explore data cache replacement policies such as LRU <ref type="bibr">[69]</ref>, Hawkeye <ref type="bibr">[40]</ref>/Harmony <ref type="bibr">[41]</ref>, SRRIP <ref type="bibr">[43]</ref>,</p><p>and DRRIP <ref type="bibr">[43]</ref> applied to the I-cache. GHRP <ref type="bibr">[7]</ref> was designed to eliminate I-cache and Branch Target Buffer (BTB) misses. During execution, GHRP populates a prediction table indexed by control flow information to predict whether a given cache line is dead or alive. While making replacement decisions, GHRP favors evicting lines that are more likely to be dead. Every time GHRP evicts a cache line, it uses a counter to update the predictor table that the evicted cache line is more likely to be dead. Similarly, GHRP updates the predictor table after each hit in the I-cache to indicate that that the hit cache line is more likely to be alive. GHRP uses 4.13KB extra on-chip metadata for a 32KB I-cache to primarily store this prediction table.</p><p>Hawkeye/Harmony <ref type="bibr">[40]</ref> was designed for the data cache, specifically for the Last Level Cache (LLC). By simulating the ideal cache replacement policy <ref type="bibr">[15]</ref> on access history, Hawkeye determines whether a Program Counter (PC) is "cache-friendly" or "cache-averse", i.e., whether the data accessed while the processor executes the instruction corresponding to this PC follows a cache-friendly access pattern <ref type="bibr">[42]</ref> or not. Cache lines accessed at a cache-friendly PC are maintained using the LRU cache replacement policy, while lines accessed by a cache-averse PC are marked to be removed at the earliest opportunity. Harmony <ref type="bibr">[41]</ref> is a state-of-the-art replacement policy that adds prefetch-awareness to Hawkeye. It simulates Demand-MIN <ref type="bibr">[41]</ref> on the access history in hardware to further categorize PCs as either prefetch-friendly or prefetch-averse.</p><p>SRRIP <ref type="bibr">[43]</ref> was mainly designed to eliminate the adverse effects of the scanning <ref type="bibr">[14]</ref> cache access pattern, where a large number of cache lines are accessed without any temporal locality (i.e., a sequence of accesses that never repeat). SRRIP assumes that all newly-accessed cache lines are cache-averse (i.e., scans). Only when a cache line is accessed for a second time, SRRIP promotes the status of the line to cache-friendly.</p><p>DRRIP <ref type="bibr">[43]</ref> improves over SRRIP by considering thrashing access patterns, i.e., when the working set of the application exceeds the cache size <ref type="bibr">[20]</ref>. DRRIP reserves positions for both cache-friendly and cache-averse lines via set-dueling <ref type="bibr">[80]</ref>.</p><p>Fig. <ref type="figure">3</ref> shows the performance for different cache replacement policies over the LRU baseline with FDIP. Tab. I shows the metadata storage overheads induced by each replacement policy. As shown, none of the existing replacement policies provide any performance or storage benefits over LRU even though the ideal cache replacement policy provides 3.16% average speedup over LRU. We now explain why each of these prior replacement policies do not provide any significant benefit.</p><p>GHRP classifies cache lines into dead or alive based on the prediction table, to inform eviction decisions. One issue with GHRP is that it increases the classification confidence in the prediction table after eviction even if the decision was incorrect (e.g., evicted a line that was still needed). We modified GHRP so that it decreases the confidence in the prediction table after each eviction. With this optimization, GHRP outperforms LRU by 0.1%.</p><p>Hawkeye/Harmony predicts whether a PC is likely to access a cache-friendly or cache-averse cache line. This insight works well for D-caches where an instruction at a given PC is responsible for accessing many D-cache lines that exhibit similar cache-friendly or cache-averse access patterns. However, for I-cache, an instruction at a given PC is responsible for accessing just one cache line that contains the instruction itself. If the line has multiple cache-friendly accesses followed by a single cache-averse access, Hawkeye predicts the line as cachefriendly. Therefore, Hawkeye cannot identify that single cacheaverse access and cannot adapt to dynamic I-cache behavior. For I-cache accesses in data center applications, Hawkeye predicts almost all PCs (more than 99%) as cache friendly and hence fails to provide performance benefits over LRU.</p><p>SRRIP and DRRIP can provide significant performance benefits over LRU if the cache accesses follow a scanning access pattern. Moreover, DRRIP provides further support for thrashing access patterns <ref type="bibr">[20]</ref>. For the I-cache, scanning access patterns are rare and hence classifying a line as a scan introduces a penalty over plain LRU. We quantify the scanning access pattern for our data center applications by measuring the compulsory MPKI (misses that happen when a cache line is accessed for the first time <ref type="bibr">[34]</ref>). For these applications, compulsory MPKI is very small (0.1-0.3 and 0.16 on average). Moreover, both SRRIP and DRRIP arbitrarily assume that all cache lines will have similar access patterns (either scan or thrash) which further hurts data center applications' I-cache performance. Consequently, SRRIP and DRRIP cannot outperform LRU for I-cache accesses in data center applications.</p><p>We observe that data center applications tend to exhibit a unique reuse distance behavior, i.e., the number of unique cache lines accessed in the current associative set between two consecutive accesses to the same cache line, or the re-reference interval <ref type="bibr">[43]</ref> of a given cache line varies widely across the program life time. Due to this variance, a single I-cache line can be both cache-friendly and cache-averse at different stages of the program execution. Existing works do not adapt to this dynamic variance and hence fail to improve performance over LRU. We combine these insights with our observations in &#167;II-C to design Ripple, a profile-guided replacement policy for data center applications. </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>III. THE RIPPLE REPLACEMENT MECHANISM</head><p>As we show in our analysis, an ideal cache replacement policy provides on average 3.16% speedup over an LRU I-cache for data center applications. Moreover, we find that existing instruction and data cache replacement policies <ref type="bibr">[7,</ref><ref type="bibr">40,</ref><ref type="bibr">41,</ref><ref type="bibr">43]</ref> fall short of the LRU baseline, since they are ineffective at avoiding wasteful evictions due to the complex instruction access behaviors. Hence, there is a critical need to assist the underlying replacement policy in making smarter eviction decisions by informing it about complex instruction accesses.</p><p>To this end, we propose augmenting existing replacement mechanisms with Ripple-a novel profile-guided replacement technique that carefully identifies program contexts leading to I-cache misses and strives to evict the cache lines that would be evicted by the ideal policy. Ripple's operation is agnostic of the underlying I-cache replacement policy. It sparingly injects "cache line eviction" instructions in suitable program locations at link time to assist an arbitrary replacement policy implemented in hardware. Ripple introduces no additional hardware overhead and can be readily implemented on soon-tobe-released processors. Ripple enables an existing replacement policy to further close the performance gap in achieving the ideal I-cache performance.</p><p>Fig. <ref type="figure">4</ref> shows Ripple's design components. First, at run time (online), Ripple profiles a program's basic block execution sequence using efficient hardware-based control flow tracing support such as Intel PT <ref type="bibr">[58]</ref> or Last Branch Record (LBR) <ref type="bibr">[21]</ref> (step 1 , &#167;III-A). Ripple then analyzes the program trace offline using the ideal I-cache replacement policy (step 2 , &#167;III-B) to compute a set of cue blocks. A cue block is a basic block whose execution almost always leads to the ideal victim cache line to be evicted. The key idea behind Ripple's analysis is to mimic an ideal policy that would evict a line that will be used farthest in the future. During recompilation, Ripple then injects an instruction in the cue block that invalidates the victim line (step 3 &#167;III-C). Consequently, the next time a cache line needs to be inserted into the cache set that the victim line belongs to, the victim line will be evicted. In contrast to prior work <ref type="bibr">[7,</ref><ref type="bibr">40,</ref><ref type="bibr">41,</ref><ref type="bibr">43,</ref><ref type="bibr">43]</ref>, Ripple moves the compute-intensive task of identifying the victim line from the hardware to the </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>A. Runtime Profiling</head><p>Ripple profiles data center applications at run time using Intel PT <ref type="bibr">[58]</ref> to collect a trace of the dynamically-executed basic blocks. As shown in Fig. <ref type="figure">4</ref>, the collected program trace includes two pieces of information for each control-flow instruction in the program. The first bit (T/NT), denotes whether the branch in question was taken (T) or the fall-through path was followed (NT). If the program follows the taken path of an indirect branch, the program trace also includes the address of the next instruction on the taken path. Ripple leverages this program execution trace, to perform (1) eviction analysis and (2) invalidation injection offline at link-time. During eviction analysis, Ripple identifies the I-cache lines that will be touched (omitting speculative accesses) in real hardware based on the execution trace. Ripple's eviction analysis does not require recording the I-cache lines that will be evicted in hardware.</p><p>Ripple leverages Intel PT <ref type="bibr">[58]</ref> to collect the precise basic block execution order with low runtime performance overhead (less than 1% <ref type="bibr">[48,</ref><ref type="bibr">109]</ref>). Ripple uses Intel PT since it is efficient in real-world production scenarios <ref type="bibr">[19,</ref><ref type="bibr">28,</ref><ref type="bibr">49,</ref><ref type="bibr">50]</ref>. Fig. <ref type="figure">5</ref>: An example of Ripple's eviction analysis process</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>B. Eviction Analysis</head><p>The goal of Ripple's eviction analysis is to mimic an ideal replacement policy, which would evict cache lines that will not be accessed for the longest time in the future. The basic block trace collected at run time allows Ripple to retroactively determine points in the execution that would benefit from invalidating certain cache lines to help with cache replacement.</p><p>Eviction analysis determines a cue block, whose execution can identify the eviction of a particular victim cache line with high probability, if an ideal cache replacement policy were used. To determine the cue block in the collected runtime profile, Ripple analyzes all the blocks in the eviction window of each cache line, i.e., the time window spanning between the last access to that cache line to the access that would trigger the eviction of the same line, given an ideal replacement policy.</p><p>Fig. <ref type="figure">5a</ref> shows examples of eviction windows for the cache line, A. In this example, the cache line A gets evicted six times by the ideal cache replacement policy over the execution of the program. To compute each eviction window, Ripple iterates backward in the basic block trace from each point where A would be evicted by an ideal replacement policy until it reaches a basic block containing (even partially) the cache line A. Ripple identifies the basic blocks across all eviction windows that can accurately signal the eviction as candidate cue blocks (described further in Sec. III-C), where it can insert an invalidation instruction to mimic the ideal cache replacement behavior. In this example, Ripple identifies basic blocks, B, C, D, and E as candidate cue blocks.</p><p>Next, Ripple calculates the conditional probability of a cache line eviction given the execution of each candidate cue block. Fig. <ref type="figure">5b</ref> shows an example of this probability calculation for the cache line, A. To calculate this conditional probability, Ripple calculates two metrics. First, it computes how many times each candidate cue block was executed during the application's lifetime. In this example, the candidate cue blocks B, C, D, and E are executed 16, 8, 6, and 3 times respectively. Second, for each candidate cue block, Ripple computes the number of unique eviction windows which include the corresponding candidate cue block. In our example, basic blocks B, C, D, and E are included in 4, 4, 2, and 2 unique eviction windows, respectively. Ripple calculates the conditional probability as the ratio of the second value (count of windows containing the candidate cue block) to the first value (execution count of the cue block). For instance, P((Eviction, A)|(Execute, B)) = 0.25 denotes that for each execution of B, there is a 25% chance that the cache line A may be evicted.</p><p>Finally, for each eviction window, Ripple selects the cue block with the highest conditional probability, breaking ties arbitrarily. In our example, Ripple will select basic blocks C and E as cue blocks for 4 (windows 1, 4, 5, 6) and 2 (windows 2, 3) eviction windows, respectively. If the conditional probability of the selected basic block is larger than a threshold, Ripple will inject an explicit invalidation request in the basic block during recompilation. Next, we describe the process by which the invalidation instructions are injected as well as the trade-off that is associated with this probability threshold.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>C. Injection of Invalidation Instructions</head><p>Based on the eviction analysis, Ripple selects the cue basic block for each eviction window. Next, Ripple inserts an explicit invalidation instruction into the cue block to invalidate the victim cache line. Ripple's decision to insert an invalidation instruction is informed by the conditional probability it computes for each candidate cue block. Specifically, Ripple inserts an invalidation instruction into the cue block only if the conditional probability is higher than the invalidation threshold. We now describe how Ripple determines the invalidation threshold and the invalidation granularity (i.e., why Ripple decides to inject invalidation instructions in a basic block to evict a cache line). We then give details on the invalidation instruction that Ripple relies on.</p><p>Determining the invalidation threshold. Ripple considers two key metrics when selecting the value of the invalidation threshold: replacement coverage and replacement accuracy. We first define these metrics and then explain the trade-off between them.</p><p>Replacement-Coverage. We define replacement-coverage as the ratio of the total number of replacement decisions performed by a given policy divided by the total number of replacement decisions performed by the ideal replacement policy. A policy that exhibits less than 100% replacement-coverage omits some invalidation candidates that the optimal replacement policy would have chosen for eviction.</p><p>Replacement-Accuracy. We define replacement-accuracy as the ratio of total optimal replacement decisions of a given policy divided by the replacement decisions performed by the ideal replacement policy. Therefore, if Ripple induces x invalidations over a program's lifetime, and y of those invalidations do not introduce any new misses over the ideal cache replacement policy, then Ripple's accuracy (in percentage) is: 100&#8676;y</p><p>x . A policy that exhibits less than 100% replacement-accuracy will evict cache lines that the ideal cache replacement policy would not have evicted.</p><p>Coverage-Accuracy Trade-off. Replacement-coverage and replacement-accuracy represent useful metrics to measure a cache replacement policy's optimality. A software-guided policy with a low replacement-coverage will frequently need to revert to the underlying hardware policy suffering from its sub-optimal decisions. On the other hand, a policy with low replacementaccuracy will frequently evict lines that the program could still use. As shown in Fig. <ref type="figure">6</ref>, Ripple leverages the invalidationthreshold to control the aggressiveness of its evictions, allowing to trade-off coverage and accuracy. Although this figure presents data from a single application (i.e., finagle-http), we observe similar trends across all the data center applications that we evaluate.</p><p>At a lower threshold (0-20%), Ripple has almost 100% coverage, because all the replacement decisions are made by Ripple's invalidations. At the same time, Ripple's accuracy suffers greatly because it invalidates many cache lines that introduce new misses over the ideal cache replacement policy. Consequently, at a lower threshold, Ripple does not provide additional performance over the underlying replacement policy.</p><p>Similarly, at a higher threshold (80-100%), Ripple achieves near-perfect accuracy as cache lines invalidated by Ripple do not incur extra misses over the ideal replacement policy. However, Ripple's coverage drops sharply as more replacement decisions are not served by Ripple-inserted invalidations. Therefore, Ripple's performance benefit over the underlying hardware replacement policy declines rapidly.</p><p>Only at the middle ground, i.e., when the invalidation threshold ranges from 40-60%, Ripple simultaneously achieves both high coverage (greater than 50%) and high accuracy (greater than 80%). As a result, Ripple provides the highest performance benefit at this invalidation threshold range. For each application, Ripple chooses the invalidation threshold that provides the best performance for a given application. Across 9 applications, this invalidation threshold varies from 45-65%.</p><p>Invalidation granularity. Ripple injects invalidation instructions at the basic block granularity while invalidation instructions evict cache lines. In practice, we find that Ripple does not suffer a performance loss due to this mismatch. In particular, Ripple provides a higher speedup when evicting at the basic block granularity than when evicting at the cache line or combination of basic block and cache line granularity. Other applications also exhibit a similar trade-off curve. The invalidation threshold providing the best performance across the 9 data center applications we studied varies between 45-65%.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>The Invalidation instruction. We propose a new invalidation instruction, invalidate that takes the address of a cache line</head><p>as an operand and invalidates it if the cache line resides in the Icache. Our proposed invalidate instruction exhibits one key difference compared to existing cache line flushing instructions (e.g., the clflush instruction on Intel processors) in that it does not invalidate the cache line from other caches in the cache hierarchy. Instead, our proposed invalidate instruction invalidates the cache line only in the local I-cache, thereby avoiding costly cache-coherency transactions and unnecessary invalidations in remote caches. Furthermore, the invalidate instruction has low latency as it does not have to wait for the potentially dirty cache line to be written back to the lower cache levels. Instead, invalidate can be regarded as a hint that can be freely reordered with fences and synchronization instructions. Intel recently introduced <ref type="bibr">[98]</ref> such an invalidation instruction called (cldemote) slated to be supported in its future servers, and hence Ripple will be readily implementable on such upcoming processors.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>IV. EVALUATION</head><p>In this section, we first describe our experimental methodology and then evaluate Ripple using key performance metrics.</p><p>Trace collection. We collect the execution trace of data center applications using Intel Processor Trace (PT). Specifically, we record traces for 100 million instructions in the application's steady-state containing both user and kernel mode instructions as Intel PT allows to capture both. We find that for most applications, the percentage of kernel mode instruction induced I-cache misses is small (&lt; 1%). However, for drupal, mediawiki, and wordpress, kernel code is responsible for 15% of all I-cache misses.</p><p>Simulation. At the time of this writing, no commerciallyavailable processor supports our proposed invalidate instruction, even though future Intel processors will support the functionally-equivalent cldemote instruction <ref type="bibr">[101]</ref>. To simulate this invalidate instruction, we evaluate Ripple using simulation. This also allows us to evaluate additional replacement policies and their interactions with Ripple. We extend the ZSim simulator <ref type="bibr">[86]</ref> by implementing our proposed invalidate instruction. We list several important parameters of the trace- driven out-of-order ZSim simulation in Table <ref type="table">II</ref>. We implement Ripple on the L1 I-cache in our experiments.</p><p>Data center applications and inputs. We use nine widelyused data center applications described in &#167;II to evaluate Ripple.</p><p>We study these applications with different input parameters offered to the client's load generator (e.g., number of requests per second or the number of threads). We evaluate Ripple using different inputs for training (profile collection) and evaluation. We now evaluate Ripple using key performance metrics on all nine data center applications described in Sec. II. First, we measure how much speedup Ripple provides compared to ideal and other prior cache replacement policies. Next, we compare L1 I-cache MPKI reduction (%) for Ripple, ideal, and other policies for different prefetching configurations. Then, we evaluate Ripple's replacement-coverage and replacementaccuracy as described in Sec. III-C. Next, we measure how much extra static and dynamic instructions Ripple introduces into the application binary. Finally, we evaluate how Ripple performs across multiple application inputs.</p><p>Speedup. We measure the speedup (i.e., percentage improvement in instructions per cycle [IPC]) provided by Ripple over an LRU baseline. We also compare Ripple's speedup to speedups provided by the prefetch-aware ideal replacement policy as well as four additional prior cache replacement policies including Hawkeye/Harmony, DRRIP, SRRIP, and GHRP, whose details were discussed in &#167;II. To show that Ripple's speedup is not primarily due to the underlying hardware replacement policy, we also provide Ripple's speedup with two different underlying hardware replacement policies (random and LRU). Finally, we measure the speedups for all replacement policies by altering the underlying I-cache prefetching mechanisms (no prefetching, NLP, and FDIP). Fig. <ref type="figure">7</ref> shows the speedup results. Ripple, with an underlying LRU-based hardware replacement policy (i.e., Ripple-LRU in Fig. <ref type="figure">7</ref>), always outperforms all prior replacement policies across all different prefetcher configurations. In particular, Ripple-LRU provides on average 1.25% (no prefetching), 2.13% (NLP), 1.4% (FDIP) speedups over a pure-LRU replacement policy baseline. These speedups correspond to 37% (no prefetching), 55% (NLP), and 44% (FDIP) of the speedups of an ideal cache replacement policy. Notably, even Ripple-Random, which operates with an underlying random hardware replacement policy (which itself is on average 1% slower than LRU), provides 0.86% average speedup over the LRU baseline across the three different I-cache prefetchers. In combination with Ripple, Random becomes a feasible replacement policy that eliminates all meta-data storage overheads in hardware.</p><p>The performance gap between Ripple and the ideal cache replacement policy stems from two primary reasons. First, Ripple cannot cover all eviction windows via software invalidation as covering all eviction windows requires Ripple to sacrifice eviction accuracy which hurts performance. Second, software invalidation instructions inserted by Ripple introduce static and dynamic code bloat, causing additional cache pressure that contributes to the performance gap (we quantify this overhead later in this section).</p><p>I-cache MPKI reduction. Fig. <ref type="figure">8</ref> shows the L1 I-cache miss reduction provided by Ripple (with underlying hardware replacement policies of LRU and Random) and the prior work policies. As shown, Ripple-LRU reduces I-cache misses over all prior policies across all applications. Across different prefetching configurations, Ripple can avoid 33% (no prefetching), 53% (NLP), and 41% (FDIP) of I-cache misses that are avoided by the ideal replacement policy. Ripple reduces I-cache MPKI regardless of the underlying replacement policy. Even when the underlying replacement policy is random (causing 12.71% more misses in average than LRU), Ripple-Random incurs 9.5% fewer misses on average than LRU for different applications and prefetching configurations.</p><p>Replacement-Coverage. As described in &#167;III-C, Ripple's coverage is the percentage of all replacement decisions (over the program life time) that were initiated by Ripple's invalidations. Fig. <ref type="figure">9</ref> shows Ripple's coverage for all applications. As shown, Ripple achieves on average more than 50% coverage. Only for three HHVM applications (i.e., drupal, mediawiki, and wordpress), Ripple's coverage is lower than 50%, as for these applications Ripple does not insert invalidate instructions on the just-in-time compiled basic blocks. Just-in-time (Jit) compiled code may reuse the same instruction addresses for different basic blocks over the course of an execution rendering compile-time instruction injection techniques challenging. Nevertheless, even for these Jit applications there remains enough static code that Ripple is able to optimize.</p><p>Accuracy. In Fig. <ref type="figure">10</ref>, we show Ripple's replacement-accuracy (as defined in &#167;III-C). As shown, Ripple achieves 92% accuracy on average (with a minimum improvement of 88%). Ripple's accuracy is on average 14% higher than LRU's average accuracy (77.8%). Thanks to its higher accuracy, Ripple avoids many inaccurate replacement decisions due to the underlying LRUbased hardware replacement policy (which has an average accuracy of 77.8%), and, therefore, the overall replacement accuracy for Ripple-LRU is on average 86% (8.2% higher than the LRU baseline).</p><p>Instruction overhead. Fig. <ref type="figure">11</ref> and 12 quantify the static and dynamic code footprint increase introduced by the injected invalidate instructions. The static instruction overhead of Ripple is less than 4.4% for all cases while the dynamic instruction overhead is less than 2% in most cases, except for verilator. Over fetch directed instruction prefetching baseline, Ripple provides 1.4% speedup compared to 3.16% ideal speedup on average. Fig. <ref type="figure">7</ref>: Ripple's speedup compared to ideal and state-of-the-art replacement policies over an LRU baseline (with different hardware prefetching): On average, Ripple provides 1.6% speedup compared to 3.47% ideal speedup.</p><p>For this application, Ripple executes 10% extra instructions to invalidate cache lines. This is because for verilator, Ripple covers almost all replacement policy decisions via software invalidation (98.7% coverage as shown in Fig. <ref type="figure">9</ref>). Similarly, Ripple's accuracy for verilator is very high (99.9% as shown in Fig. <ref type="figure">10</ref>). Therefore, though Ripple executes a relatively greater number of invalidation instructions for verilator, it does not execute unnecessary invalidation instructions.</p><p>Profiling and offline analysis overhead. Ripple leverages Intel PT to collect basic block traces from data center application executions because of its low overhead (less than 1%) and adoption in production settings <ref type="bibr">[19,</ref><ref type="bibr">28]</ref>. While Ripple's extraction and analysis on this trace takes longer (up to 10 minutes), we do not expect that this expensive analysis will be deployed in production servers. Instead, we anticipate the extraction, analysis, and invalidation injection component of Ripple will be performed offline, similar to how existing profile-guided optimizations for data center applications are performed <ref type="bibr">[17,</ref><ref type="bibr">30,</ref><ref type="bibr">54,</ref><ref type="bibr">75,</ref><ref type="bibr">76]</ref>. Therefore, we consider the overhead for Ripple's offline analysis acceptable.</p><p>Invalidation vs. reducing LRU priority. When the underlying hardware cache replacement policy is LRU, moving a cache line to the bottom of the LRU chain is sufficient to cause eviction. This LRU-specific optimization improved Ripple's IPC speedup from 1.6% to 1.7% (apart from verilator, all other applications benefited from this optimization). This shows that Ripple's profiling mechanism works well independent of the particular eviction mechanism.</p><p>Performance across multiple application inputs. We investigate Ripple's performance for data center applications with  three separate input configurations ('#1' to '#3'). We vary these applications' input configurations by changing the webpage, the client requests, the number of client requests per second, the number of server threads, random number seeds, and the size of input data. We optimize each application using the profile from input '#0' and measure Ripple's performance benefits for different test inputs '#1, #2, #3'. For each input, we also measure the performance improvement when Ripple optimizes the application with a profile for the same input. As shown in Fig. <ref type="figure">13</ref>, Ripple provides 17% more IPC gains with inputspecific profiles compared to profiles that are not input specific. For brevity, we only show the results for the FDIP baseline.</p><p>Results with the other prefetching baselines are similar.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>V. DISCUSSION</head><p>Ripple generates optimized binaries for different target architectures considering the processor's I-cache size and associativity. Such a process is common in data centers deploying profile-guided <ref type="bibr">[17,</ref><ref type="bibr">62]</ref> and post-link-time-based optimization <ref type="bibr">[75,</ref><ref type="bibr">76]</ref> techniques. Therefore, Ripple can be conveniently integrated into the existing build and optimization processes. Moreover, as the I-cache size (32KB) and associativity (8-way) for Intel data center processors has been stable for the last 10 years, the number of different target architectures that Ripple needs to support is small.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>VI. RELATED WORK</head><p>Instruction prefetching. Hardware instruction prefetchers such as next-line and decoupled fetch directed prefetchers <ref type="bibr">[18,</ref><ref type="bibr">39,</ref><ref type="bibr">82,</ref><ref type="bibr">84,</ref><ref type="bibr">92]</ref> have been pervasively deployed in commercial designs <ref type="bibr">[32,</ref><ref type="bibr">78,</ref><ref type="bibr">85,</ref><ref type="bibr">97]</ref>. While complex techniques <ref type="bibr">[25,</ref><ref type="bibr">26,</ref><ref type="bibr">51,</ref><ref type="bibr">52]</ref> employing record and replay prefetchers are highly effective in reducing I-cache misses, they require impractical on-chip metadata storage. Branch predictor-guided prefetchers <ref type="bibr">[9,</ref><ref type="bibr">60,</ref><ref type="bibr">61]</ref>, on the other hand, follow the same principle as FDIP to reduce on-chip metadata storage, however, they also require a complete overhaul of the underlying branch target prediction unit. Even recent proposals <ref type="bibr">[8,</ref><ref type="bibr">29,</ref><ref type="bibr">31,</ref><ref type="bibr">33,</ref><ref type="bibr">70,</ref><ref type="bibr">72,</ref><ref type="bibr">83,</ref><ref type="bibr">89]</ref> from 1st Instruction Prefetching Championship (IPC1) require kilobytes of extra on-chip storage to provide nearideal performance even on workloads where FDIP with a large enough fetch target queue provides most of the potential performance benefit <ref type="bibr">[38]</ref>. Hybrid hardware-software prefetchers <ref type="bibr">[12,</ref><ref type="bibr">13,</ref><ref type="bibr">55,</ref><ref type="bibr">68,</ref><ref type="bibr">71]</ref> analyze a program's control flow information in software and inject dedicated prefetching instructions in code which does not exist in today's hardware. In contrast, we show that instruction prefetchers alone do not close the performance gap due to wasteful evictions that must be handled by smarter cache line replacement.</p><p>Cache replacement policies. Heuristic-based hardware data cache replacement policies have been studied for a long time, including LRU and its variations <ref type="bibr">[47,</ref><ref type="bibr">63,</ref><ref type="bibr">73,</ref><ref type="bibr">91,</ref><ref type="bibr">106]</ref>, MRU <ref type="bibr">[80]</ref>, re-reference interval prediction <ref type="bibr">[43]</ref>, reuse prediction <ref type="bibr">[22,</ref><ref type="bibr">23,</ref><ref type="bibr">66]</ref> and others <ref type="bibr">[6,</ref><ref type="bibr">27,</ref><ref type="bibr">35,</ref><ref type="bibr">56,</ref><ref type="bibr">81,</ref><ref type="bibr">87,</ref><ref type="bibr">96,</ref><ref type="bibr">99]</ref>. Learningbased data cache replacement policies <ref type="bibr">[40,</ref><ref type="bibr">41,</ref><ref type="bibr">53,</ref><ref type="bibr">107]</ref> consider replacement as a binary classification problem of cache-friendly or cache-averse. Recent methods introduce machine learning techniques like perceptrons <ref type="bibr">[45,</ref><ref type="bibr">100]</ref> and genetic algorithms <ref type="bibr">[44]</ref>. Some learning-based policies use information of Belady's optimal solution <ref type="bibr">[15]</ref>, including Hawkeye <ref type="bibr">[40]</ref>, Glider <ref type="bibr">[90]</ref> and Parrot <ref type="bibr">[65]</ref>. However, these policies are mostly designed for data caches and do not work well for instruction caches as we show earlier (Sec. II). We also propose a profile-guided approach that can work on top of any of these policies.</p><p>Prefetch-aware replacement policy. Prefetch-aware replacement policies focus on avoiding cache pollution caused by inaccurate prefetches. Some prefetch-aware policies <ref type="bibr">[36,</ref><ref type="bibr">37,</ref><ref type="bibr">57]</ref> get feedback from prefetchers to identify inaccurate prefetches, and need co-design or prefetcher modifications. Others <ref type="bibr">[88,</ref><ref type="bibr">94,</ref><ref type="bibr">108]</ref> work independently from the prefetcher and estimate prefetch accuracy from cache behavior. With prefetching, Belady's optimal policy <ref type="bibr">[15]</ref> becomes incomplete as it cannot distinguish easy-to-prefetch cache lines from hard-to-prefetch cache lines <ref type="bibr">[94,</ref><ref type="bibr">108]</ref>. To address this limitation, Demand-MIN <ref type="bibr">[41]</ref> revised Belady's optimal policy to accommodate prefetching and proposed a program counter (PC) classification based predictor, Harmony to emulate the ideal performance. In this work, we not only revise Demand-MIN to cover an extra corner case, but also show that a PCclassification based predictor performs poorly for I-cache. We address this imprecision and effectively emulate optimal I-cache behavior in our work via a profile-guided software technique.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>VII. CONCLUSION</head><p>Modern data center applications have large instruction footprints, leading to significant I-cache misses. Although numerous prior proposals aim to mitigate I-cache misses, they still fall short of an ideal cache. We investigated why existing I-cache miss mitigation mechanisms achieve sub-optimal speedup, and found that widely-studied instruction prefetchers incur wasteful prefetch-induced evictions that existing replacement policies do not mitigate. To enable smarter evictions, we proposed Ripple, a novel profile-guided replacement technique that uses program context to inform the underlying replacement policy about efficient replacement decisions. Ripple identifies program contexts that lead to I-cache misses and sparingly injects "cache line eviction" instructions in suitable program locations at link time. We evaluated Ripple using nine popular data center applications and demonstrated that it is replacement policy agnostic, i.e., it enables any replacement policy to achieve speedup that is 44% closer to that of an ideal I-cache.</p></div></body>
		</text>
</TEI>
