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.