Lecture 11 - Florida State University

Lecture 11 - Florida State University

LECTURE 11 Memory Hierarchy MEMORY HIERARCHY When it comes to memory, there are two universally desirable properties: Large Size: ideally, we want to never have to worry about running out of memory. Speed of Access: we want the process of accessing memory to take as little time as possible. But we cannot optimize both of these properties at the same time. As our memory size increases, the time to find a memory location and access it grows as well. The goal of designing a memory hierarchy is to simulate having unlimited amounts of fast memory. LOCALITY To simulate these properties, we can take advantage of two forms of locality. Temporal Locality: if an item is referenced, it will tend to be referenced again soon. The location of the variable counter may be accessed frequently in a loop. A branching instruction may be accessed repeatedly in a given period of time.

Spatial Locality if an item is referenced, items whose addresses are close by will tend to be referenced soon. If we access the location of A[0], we will probably also be accessing A[1], A[2], etc. Sequential instruction access also exhibits spatial locality. MEMORY HIERARCHY A memory hierarchy, consisting of multiple levels of memory with varying speed and size, exploits these principles of locality. Faster memory is more expensive per bit, so we use it in smaller quantities. Slower memory is much cheaper so we can afford to use a lot of it. The goal is to, whenever possible, keep references in the fastest memory. However, we also want to minimize our overall memory cost. MEMORY HIERARCHY All data in a level is typically also found in the next largest level. We keep the smallest, faster memory unit closest to the processor. The idea is that our access time during a running program is defined primarily by the access time of the level 1 unit. But our memory capacity is as large as the

level n unit. MEMORY HIERARCHY Processo r Level 1 Level 2 Level 3 The unit of data that is transferred between two levels is fixed in size and is called a block, or a line. MEMORY HIERARCHY Speed Processo r Size Cost ($/bit)

Technology Fastest Memory Smallest Highest SRAM DRAM Memory Slowest Biggest Memory Lowest Magnetic Disk

MEMORY HIERARCHY There are four technologies that are used in a memory hierarchy: SRAM (Static Random Access Memory): fastest memory available. Used in memory units close to the processor called caches. Volatile. DRAM (Dynamic Random Access Memory): mid-range. Used in main memory. Volatile. Flash: Falls between DRAM and disk in cost and speed. Used as non-volatile memory in personal mobile devices. Magnetic Disk: slowest memory available. Used as non-volatile memory in a server or PC. MEMORY HIERARCHY Technology Typical Access Time $ per GiB in 2012 SRAM 0.5-2.5 ns $500 - $1000 DRAM

50-70 ns $10 - $20 Flash 5,000-50,000 ns $0.75 - $1.00 Magnetic Disk 5,000,000 20,000,000 ns $0.05 - $0.10 SRAM Static Random Access Memory Used in caches. Has a single access port for reads/writes. Access time is 5-10 times faster than DRAM. Semiconductor memory that uses ~6 transistors for each bit of data. Data is maintained as long as power to the SRAM chip is provided;

no need to refresh. DRAM Dynamic Random Access Memory Used for main memory. Requires a single transistor per bit (much denser and cheaper than SRAM). Data is lost after being read, so we must refresh after a read by writing back the data. The charge can be kept for several milliseconds before a refresh is required. About 1%-2% of the cycles are used to refresh accomplished by reading a row of data and writing it back. MEMORY HIERARCHY TERMS Hit: item found in a specified level of the hierarchy. Miss: item not found in a specified level of the hierarchy. Hit time: time required to access the desired item in a specified level of the hierarchy (includes the time to determine if the access is a hit or a miss). Miss penalty: the additional time required to service the miss. Hit rate: fraction of accesses that are in a specified level of the hierarchy. Miss rate: fraction of accesses that are not in a specified level of the hierarchy. Block: unit of information that is checked to reside in a specified level of the hierarchy and is retrieved from the next lower level on a miss.

