Commit 3766e342 authored by Michal Sudwoj's avatar Michal Sudwoj

Add solution ex07

parent 8efe816a
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.2
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
cmake_minimum_required(VERSION 3.11)
project (containers LANGUAGES CXX)
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED TRUE)
set (CMAKE_CXX_EXTENSIONS FALSE)
if (
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU"
OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang"
)
set (CMAKE_CXX_FLAGS "-Wall -Wextra -Wpedantic")
endif ()
add_subdirectory (timer)
add_executable (containers containers.cpp)
target_link_libraries (containers timer)
add_custom_command (
OUTPUT containers.dat
COMMAND containers > containers.dat
DEPENDS containers
)
add_custom_command (
OUTPUT containers.png
COMMAND ${CMAKE_SOURCE_DIR}/plot.py containers.dat containers.png
DEPENDS ${CMAKE_SOURCE_DIR}/plot.py containers.dat
)
add_custom_target (
plots ALL
DEPENDS containers.png
)
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.2
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
# Define our tools and parameters, preferably using the implicit variable names.
CXX = g++
CXXFLAGS = -O3 -std=c++11 -march=native -DNDEBUG
LDFLAGS = -Ltimer
LDLIBS = -ltimer
# Export all variables into the environment. This will come handy further below.
export
# The first target is called 'all' by convention and used to delegate.
.PHONY: all
all: containers.png
# Compile our code and generate an executable binary together with the library.
containers: containers.cpp timer/libtimer.a
$(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LDLIBS)
# Libraries often know how to build themselves (provide their own build system).
# Exported variables will also be visible in a sub-make.
timer/libtimer.a:
$(MAKE) -C timer
# Rerun the file if either our program or the input parameter changes.
containers.dat: containers
./containers | tee containers.dat # "tee" just prints to the terminal and to the file
# Regenerate the plot if the data or plotting configuration changes.
containers.png: containers.dat containers.gnuplot
gnuplot containers.gnuplot
# Always offer a way to clean up!
.PHONY: clean
clean:
rm -f containers containers.dat containers.png
$(MAKE) -C timer clean
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.2
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include <iomanip> // for std::setw, std::setprecision
#include <ios> // for std::right
#include <iostream> // for std::cout
#include <vector> // for std::vector
#include <list> // for std::list
#include <set> // for std::set
#include "timer/timer.hpp"
using test_type = unsigned long;
template <class C>
void fill_container(const size_t & num_ops, C & container) {
typename C::iterator it;
for( size_t i = 0; i < num_ops; ++i ) {
it = container.insert(container.begin(), 0);
container.erase(it);
}
}
template<typename C>
double measure_container(const size_t & num_ops,
const std::vector<test_type> & input) {
Timer t;
// C is std::vector<test_type>, std::list<test_type> or std::set<test_type>
C container(input.cbegin(),input.cend()); // copy the input to our container
t.start();
fill_container(num_ops, container);
t.stop();
// output in nanoseconds
return 1e9 * t.duration() / num_ops;
}
int main() {
const size_t num_ops = 4e06;
std::cout
<< std::right << "# "
<< std::setw(4) << "N" << ' '
<< std::setw(13) << "Vector[ns/op]" << ' '
<< std::setw(13) << "List[ns/op]" << ' '
<< std::setw(13) << "Set[ns/op]" << '\n'
;
for(unsigned i = 4; i < 14; ++i){
const size_t size = 1ul << i; // == std::pow(2, i)
std::vector<test_type> input(size);
for(size_t i=0; i < input.size(); ++i) {
input[i] = i+1;
}
std::cout
<< std::right << std::fixed << std::setprecision(6) << " "
<< std::setw(4) << size << ' '
<< std::setw(13)
<< measure_container<std::vector<test_type>>(num_ops, input) << ' '
<< std::setw(13)
<< measure_container<std::list<test_type>>(num_ops, input) << ' '
<< std::setw(13)
<< measure_container<std::set<test_type>>(num_ops, input) << '\n'
;
}
return 0;
}
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.2
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
set title 'containers benchmarks'
set xlabel 'n'
set ylabel 'time [ns]'
set log xy
set grid
set key top left
set terminal png
set output 'containers.png'
plot 'containers.dat' u 1:2 w lp ti 'vector', 'containers.dat' u 1:3 w lp ti 'list', 'containers.dat' u 1:4 w lp ti 'set'
#!/usr/bin/env python
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.2
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
from argparse import ArgumentParser
import pandas as pd
import matplotlib.pyplot as plt
def main():
parser = ArgumentParser()
parser.add_argument("input_file")
parser.add_argument("output_file", nargs="?")
args = parser.parse_args()
data = pd.read_fwf(args.input_file).drop("#", "columns").set_index("N")
fig = plt.figure()
fig.gca().set_xscale("log", base=2)
fig.gca().set_yscale("log", base=10)
for container in ["Vector", "List", "Set"]:
plt.plot(data.index, data[container + "[ns/op]"], label=container)
plt.legend()
plt.title("Standard container comparison")
plt.xlabel("Size in elements")
plt.ylabel("Time in ns / op")
if args.output_file:
plt.savefig(args.output_file)
else:
plt.show()
if __name__ == "__main__":
main()
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.2
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
cmake_minimum_required(VERSION 3.1)
project(timer)
set(CMAKE_CXX_STANDARD 11)
add_library(timer STATIC timer.cpp)
install(TARGETS timer DESTINATION lib)
install(FILES timer.hpp DESTINATION include)
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.2
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
# Create a library from the object code.
libtimer.a: timer.o
ar rvs $@ $^
# Object code depending on header and implementation.
timer.o: timer.cpp timer.hpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
# Always offer a way to clean up!
.PHONY: clean
clean:
rm -f *.o *.a
# Note: in the target timer.o we could rely on implicit integration (leave the
# body empty). It is shown for verbosity: CXXFLAGS is inherited.
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.2
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include "timer.hpp"
Timer::Timer() {
tstart_ = std::chrono::high_resolution_clock::now();
}
void Timer::start() {
tstart_ = std::chrono::high_resolution_clock::now();
}
void Timer::stop() {
tend_ = std::chrono::high_resolution_clock::now();
}
double Timer::duration() const {
return std::chrono::duration< double >(tend_ - tstart_).count();
}
#ifndef TIMER_HPP
#define TIMER_HPP
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.2
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include <chrono>
class Timer {
public:
Timer();
void start();
void stop();
double duration() const;
private:
using time_point_t = std::chrono::high_resolution_clock::time_point;
time_point_t tstart_;
time_point_t tend_;
};
#endif // !defined TIMER_HPP
# Programming Techniques for Scientific Simulations I
# HS 2020
# Exercise 7.1
# 2020-11-05
# Michal Sudwoj <msudwoj@student.ethz.ch>
# Require 3.1 for CMAKE_CXX_STANDARD property
cmake_minimum_required(VERSION 3.1)
# C++11
set(CMAKE_CXX_STANDARD 11)
# setting warning compiler flags
if(CMAKE_CXX_COMPILER_ID MATCHES "(C|c?)lang")
add_compile_options(-Weverything)
else()
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# add options
option(VERBOSE "Show what's happening" OFF)
if ( VERBOSE )
add_definitions(-DVERBOSE)
endif()
option(MOVE "Enable move ctor & assignment" ON)
if ( MOVE )
add_definitions(-DMOVE)
endif()
add_executable(main main.cpp)
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.1
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include "main.hpp" // for print_reverse
#include "sarray.hpp"
#include <iostream>
int main() {
// alias for SArray type
using sarray_t = SArray<int>;
// create SArray with five elements
sarray_t a(5);
// set some values in the array using our (forward) iterator
std::cout << "Fill array with some values:\n";
sarray_t::iterator end;
end = a.end(); // copy assignment!
// auto end = a.end(); // or copy ctor with auto (C++11)
int i = 0;
for (sarray_t::iterator iter = a.begin(); iter != end; ++iter) {
*iter = i++;
std::cout << "*iter = " << *iter << '\n';
}
// range-based for loop (C++11) using our (forward) iterator
std::cout << "Print array with range-based for loop:\n";
for (auto const& elem : a) {
std::cout << "elem = " << elem << '\n';
}
print_reverse(a);
}
#ifndef MAIN_HPP
#define MAIN_HPP
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.1
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include <iostream> // for std::cout
#include <iterator> // for std::iterator_traits, std::bidirectional_iterator_tag
#include <type_traits> // for std::enable_if_t, std::is_base_of_v
// WARNING: INTERNAL CODE
// this code is only here to make everyone's life easier
// and make both the base exercise and the bonus compile
// we use the SFINAE idiom ("substitution failure is not an error")
// to check if `my_iterator` implements *BidirectionalIterator*
// this way, code compiles in both cases, without the hassle of commenting
// or using the preprocessor
// read more about SFINAE here:
// https://en.cppreference.com/w/cpp/language/sfinae
// Base version (without BidirectionalIterator implemented)
template<class T>
typename std::enable_if<!std::is_base_of<
std::bidirectional_iterator_tag,
typename std::iterator_traits<typename T::iterator>::iterator_category>::
value>::type
print_reverse(T const&) {
std::cout << "my_iterator does not implement BidirectionalIterator\n";
}
// Bonus version (with BidirectionalIterator implemented)
template<class T>
typename std::enable_if<std::is_base_of<
std::bidirectional_iterator_tag,
typename std::iterator_traits<typename T::iterator>::iterator_category>::
value>::type
print_reverse(T const& a) {
// print in reverse order using our bidirectional iterator
std::cout << "Print array in reverse order with bidirectional iterator:\n";
auto beg{a.begin()};
auto iter{a.end()};
do {
std::cout << "*(--iter) = " << *(--iter) << '\n';
} while (iter != beg);
}
#endif // MAIN_HPP
#ifndef MY_ITERATOR_HPP
#define MY_ITERATOR_HPP
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.1
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include <cassert>
#include <iostream> // for debugging
#include <iterator> // for iterator category tags
/*
Forward & bidirectional iterators requirements:
Iterator:
- CopyConstructible
- CopyAssignable
- Destructible
- Supports: *a (Dereferenceable)
- Supports: ++a (Preincrementable)
Input Iterator:
- All requirements of an iterator.
- Supports: == (EqualityComparable)
- Supports: !=
- Supports: ->
- Supports: a++ (Postincrementable)
Forward Iterator:
- All requirements of an input iterator
- DefaultConstructible
- Supports expression: *a++
Bidirectional Iterator:
- All requirements of a forward iterator
- Predecrementable
- Postdecrementable
- Supports expression: *a--
*/
// my iterator
template <typename T>
class MyIterator {
public:
// member types
using value_type = T; // type of values obtained when dereferencing the
// iterator
using difference_type = std::size_t; // signed integer type to represent
// distance between iterators
using reference = T&; // type of reference to type of values
using pointer = T*; // type of pointer to the type of values
// using iterator_category = std::forward_iterator_tag; // category of
// // the iterator
using iterator_category = std::bidirectional_iterator_tag; // category of
// the iterator
// special members
// ctor
MyIterator(pointer p_curr, pointer p_begin, pointer p_end)
: p_curr_(p_curr)
, p_begin_(p_begin)
, p_end_(p_end) {
assert( check(false) );
};
// copy ctor
MyIterator(MyIterator const&) = default;
// copy assignment
MyIterator& operator=(MyIterator const&) = default;
// dtor
~MyIterator() { }
// dereference operator
reference operator*() const {
assert( check(true) );
return *p_curr_;
}
// prefix increment operator
MyIterator& operator++() {
assert( check(true) );
++p_curr_;
return *this;
}
// so far we model an iterator
// equality operator
bool operator==(MyIterator const& rhs) const {
return ( p_begin_ == rhs.p_begin_ // check that beginnings match
&& p_curr_ == rhs.p_curr_ // check that current position match
&& p_end_ == rhs.p_end_); // check that endings match
}
// inequality operator (implemented in terms of equality operator!)
bool operator!=(MyIterator const& rhs) const {
return !(*this == rhs); // note that we reuse the operator== (DRY!)
}
// arrow operator
pointer operator->() const {
assert( check(true) );
return p_curr_;
}
// postfix increment operator
MyIterator operator++(int) {
MyIterator old = *this; // copy necessary!
++(*this); // we reuse the prefix increment (DRY!)
return old; // return old here!
}
// so far we model an input iterator
// default ctor
MyIterator() : p_curr_(nullptr), p_begin_(nullptr), p_end_(nullptr) {
assert( check(false) );
};
// so far we model a forward iterator
// prefix decrement operator
MyIterator& operator--() {
assert( check(false) ); // check!
--p_curr_;
assert( check(true) ); // check!
return *this;
}
// postfix decrement operator
MyIterator operator--(int) {
MyIterator old = *this; // copy necessary!
--(*this); // we reuse the prefix increment (DRY!)
return old; // return old here!
}
// now we model a bidirectional iterator
private:
// private data members
pointer p_curr_; // current position pointer
pointer p_begin_; // pointer to beginning
pointer p_end_; // pointer to "one past" the end
// check iterator
bool check(bool Dereferenceable) const {
bool itChecksOut;
if ( Dereferenceable ) { // must be dereferenceable
itChecksOut = (p_begin_ <= p_curr_) && (p_curr_ < p_end_);
} else { // does not have to be dereferenceable
bool allNullPtr = (p_begin_ == nullptr) // catches case
&& (p_curr_ == nullptr) // of default ctored
&& (p_end_ == nullptr); // iterator
itChecksOut = allNullPtr
|| ( (p_begin_ <= p_curr_)
&& (p_curr_ <= p_end_) );
}
return itChecksOut;
}
};
#endif /* MY_ITERATOR_HPP */
#ifndef SARRAY_HPP
#define SARRAY_HPP
/*
* Programming Techniques for Scientific Simulations I
* HS 2020
* Exercise 7.1
* 2020-11-05
* Michal Sudwoj <msudwoj@student.ethz.ch>
*/
#include "my_iterator.hpp"
#include <cassert>
#include <iostream>
#include <utility> // for std::swap
// (Too!) Simple array class template
template <typename T>
class SArray {
public:
// member types
using size_type = std::size_t; // size type
using value_type = T; // value/element type
using reference = T&; // reference type
using const_reference = T const&; // const reference type
using iterator = MyIterator<T>; // iterator
// special members