CSE141L Final: Processor Benchmarking


CSE 141L Homepage | Lab Overview | Lab Description


Deadline:
  1. Progress Report: Sun, May 28th at 11:59PM
Changes:
  1. None.

Lab Overview:

Congratulations! Now that you have a correctly working processor, we are almost at the end of our development cycle.

Since we want to be able to tout the excellent performance of our processors in our upcoming press release, you are going to need to perform a few more benchmarks to gauge your processor's speed. If you are unsatisfied with your results, you may wish to perform various optimizations in order to improve your performance. If you are feeling up to the challenge, you may even join the multi-core revolution and make your processor a dual-, quad-, or even higher core system! Good luck.

Lab Deliverables:

In this lab you will be demonstrating the final result of your processor implementation. Your main focus will be on benchmarking your processor.

You will need to turn in an electronic copy of your progress reporti via moodle by the deadline. The answers to the questions should be in a PDF file. The Verilog files (as well as your PDF) should be included in a ZIP file with the following name convention: cse141L-final-lastname1-firstname1-AND-lastname2-firstname2.zip.


Lab Description:

Jump to: Performance Benchmarking | Uniqueness of Design | Processor Optimization (Optional) | Multi-core Implementation (Optional)

Performance Benchmarking

For the official performance evaluation, you will be using the following benchmarks:
  1. fib_native: Your native implementation of Fibonacci (i.e. written in your ISA). It must be able to handle arbitrary inputs such as: -1, 2, 9, and 31.
  2. fib_sg: The SuperGarbage implementation of Fibonacci (i.e. run through your VirtualMachine). This also must be able to handle arbitrary inputs.
  3. sort_sg: The SuperGarbage implementation of Sort (i.e. app2). It is enough to correctly process the given app2.in input data.
Q1 What is the maximum operating frequency of your processor? For the 3 benchmarks, fill out the following table for the performance of your processor. The input to fib_native and fib_sg should both be 12 (and the result should be 144) while sort_sg should use the app2.in input file from lab 3, part B.
Frequency: fib_sg fib_native sort_sg
# of Cycles for the Execution
     
Dynamic Instruction Count
     
Cycles Per Instruction (CPI)
     
Execution Time
     
Tested and Working in Timing Simulation (testing with smaller input ok)?
     
Q2 Briefly describe your methodology for measuring the number of cycles for execution and the dynamic instruction count that you reported in Q1.
Q3 For the fib_sg and fib_native benchmarks, grab a screenshot showing the final result of the calculations. Again, the input should be 12 which should produce a result of 144.
Q4 Fill out the following table on resource usage of your design. If there are any other resources we missed here, include them.
Resource Type Used Available Utilization
Logic Cells      
Registers      
I/O Pins      
Total RAM Block Bits      
DSP Block 9-bit elements      
Q5 What is the critical path in your processor implementation? Draw it on a copy of your finalized datapath schematic.

Uniqueness of Design

Please answer the following question about the unique aspects of your implementation. This will help us in determining special awards that will be given out based on your designs.

Q6 Please describe any feature(s) of your processor or implementation that would be considered novel, cool, unusual and/or different than the "average" 141L project. For example, any branch prediction/BTB scheme, pipelining tricks, shadow bits, interesting instructions, use of special purpose registers, interesting addressing modes, banking, I/O devices, implementation of multicore, etc.

All work beyond this point is optional.


Optimize Your Processor (Optional)

As you will recall from CSE141, execution time is a function of 3 factors:
  1. Cycle Time
  2. CPI
  3. (Dynamic) Instruction Count

Therefore, if you wish to improve the performance of your processor, you can do so by improving one of these three things. Remember that your grade depends mostly on you having a correctly working processor. Before you start optimizing your processor, be sure to save all the files for your correctly operating processor. Also, as you may take several stages of optimization, make sure you save your files after each stage.

Cycle Time

The cycle time of your processor not only depends on your design, but also on how Altra turns your design into hardware. Since the quality of the output of the Altra tools depends on the optimization parameters you give them, you can try different options to get better results. The easiest thing to do (and the one you should try first) is to set aggressive timing constraints to be met. Setting tougher constraints will force Altra to work longer and not give up on a suboptimal design. While this option of improving cycle time does not require modifications to your processor, it does come at a cost: computation time.

There may be other options available in Altra tools (such as "Timing Advisor") to explore. You may want to speak with your classmates to find out about anecdotal experiences with various parameters.

As you reach diminishing returns from tweaking Quartus II synthesis and implementation parameters, the next step is to try to improve the cycle time of your processor by modifying the design itself (i.e. your Verilog implementation). Remember that it is the critical path that ultimately determines the operating frequency. Your efforts should therefore be aimed at reducing the critical path using such techniques as MUX reordering, moving logic across pipeline stages, or introducing more pipelining. By examining the timing reports and other Quartus II outputs, you can determine the parts of your design (e.g., the critical path) that you may want to focus on optimizing. You often can rewrite the code to reduce critical paths (e.g., reordering muxes, or moving logic across pipeline stages, or pipelining more.) Again, before you start changing your design, make sure to save your work!