MEMORY HIERARCHY The key points so far: Memory hierarchies take advantage of temporal locality by keeping more recently accessed data items closer to the processor. Memory hierarchies take advantage of spatial locality by moving blocks consisting of multiple contiguous words in memory to upper levels of the hierarchy. Memory hierarchy uses smaller and faster memory technologies close to the processor. Accesses that hit in the highest level can be processed quickly. Accesses that miss go to lower levels, which are larger but slower. If the hit rate is high enough, the memory hierarchy has an effective access time close to that of the highest (and fastest) level and a size equal to that of the lowest (and largest) level. Memory is typically a true hierarchy, meaning that data cannot be present in level i unless it is also present in level i+1. EXERCISES Which of the following are true statements? Caches take advantage of temporal locality. On a read, the value returned depends on which blocks are in the cache. Most of the cost of the memory hierarchy is at the highest level. EXERCISES Which of the following are true statements? Caches take advantage of temporal locality. True.

On a read, the value returned depends on which blocks are in the cache. False. Most of the cost of the memory hierarchy is at the highest level. False. EXERCISES SRAM is commonly used to implement small, fast, on-chip caches while DRAM is used for larger, slower main memory. In the past, a common design for supercomputers was to build machines with no caches and main memories made entirely out of SRAM (the Cray C90, for example, a very fast computer in its day). If cost were no object, would you still want to design a system this way? EXERCISES SRAM is commonly used to implement small, fast, on-chip caches while DRAM is used for larger, slower main memory. In the past, a common design for supercomputers was to build machines with no caches and main memories made entirely out of SRAM (the Cray C90, for example, a very fast computer in its day). If cost were no object, would you still want to design a system this way? A large SRAM chip uses a lot more area than a comparably-sized DRAM chip. Larger SRAM area means slower lookup times and higher power consumption. SRAM does not need refreshing. Needs only steady state power.

CACHES Well begin by looking at the most basic cache. Lets say were running a program that, so far, has referenced words. These could be independent integer variables, for example. At this point, our cache might look like this. That is, every reference made so far has been moved into the cache to take advantage of temporal locality. What happens when our program references ? CACHES A reference to causes a miss, which forces the cache to fetch from some lower level of the memory hierarchy, presumably main memory. Two Questions: 1. How do we know if an item is present in the cache? 2. How do we find the item in the cache? CACHES A reference to causes a miss, which forces the cache to fetch from some lower level of the memory hierarchy, presumably main memory. Two Questions:

1. How do we know if an item is present in the cache? 2. How do we find the item in the cache? One Answer: If each word can go in exactly one place in the cache, then we can easily find it in the cache. DIRECT-MAPPED CACHES The simplest way to assign a location in the cache for each word in memory is to assign the cache location based on the address of the word in memory. This creates a direct-mapped cache every location in memory is mapped directly to one location in the cache. A typical direct-mapped cache uses the following mapping: Conveniently, entering a block into a cache with entries means just looking at the lower bits of the block address. DIRECT-MAPPED CACHE Here is an example cache which contains entries. Words in memory are mapped to a particular cache index if the lower 3 bits of the address matches the index. So, now we know where to find the data

but we still have to answer the following question: how do we know if the data we want is in the cache? TAGS To verify that a cache entry contains the data were looking for, and not data from another memory address with the same lower bits, we use a tag. A tag is a field in a table which corresponds to a cache entry and gives extra information about the source of the data in the cache entry. What is an obvious choice for the tag? TAGS To verify that a cache entry contains the data were looking for, and not data from another memory address with the same lower bits, we use a tag. A tag is a field in a table which corresponds to a cache entry and gives extra information about the source of the data in the cache entry. What is an obvious choice for the tag? The upper bits of the address of the block! TAGS For instance, in this particular example,

lets say the block at address 01101 is held in the cache entry with index 101. The tag for the cache entry with index 101 must then be 01, the upper bits of the address. Therefore, when looking in the cache for the block at address 11101, we know that we have a miss because 11 != 01. VALID BIT Even if there is data in the cache entry and a tag associated with the entry, we may not want to use the data. For instance, when a processor has first started up, the cache entries and tag fields are meaningless. Generally speaking, a valid bit associated with the cache entry can be used to ensure that an entry is valid. EXERCISE Lets assume we have an 8-entry cache with the initial state shown to the right. Lets fill in the cache according to the references that come in listed in the table below. Note that initially the valid-bit entries are all N for not valid.

