Open-Ended Portion

Optimize the Canny edge-detection pipeline using profiler evidence to choose and evaluate your changes. Complete this portion individually and submit your source changes and a report. Complete the directed portion first; its finished implementation is your starting point here.

You may change internal data layouts, fuse stages, and use SIMD or all four cores with pthreads or OpenMP. Keep the supplied build settings and preserve the interfaces in include/ and the complete pipeline result; see the lab-wide requirements.

Investigation

A profile samples the functions executing while the program runs. It helps you find expensive work to investigate. Start with your completed directed code and run make benchmark PROFILE=1. The APerf guide explains how to view the saved reports. Read the source for the expensive functions and decide what work could be reduced or reorganized.

Make one change at a time, then run make benchmark. It saves your source, checks correctness, and compares timings with the starter and previous successful version. Use the saved experiment numbers to identify the versions in your report. The starter remains the original lab version; beginning this portion does not reset it. The previous version may be a repeated run of the same code. You can also use make standalone to build the application without saving an experiment; make benchmark runs the correctness checks.

Try the other presets in the Benchmarking guide or add your own images to workloads.yaml. An optimization that helps on one input may add overhead on another. Profile again after a change to see what is now expensive, and keep track of unsuccessful attempts as well as improvements.

Once you have an expensive function to investigate, consult its stages in the Program Guide. The machine crib sheet provides cache and SIMD information if it is relevant to your proposed change.

Possible Directions

These questions suggest starting points for your investigation.

  • Arithmetic: Are expensive operations computing information that a simpler calculation could provide? Can a filter be reorganized without changing its rounding or border behavior?
  • Memory traffic: Can adjacent stages share a pass? Would smaller records, different field layouts, or separate hot and cold fields reduce memory access?
  • Traversal: Does the code inspect absent pixels or already visited connections? Can masks or bit operations reduce that work while preserving order?
  • SIMD: Which loops process independent elements? What limits vectorization, and how will you handle borders and leftover elements?
  • Parallelism: Which rows or paths can run independently? How will you preserve output order? When might thread overhead exceed the benefit?

Submission

Submit this portion by pushing to your student GitHub repository.

Submit the source from your final reported experiment and a report explaining what you tried and why. Include profiles showing where the program spent its time, correctness results, and before-and-after timings. State which versions you compared and which images you used. The saved experiment numbers and results identify these for you. If you used multiple threads, give the thread count.

Explain whether your changes helped and how the results varied with the input. Include unsuccessful attempts; a well-explained negative result is useful.

Put the report and selected evidence in a tracked directory such as submission/; the generated experiments/ directory is ignored by Git. Copy the timing tables and profile figures you cite into that directory. Check that your committed src/ matches the final experiment you report before pushing.

Use VS Code Remote SSH or scp to retrieve files needed on your laptop. Download any experiments you want to retain before losing access to the instance; pushing your code does not back up the ignored experiment history.

You may continue optimizing for the optional competition.