Cycles Per Instruction (CPI)

While the opportunities of significantly reducing your CPI are limited, there are still several techniques you might try. One area of possible optimization is in branch prediction. The P-bit of your ISA is a static prediction so it may not always be very accurate. Adding an advanced dynamic branch predictor to your front end may yield much better prediction results. Loads and stores are another source of optimization. Stores can be optimized to take only a single cycle by not waiting for the store to go to memory before beginning execution of the next instruction. Loads can also be pipelined. Oh, and did we mention that you should save your work often?

Dynamic Instruction Count

The final area of optimization you might explore is reducing the instruction count. Improvements in this area will come from you improving your skills as a compiler and crafting better assembly code. Some of the techniques discussed in CSE141, such as loop unrolling and loop fusion, may yield better assembly code. You may also want to consider changing the way you handle load of immediates. By storing them in memory and using a basic "load" instruction, you may be able to significantly reduce the number of instructions in your programs. One thing to keep in mind when doing assembly optimizations is that you should first think about the performance impact of your change. Reducing your assembly code length by 2 instructions may not be worth the time and effort spend meticulously hand-optimizing the code.

Also, you should note that in the course of your optimization, you should not change the basic algorithms being used for the benchmarks. For example, there are much better ways of calculating fibonacci numbers but your algorithm still needs to be recursive and do the same number of comparisons as the original version. Also, you should not try to optimize the SuperGarbage binaries although it is acceptable to optimize VirtualMachine. Finally, don't forget to make sure that your "optimizations" don't change the end result of the algorithm (i.e. they should still be correct ;)).


Multi-core Processor (Optional)

Nearly all mainstream processors sold today consist of multiple cores on a single processor. These multi-core designs are a result of increased number of transistors on a chip and the descreasing returns of optimizing a single core. As you may have noticed from Q2, you also have some extra hardware resources that you might be able to translate into extra cores. Surprisingly, the process is not that difficult!

The following figure shows a simple dual-core design.

In this example, there are two slightly modified cores (i.e. execution units). The biggest change in this system is the location of the data memory. In your single-core design, the data memory resided within the execution unit. However, in a multi-core design the data memory is located outside of the backend so that it can be shared among multiple execution units.

Sharing of the data memory can lead to contention on the memory among the cores. Since the memory can only handle a single request at a time, if multiple requests are received simultaneously, there must be some form of arbitration. For the core that loses out on arbitration, the 'refused' signal is asserted. The simplest arbitration scheme is to always give priority to one of the cores. Unfortunately, this is an unfair scheme and can lead to poor performance of the lesser priority core is memory-intensive. To handle this problem, you will likely want to devise a better method of arbitration.

More sophisticated designs may employ banking, where the data memory is actually composed of several smaller memories. The lower bits of the address indicate which bank an address goes to. In this case, the arbitration logic will only assert the refused signal if the two cores try to address the same bank.

Multi-core Performance

Adding multiple cores to a processor makes sense only when it leads to an improvement in performance. There are generally two ways which we can take advantage of multi-core processors: by running multiple applications on them or by parallelizing the applications. For this lab, we are only interested in the latter. Parallelizing a program involves dividing it into multiple parts that can be executed simultaneously by multiple cores. In general, parallelizing a program is not a trivial task. Of the three benchmarks used in this lab, the easiest to parallelize is fib_native. Using a simple modification, you should see almost linear speedup (i.e. a speed up of N when N cores are used) with fib_native. (Memory contention will likely lead to your performance be less than this though.)

Provided below is a parallel version of the fibonacci algorithm:

 
// the following variables are shared between the two processors
int sync = 0;
int val = 0x3DEADBEEF;
 
int fib_core0(int n)
{
  if (n < 5)
    return fib_internal(n);
 
  val = n-2;                       // write value to core 1
  sync = 1;                        // signal to core 1 that value is ready
 
  int temp = fib_internal(n-1);    // compute my part of fib
  while (sync == 1);               // wait for core1 to be ready 
 
  return temp + val; 
}
 
 
int fib_core1(int n)
{
   while (1) { 
       while (sync == 0);          // wait for core0 to send value 
 
       int temp = fib_internal(val);
       val = temp;                 // write value for core0 to receive
 
       sync = 0;                   // signal back to core1 that we are done;   
 
   }
}

In this code, core0 relegates the task of computing fib(n-2) to core1 while it computes fib(n-1). After both results are available, core0 adds them to get the final result. You will note that both 'sync' and 'val' are shared variables so communication between cores occurs through these variables.

The VirtualMachine function is much harder to parallelize but if you can do it effectively, we will give you a gold star for the day!

Multi-core Implementation Details

There are a couple subtle points that need to be addressed before your multi-core implementation will work. Listed below are a few of the points you will need to take under consideration:


This lab created by Donghwan Jeon, 2007. Modified by Sat Garcia, 2008. Modified by Arash Arfaee 2010