EXERCISE The first reference is for the block at address 22, which uses the lower bits 110 to index into the cache. The 110 cache entry is not valid so this is a miss. We need to retrieve the contents of the block at address 22 and place it in the cache entry. EXERCISE The block at address 22 is now placed in the data entry of the cache and the tag is updated to the upper portion of the address, 10. Also, the valid bit is set to Y. Now, we have a reference to the block at address 26. What happens here? EXERCISE We have a miss, so we retrieve the data from address 26 and place it in the cache entry. We also update the tag and valid bit. Now, we have a reference the block at address 22 again. Now what happens? EXERCISE The correct data is already in the cache! We dont have to update the contents or fetch anything from main memory. Similarly, we will have another reference to the block at address 26. We do not need to update the cache at all.

EXERCISE Now, we have a reference to the block at address 16. Its associated cache entry is invalid, so we will need to fetch the data from main memory and update the entry. EXERCISE Now, we have a reference to the block at address 3. Its associated cache entry is invalid, so we will need to fetch the data from main memory and update the entry. EXERCISE A reference to the block at address 16 causes a hit (as we have already pulled this data into the cache) so we do not have to make any changes. EXERCISE Now, we get something interesting. We have a reference to the block at address 18. The lower bits used to index into the cache are 010. As these are also the lower bits of address 26, we have a valid entry but its not the one we want. Comparing the tag of the entry with the upper portion of 18s binary representation tells us we have a miss. EXERCISE We fetch the data at address 18 and update the cache entry to hold

this data, as well as the correct tag. Note now that a reference to the block at address 26 will result in a miss and well have to fetch that data again. PHYSICAL ADDRESS TO CACHE To the right is a figure showing how a typical physical address may be divided up to find the valid entry within the cache. The offset is used to indicate the first byte accessed within a block. Its size is . For example, a block containing 4 bytes does not need to consider the lower 2 bits of the address to index into the cache. The cache index, in this case, is a 10-bit wide lower portion of the physical address (because there are entries). The tag is the upper 20 bits of the physical address. OFFSET Consider a scheme where a block of memory contains 2 words. Each word is 4 bytes. Bytes are the smallest

addressable unit of memory so a block starting at address 34892896 contains 8 byteaddressable locations. Because , we need 3 bits to individually identify the addresses in the block. The 4th bit is the first bit common to all addresses in the block. Therefore, the offset to the index is given by . 348928940000 348928950000 348928960000 348928970000 348928980000 Block 348928990000 348929000000 348929010000 348929020000 348929030000 348929040000

348929050000 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 0001 0001 0001 0001 0001 0001 0001 0001 0001

0001 0001 0001 0100 0100 0100 0100 0100 0100 0100 0100 0100 0100 0100 0100 0110 0110 0110 0110 0110 0110 0110 0110

0110 0110 0110 0110 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 0101 0101 0110 0110 0110 0110 0110

0110 0110 0110 0110 0110 1110 1111 0000 0001 Word 1 0010 0011 0100 0101 Word 2 0110 0111 1000 1001 BLOCKS IN A CACHE Weve mostly assumed so far that a block contains one word, or 4 bytes. In reality, a block contains several words. Assuming we are using 32-bit addresses, consider a direct-mapped

cache which holds blocks and each block contains words. How many bytes are in a block? bytes per block. How big does a tag field need to be? 32 (n + m + 2). A block has a 32-bit address. We do not consider the lower m+2 bits because there are bytes in a block. We need n bits to index into the cache, m bits to identify the word. EXERCISE How many total bits are required for a direct-mapped cache with 16 KB of data and 4-word blocks, assuming a 32-bit address? We know that 16 KB is 4K words, which is words, and, with a block size of 4 words (), blocks. Each block contains 4 words, or 128 bits, of data. Each block also has a tag that is 32-10-2-2 bits long, as well as one valid bit. Therefore, the total cache size is Or, 18.4 KB cache for 16KB of data. EXERCISE Consider a cache with 64 blocks and a block size of 16 bytes (4 words). What block number does byte address 1200 map to? First of all, we know the entry into the cache is given by ( ) % ( h h) Where the block address is given by .

So, the block address is . This corresponds to block number . This block maps all addresses between 1200 and 1215. BLOCK SIZE AND MISS RATE A larger block size means we bring more contiguous bytes of memory in when we fetch a block. This can lower our miss rate as it exploits spatial locality. However, in a fixed-size cache, a larger block size means less blocks in a cache therefore, we may have blocks competing for cache space more often. Furthermore, a larger block size takes more time to retrieve from main memory in the case of a miss. 64-BYTE BLOCK CACHE Here is a 256-entry cache that has 64-byte block entries. That is, each block is 16 words wide.

