Generic Programming and Library DevelopmentQuarter 4, 2007 |
|
Assignment 1Deadline: Friday, 27 April 2007 at 9.15 Contact person: Jyrki Katajainen e-mail: jyrki@diku.dk Problem formulation:
The starting point for this assignment is a routine, called it The tools you need are a text editor (e.g. emacs), a C++ compiler (e.g. g++), and a tool to make time measurements (e.g. Benz [1]).
In 2001, Steffen Nissen pointed out that loop unrolling could be used
to optimize many of the standard-library routines. In this assignment your task is to examine
whether Steffen's optimization technique is relevant for routine
1) write a small test program that tests the
correctness of
2) take the unoptimized version of
3) create another version of
4) compare the efficiency of the two versions of
Independent of the outcome of your experiment, you are welcome to commit your programs to the CPH STL CVS repository. This way you will get full access to all files created in the project. Ask Jyrki for further details. References:
Appendix: Code for find Directory structure Benz drive.c++ experiment.c++ form.py Optimized find.cpp Unittest test_find.c++ Unoptimized find.cpp makefile Unoptimized/find.cpp namespace cphstl { template <typename P, typename E> P find (P first, P one_past_the_end, const E& elem) { while (first != one_past_the_end) { if (*first == elem) { return first; } ++first; } return one_past_the_end; } } Optimized/find.cpp #include <iterator> // defines std::iterator_traits #include "optimization" // defines UNROLLED_LOOP namespace cphstl { template <typename P, typename E> inline P find (P first, P one_past_the_end, const E& elem, std::input_iterator_tag used_for_branching) { while (first != one_past_the_end) { if (*first == elem) { return first; } ++first; } return one_pass_the_end; } template <typename R, typename E> inline R find (R first, R one_past_the_end, const E& elem, std::random_access_iterator_tag used_for_branching) { UNROLLED_LOOP(first, one_past_the_end, if (*first == elem) { return first; } ) return one_past_the_end; } template <typename X, typename E> X find(X first, X one_past_the_end, const E& elem) { typename std::iterator_traits<X>::iterator_category strength; return cphstl::find(first, one_past_the_end, elem, strength); } } Unittest/test_find.c++ #if defined(STD) #define NAMESPACE std #include <algorithm> // defines std::find #else #define NAMESPACE cphstl #include "find.cpp" //defines cphstl::find #endif #include <cassert> #include <iterator> template <typename F> void generate ( F first, F beyond ) { typedef typename std::iterator_traits<F>::value_type E; F p = first; while (p != beyond) { *p = E(2 * (p - first) + 1); ++p; } } int main() { const unsigned int bign = 12; typedef int E; typedef E* (*routine)(E*, E*, const E&); routine search = NAMESPACE::find; for (unsigned int n = 0; n <= bign; n++) { E a[12]; generate(a, a + n); for (unsigned int i = 0; i < n; i++) { if (n > 0) assert(search(a, a + n, a[i]) == a + i); else assert(search(a, a + n, a[i]) == a); assert(search(a, a + n, a[i] - 1) == a + n); assert(search(a, a + n, a[i] + 1) == a + n); } E b[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; assert(search(b, b + n, -1) == b + n); if (n > 0) assert(b <= search(b, b + n, 0) && search(b, b + n, 0) < b + n); else assert(search(b, b + n, 0) == b); if (n > 0) assert(search(b, b + n, 0) < b + n); else assert(search(b, b + n, 0) == b); assert(search(b, b + n, 1) == b + n); } return 0; } Benz/drive.c++ #include <iostream> // defines std::cout and std::endl #include <ctime> // defines std::clock #include "experiment.c++" // defines experiment #ifndef N #define N 1000000 #endif int main() { experiment<int> e(N); clock_t start = clock(); e(); clock_t ticks = clock() - start; double ns_per_N = 1.0e9 / (double(CLOCKS_PER_SEC) * double(N)); std::cout << ns_per_N * double(ticks) << std::endl; return 0; } Benz/experiment.c++ /* Experiment for the find() function */ #if defined(STD) #define NAMESPACE std #include <algorithm> // defines std::find #else #define NAMESPACE cphstl #include "find.cpp" //defines cphstl::find #endif #include <vector> // defines std::vector #include <cassert> // defines assert template <typename E> class experiment { public: experiment(unsigned int n) { a.resize(n, E(0)); s = E(1); } void operator()() { typedef typename std::vector<E>::iterator R; R answer = NAMESPACE::find(a.begin(), a.end(), s); assert(answer == a.end()); } private: E s; std::vector<E> a; }; Benz/form.py """ Benchmarking the find() function shell> echo $PYTHONPATH /home/disk04/jyrki/CPHSTL/Tool/Benz/ """ import benz import os class find_case(benz.case): def __init__(self, n, element, directory): benz.case.__init__(self) self.n = n self.compiler = '/home/disk04/jyrki/Script/Gfilt/gfilt' self.compiler_options.extend(['-O6', '-funroll-all-loops', '-DN=' + str(n), '-D' + directory]) home = os.environ['HOME'] self.include_paths.extend(['Benz/', home + '/CPHSTL/Program/Optimization/']) if directory != 'STD': self.include_paths.extend([directory]) self.driver_file = 'Benz/drive.c++' def output(self): if self.driver_output != '': return (self.n, self.driver_output) else: return () class find_curve(benz.curve_suite): def __init__(self, element, namespace, directory, linetype, pointtype): benz.curve_suite.__init__(self) self.title = directory + ' ' + namespace + '::find()' self.linetype = linetype self.pointtype = pointtype for k in range(10, 25): n = 1 << k; self.add(find_case(n, element, directory)) class find_plot(benz.plot_suite): """ Measure the efficiency of the find() function """ def __init__(self): benz.plot_suite.__init__(self) self.title = 'Runtime of an unsuccessful search for a vector of size n' self.xlabel = 'Input size [n]' self.ylabel = 'Execution time per element [nanoseconds]' self.add(find_curve('int', 'std', 'STD', 1, 3)) self.add(find_curve('int', 'cphstl', 'Unoptimized', 2, 4)) self.add(find_curve('int', 'cphstl', 'Optimized', 3, 6)) self.gnuplot_commands += """ set title '%(title)s' set xlabel '%(xlabel)s' set ylabel '%(ylabel)s' """ % self.__dict__ if __name__ == '__main__': benz.main(task = find_plot(), runner = benz.gnuplot_runner) UNIX makefile CXX = g++ CXXFLAGS += -O6 -funroll-all-loops INCLUDE_PATHS = -I$(HOME)/CPHSTL/Program/Optimization/ .PHONY: default clean plot default: plot plot: python Benz/form.py %/unittest: $(CXX) -I$(@D) $(INCLUDE_PATHS) -D$(@D) Unittest/test_find.c++ ./a.out %/testdrive: $(CXX) -I$(@D) $(INCLUDE_PATHS) -D$(@D) Benz/drive.c++ ./a.out clean: -rm -f a.out */a.out plot.* driver.* |
||||||||||||
This page was last modified by Jyrki Katajainen on 20.04.2008. |