We index using an 8-bit portion of the address. The individual bytes of the address are identifiable using the lower 6 bits (). However, we dont want to access every byte. We only EXERCISE Assume a direct-mapped cache with 4 blocks and 8 bytes per block. How is the physical address portioned? Tag bits Fill in the appropriate information for the following memory references: Index bits Address 4 8

12 20 67 Tag Offset bits Index Offset EXERCISE Assume a direct-mapped cache with 4 blocks and 8 bytes per block. How is the physical address portioned? Tag bits Index bits Offset bits 27 [31-5] 2 [4-3]

3 [2-0] Fill in the appropriate information for the following memory references: Address Tag Index Offset 4 0 0 4 8

0 1 0 12 0 1 4 20 0 2 4 67 2

0 3 CACHE MISSES A cache miss occurs when a request for data from the cache cannot be filled because the data is missing from the cache. Lets say we are trying to access some instruction from memory during the IF stage of the pipeline and the cache access results in a miss. In a simple processor, the pipeline is stalled completely while the miss is serviced everything is frozen. At this point, a cache control unit has to instruct main memory to perform a read on the correct instruction and send the results to the cache. CACHE MISSES The steps to be taken on a cache miss: 1. Send the instructions PC value to the memory. The PC value is PC-4 because PC has already been incremented at this point. 2. Instruct main memory to perform a read and wait for memory to complete its access (multiple clock cycles). 3. Write the cache entry, putting data from memory in the data portion, writing the tag, and turning the valid bit on.

4. Restart instruction execution at the first step, refetching the same instruction which is now in the cache. CACHE MISSES Lets consider the effect of cache misses for instructions. Assume our miss penalty is 10 cycles and the miss rate is .10. The average access time for an instruction is given by: So, the number of cycles needed to fetch instructions is: CACHES FOR PIPELINED PROCESSORS In reality, instructions and data have separate caches. This allows us to not only avoid structural hazards (when one instruction is being fetched while another accesses memory in the same cycle), but also fine-tune the specs of the cache for each task. Cycle 1 2 3 4 5

inst1 ID EX MEM WB IF ID EX MEM WB IF ID

EX MEM WB IF ID EX MEM inst2 inst3 inst4 IF 6 7 8

WB CACHES FOR PIPELINED PROCESSORS Assume the following two instructions in a pipelined processor. First, assume both instructions are found in the cache: cycle 1 2 3 4 5 lw $2,0($3) IF ID

EX MEM WB IF ID STALL EX add $4,$2,$1 6 7 MEM WB

Now, assume the second instruction is not in the cache and there is a 2 cycle miss penalty. The load hazard stall is overlapped with the instruction cache miss stall. cycle 1 2 3 4 5 lw $2,0($3) IF ID EX

MEM WB IF STALL STALL ID add 6 7 8 EX MEM

WB FULLY-ASSOCIATIVE CACHE Weve already seen direct-mapped caches, a simple scheme where every block has one particular cache entry where it can be placed. In a fully-associative cache, any block can be found in any entry of the cache. To find a block in the cache, we must search the entire cache therefore, this scheme is only practical for caches with a small number of entries. SET-ASSOCIATIVE CACHE The middle ground between direct-mapped and fully-associative is set-associative. In a set-associative cache, there are a fixed number of entries where a particular block may be found. If a set-associative cache allows n different entries for a block to be found, it is called an nway set-associative cache. An n-way set-associative cache may have some number of sets, each containing n blocks. A block address can be mapped to a particular set, in which the block can be placed in any of the n entries. To find a reference in a set-associative cache, we figure out its set based on the address and then search all of the entries in the set. SET-ASSOCIATIVE CACHE

The example below has a reference with a block address of 12 and each cache organization has 8 entries. In a set-associative cache, the set can be found using the following: SET-ASSOCIATIVE CACHE All placement strategies are really a variation on setassociativity. SET-ASSOCIATIVE CACHE The advantage of increasing the degree of associativity is that, typically, the miss rate will decrease. The disadvantages are: Potential hit time increase. More tag bits per cache block. Logic to determine which block to replace. FOUR-WAY SET-ASSOCIATIVE CACHE Here is a set-associative cache with 256 sets of four blocks each, where each block is one word. The index tells us which set to look in. We need 8 bits for the index because .

The tag of every entry is compared to the upper 22 bits of the address. If there is a match, and the valid bit is set, we have a hit. The mux selects the data of the entry that resulted in a hit. Otherwise, we have a miss. SET-ASSOCIATIVE CACHE Assume a 2-way set-associative cache with 64 sets and 4 words per block. How is the physical address partitioned? Tag bits Fill in the appropriate information for the following memory references: Index bits Address 300 304 1216 4404 4408

Tag Offset bits Index Offset SET-ASSOCIATIVE CACHE Assume a 2-way set-associative cache with 64 sets and 4 words per block. How is the physical address partitioned? Tag bits Index bits Offset bits 22 6 4

Fill in the appropriate information for the following memory references: Address Tag Index Offset 300 0 18 12 304 0

19 0 1216 1 12 0 4404 4 19 4 4408 4 19

8 BLOCK REPLACEMENT Block replacement strategies for direct-mapped are easy: just write to the entry of the block you are bringing into the cache. However, in a set-associative cache, there are multiple block entries that can be used. If the set is full, how do we decide which block should be replaced? Random: choose a block randomly to replace. Easy to implement. Least Recently Used (LRU): replace the least-recently accessed block. Better miss rate than random. Expensive to implement, especially for high associativity. SET-ASSOCIATIVE CACHE Assume a 2-way set-associative cache with 64 sets and 4 words per block. Indicate the result of searching for the reference in the cache.

Addres s Tag Index Offset Result 300 0 18 12 Miss 304 0

19 0 1216 1 12 0 4404 4 19 4 4408 4 19

8 9416 9 12 8 296 0 18 8 304 0 19

0 1220 1 12 4 2248 2 12 8 SET-ASSOCIATIVE CACHE Assume a 2-way set-associative cache with 64 sets and 4 words per block. Indicate the result of searching

for the reference in the cache. Addres s Tag Index Offset Result 300 0 18 12 Miss 304

0 19 0 Miss 1216 1 12 0 Miss 4404 4 19

4 Miss 4408 4 19 8 Hit 9416 9 12 8 Miss 296

0 18 8 Hit 304 0 19 0 Hit 1220 1 12

4 Hit 2248 2 12 8 Miss WRITING TO THE CACHE Writing to the cache is a little more complicated than reading from the cache. Lets say, in the MEM stage of a store word instruction, we write to the data cache. Then, main memory and data cache will have different values for that particular block. In this case, they are said to be inconsistent. There are two solutions to this issue. The method we use becomes our write policy. Write-through Write-back

WRITE POLICIES Write-through Always write data into both the cache and main memory (or the next lower level). Easily implemented. Could slow down the processor use a write buffer to allow the processor to continue executing while the data is written to memory. Cache and memory are always consistent. Write-back Only write the data to the cache block. The updated block is only written back to memory when it is replaced by another block. A dirty bit is used to indicate whether the block needs to be written or not. Reduces accesses to the next lower level. What if the block to be written is not in the cache? WRITE MISS POLICIES Write allocate The block is loaded into the cache on a write miss. Typically used with write back. No-write allocate The block is not loaded into the cache on a write miss. Block simply updated in main memory.

Typically used with write through. WRITE-THROUGH, NO-WRITE ALLOCATE Assume a 2-way set-associative cache with 64 cache sets, 4 words per block, and an LRU replacement policy. Fill in the appropriate information for the following memory references. R/W Addr W 300 R 304 R 4404 W

4408 W 8496 R 8500 Tag Index Offset Result Memr ef Updat e Cache ? WRITE-THROUGH, NO-WRITE

ALLOCATE Assume a 2-way set-associative cache with 64 cache sets, 4 words per block, and an LRU replacement policy. Fill in the appropriate information for the following memory references. R/W Addr Tag Index Offset Result Memr ef Updat e Cache ? W 300

0 18 12 Miss No R 304 0 19 0 R 4404

4 19 4 W 4408 4 19 8 W 8496 8 19 0

R 8500 8 19 4 Yes WRITE-THROUGH, NO-WRITE ALLOCATE Assume a 2-way set-associative cache with 64 cache sets, 4 words per block, and an LRU replacement policy. Fill in the appropriate information for the following memory references. R/W Addr Tag Index

Offset Result Memr ef Updat e Cache ? W 300 0 18 12 Miss Yes

No R 304 0 19 0 Miss Yes Yes R 4404 4 19

4 Miss Yes Yes W 4408 4 19 8 Hit Yes Yes

W 8496 8 19 0 Miss Yes No R 8500 8 19 4

Miss Yes Yes WRITE-BACK, WRITE ALLOCATE Assume a 2-way set-associative cache with 64 cache sets, 4 words per block, and an LRU replacement policy. Fill in the appropriate information for the following memory references. R/W Addr Tag Index Offset W 300

0 18 12 R 304 0 19 0 R 4404 4 19

4 W 4408 4 19 8 W 8496 8 19 0 R 8500

8 19 4 Result Memr ef Updat e Cache ? WRITE-BACK, WRITE ALLOCATE Assume a 2-way set-associative cache with 64 cache sets, 4 words per block, and an LRU replacement policy. Fill in the appropriate information for the following memory references. R/W Addr Tag

Index Offset Result Memr ef Updat e Cache ? W 300 0 18 12 Miss

Yes Yes R 304 0 19 0 Miss Yes Yes R 4404 4

19 4 Miss Yes Yes W 4408 4 19 8 Hit No

Yes W 8496 8 19 0 Miss Yes Yes R 8500 8 19

4 Hit No No MEMORY HIERARCHY MISSES Not all misses are equal. We can categorize them in the following way: Compulsory Misses Caused by first access to block. Possibly decreased by increasing block size. Capacity Misses Caused when memory level cannot contain all blocks needed during execution of process. Can be decreased by increasing cache size. Conflict Misses Occur when too many blocks compete for same entry in cache. Can be decreased by increasing associativity. CRITICAL WORD FIRST AND

EARLY RESTART One way to reduce the penalty for misses is to reduce the time spent waiting for the actual request data, rather than the whole block of data. Critical word first means to request the missed word first from the next memory hierarchy level to allow the processor to continue while filling in the remaining words in the block, usually in a wraparound fill manner. Early restart means to fetch the words in the normal order, but allow the processor to continue once the requested word arrives. MULTILEVEL CACHES As the clock rate of processors has increased faster than the time to access main memory (DRAM), multiple levels of caches have been introduced. Three levels of cache all on the same chip are now common, where there are separate L1 instruction and data caches and unified L2 and L3 caches. The L1 cache is typically much smaller than L2 cache with lower associativity to provide faster access times. Same with L2 and L3. The L1 caches typically have smaller block sizes than L2 caches to have a shorter miss penalty. Same with L2 and L3. Lower cache levels being much larger and having higher associativity than higher cache levels decreases their misses, which have higher miss penalties. MULTILEVEL CACHE

PERFORMANCE The miss penalty of an upper level cache is the average access time of the next lower level cache. where 1 =2 +2 ( 2 ) What is the average access time given that the L1 hit time is 1 cycle, the L1 miss rate is 0.05, the L2 hit time is 4 cycles, the L2 miss rate is 0.25, and the L2 miss penalty is 50 cycles? MULTILEVEL CACHE PERFORMANCE The miss penalty of an upper level cache is the average access time of the next lower level cache. where 1 =2 +2 ( 2 ) What is the average access time given that the L1 hit time is 1 cycle, the L1 miss rate is 0.05, the L2 hit time is 4 cycles, the L2 miss rate is 0.25, and the L2 miss penalty is 50 cycles? =1+.05 ( 4 +.25 50 )=1.85 MULTILEVEL CACHE PERFORMANCE

Local Miss Rate: the fraction of references to one level of a cache that miss. 2 2 = Example: 2 Global Miss Rate: the fraction of references that miss in all levels of a multilevel cache. =1 2 Example: IMPROVING CACHE PERFORMANCE Techniques for reducing the miss rate: Increase the associativity to exploit temporal locality. Increase the block size to exploit spatial locality. Techniques for reducing the miss penalty: Use wrap-around filling of a line (early restart and critical word first). Use multilevel caches.

Techniques for reducing the hit time: Use small and simple L1 caches.

Recently Viewed Presentations