Commit b6f9a12e authored by hlgr's avatar hlgr

Hier kommt die Maus!

parents
Project(CO572CW1)
cmake_minimum_required(VERSION 3.7)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if (MSVC)
add_compile_options(/W4 /WX)
else()
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL armv7l)
# setting architecture explicitly on Pi because raspbian doesn't actually use correct ISA
add_compile_options(-Wall -Wextra -pedantic -Werror -march=armv8-a+crc)
else()
add_compile_options(-Wall -Wextra -pedantic -Werror -march=native)
endif()
endif()
# --------------------------------------------------
include(ExternalProject)
ExternalProject_Add(googlebenchmark
URL "https://github.com/google/benchmark/archive/v1.5.0.tar.gz"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CO572CW1_BINARY_DIR}/deps -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_ENABLE_GTEST_TESTS=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
ExternalProject_Add(catch2
URL "https://github.com/catchorg/Catch2/archive/v2.9.1.tar.gz"
CMAKE_ARGS -DCATCH_BUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=${CO572CW1_BINARY_DIR}/deps -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)
# --------------------------------------------------
add_executable(microbenchmarks microbenchmarks.cpp solution.c)
add_dependencies(microbenchmarks googlebenchmark)
set_property(TARGET microbenchmarks PROPERTY CXX_STANDARD 14)
target_link_libraries(microbenchmarks Threads::Threads)
target_link_libraries(microbenchmarks ${CO572CW1_BINARY_DIR}/deps/lib/${CMAKE_SHARED_LIBRARY_PREFIX}benchmark.a)
target_include_directories(microbenchmarks SYSTEM PUBLIC ${CO572CW1_BINARY_DIR}/deps/include)
# --------------------------------------------------
add_executable(macrobenchmark macrobenchmark.cpp solution.c)
add_dependencies(macrobenchmark googlebenchmark)
set_property(TARGET macrobenchmark PROPERTY CXX_STANDARD 14)
target_link_libraries(macrobenchmark Threads::Threads)
target_link_libraries(macrobenchmark ${CO572CW1_BINARY_DIR}/deps/lib/${CMAKE_SHARED_LIBRARY_PREFIX}benchmark.a)
target_include_directories(macrobenchmark SYSTEM PUBLIC ${CO572CW1_BINARY_DIR}/deps/include)
# --------------------------------------------------
add_executable(tests tests.cpp solution.c)
add_dependencies(tests catch2)
set_property(TARGET tests PROPERTY CXX_STANDARD 14)
target_include_directories(tests SYSTEM PUBLIC ${CO572CW1_BINARY_DIR}/deps/include)
#ifndef _DATA_GENERATOR_H_
#define _DATA_GENERATOR_H_
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <random>
#include "database.h"
namespace cppref {
using namespace std;
// implementation taken from cpp-ref (just to be portable)
template <class RandomIt, class URBG> void shuffle(RandomIt first, RandomIt last, URBG&& g) {
auto n = last - first;
for(auto i = n - 1; i > 0; --i) {
std::swap(first[i], first[g() % n]); // ignoring modulo skew for the sake of portablility
}
}
} // namespace cppref
template <typename Generator = std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>>
void GenerateData(Database& db, size_t itemsCardinality = 16384) {
Generator lcg(221);
auto averageNumberOfItemsPerOrder = 4ul;
auto numberOfSalesPerDay = 32;
auto numberOfStores = 256 * std::lround(std::log2(itemsCardinality));
auto numberOfUniqueManagers = numberOfStores / 4; // four stores per manager
auto numberOfUniqueCountries = 196;
auto numberOfUniqueEmployees = numberOfUniqueManagers * 8;
auto numberOfUniquePrices = itemsCardinality / 2;
db.itemsCardinality = itemsCardinality;
db.ordersCardinality = itemsCardinality / averageNumberOfItemsPerOrder;
db.storesCardinality = numberOfStores;
db.items = new ItemTuple[db.itemsCardinality];
db.orders = new OrderTuple[db.ordersCardinality];
db.stores = new StoreTuple[db.storesCardinality];
for(auto i = 0ul; i < db.storesCardinality; i++) {
db.stores[i].managerID = lcg() % numberOfUniqueManagers;
db.stores[i].countryID = lcg() % numberOfUniqueCountries;
db.stores[i].longitude = lcg();
db.stores[i].latitude = lcg();
}
for(auto i = 0ul; i < db.ordersCardinality; i+=2) {
auto salesDate = (i / numberOfSalesPerDay) + lcg() % 2;
for (size_t j = 0; j < 2; j++)
{
db.orders[i + j].salesDate = salesDate;
db.orders[i + j].employee = i % numberOfUniqueEmployees;
db.orders[i + j].employeeManagerID = db.stores[lcg() % db.storesCardinality].managerID;
db.orders[i + j].discount = (lcg()) % 100;
}
}
cppref::shuffle(db.orders, db.orders + db.ordersCardinality - 1, lcg);
auto itemsCursor = 0ul;
for(size_t i = 0ul; i < db.ordersCardinality; i++) {
auto v = lcg();
int numberOfItemsPerOrderVariation = ((v & 2)>>1) + (v & 1) - 1;
size_t numberOfItemsPerOrder =
averageNumberOfItemsPerOrder + numberOfItemsPerOrderVariation; // fix this
for(size_t j = 0; (j < numberOfItemsPerOrder) && (itemsCursor < db.itemsCardinality); j++) {
db.items[itemsCursor].salesDate = db.orders[i].salesDate;
db.items[itemsCursor].employee = db.orders[i].employee;
db.items[itemsCursor].price = lcg() % numberOfUniquePrices;
itemsCursor++;
}
}
std::lognormal_distribution<double> distribution(.3, .8);
for(size_t i = 0; i < db.itemsCardinality; i++)
std::swap(
db.items[i],
db.items[std::min(db.itemsCardinality, static_cast<size_t>(floorl(distribution(lcg))))]);
}
void FreeDatabaseTables(struct Database& db) {
delete[] db.items;
delete[] db.orders;
delete[] db.stores;
}
#endif
#ifndef DATABASE_H_
#define DATABASE_H_
#include <stdlib.h>
struct ItemTuple
{
int salesDate;
int employee;
int price;
};
struct OrderTuple
{
int salesDate;
int employee;
int employeeManagerID;
int discount;
};
struct StoreTuple
{
int managerID;
int latitude;
int longitude;
int countryID;
};
struct Database {
struct ItemTuple* items;
size_t itemsCardinality;
struct OrderTuple* orders;
size_t ordersCardinality;
struct StoreTuple* stores;
size_t storesCardinality;
void* indices;
};
#endif
#include "data_generator.h"
#include "database.h"
#include "solution.h"
#include <benchmark/benchmark.h>
static void queryMix(benchmark::State& state) {
Database db{};
GenerateData(db, state.range(0));
std::default_random_engine generator;
std::uniform_int_distribution<> managerIDs_distribution(0, (db.storesCardinality / 4));
std::uniform_int_distribution<> prices_distribution(0, (db.itemsCardinality / 2));
std::uniform_int_distribution<> dates_distribution(0, (db.ordersCardinality / 32));
std::uniform_int_distribution<> discounts_distribution(0, 100);
std::uniform_int_distribution<> countryIDs_distribution(0, 196);
for(auto _ : state) {
CreateIndices(&db);
for(auto i = 0; i < 3; i++) {
benchmark::DoNotOptimize(
Query1(&db, managerIDs_distribution(generator), prices_distribution(generator)));
benchmark::DoNotOptimize(
Query2(&db, discounts_distribution(generator), dates_distribution(generator)));
benchmark::DoNotOptimize(Query3(&db, countryIDs_distribution(generator)));
}
DestroyIndices(&db);
db.indices = nullptr;
}
FreeDatabaseTables(db);
}
BENCHMARK(queryMix)->Arg(2 * 1024 * 1024)->Unit(benchmark::kMicrosecond);
BENCHMARK_MAIN();
#include <benchmark/benchmark.h>
#include "data_generator.h"
#include "database.h"
#include "solution.h"
static void CreateIndicesBenchmark(benchmark::State& state) {
Database db{};
GenerateData(db, state.range(0));
for(auto _ : state) {
CreateIndices(&db);
state.PauseTiming();
DestroyIndices(&db);
db.indices = nullptr;
state.ResumeTiming();
}
FreeDatabaseTables(db);
}
BENCHMARK(CreateIndicesBenchmark)->Range(1024, 1024 * 1024)->Unit(benchmark::kMicrosecond);
static void Query1Benchmark(benchmark::State& state) {
Database db{};
GenerateData(db, state.range(0));
CreateIndices(&db);
std::default_random_engine generator;
std::uniform_int_distribution<> managerIDs_distribution(0, (db.storesCardinality / 4));
std::uniform_int_distribution<> prices_distribution(0, (db.itemsCardinality / 2));
for(auto _ : state)
for(auto i = 0; i < 10; i++)
benchmark::DoNotOptimize(
Query1(&db, managerIDs_distribution(generator), prices_distribution(generator)));
DestroyIndices(&db);
db.indices = nullptr;
FreeDatabaseTables(db);
}
BENCHMARK(Query1Benchmark)->Range(1024, 1024 * 1024)->Unit(benchmark::kMicrosecond);
static void Query2Benchmark(benchmark::State& state) {
Database db{};
GenerateData(db, state.range(0));
CreateIndices(&db);
std::default_random_engine generator;
std::uniform_int_distribution<> dates_distribution(0, (db.ordersCardinality / 32));
std::uniform_int_distribution<> discounts_distribution(0, 100);
for(auto _ : state)
for(auto i = 0; i < 10; i++)
benchmark::DoNotOptimize(
Query2(&db, dates_distribution(generator), discounts_distribution(generator)));
DestroyIndices(&db);
db.indices = nullptr;
FreeDatabaseTables(db);
}
BENCHMARK(Query2Benchmark)->Range(1024, 512 * 1024)->Unit(benchmark::kMicrosecond);
static void Query3Benchmark(benchmark::State& state) {
Database db{};
GenerateData(db, state.range(0));
CreateIndices(&db);
std::default_random_engine generator;
std::uniform_int_distribution<> countryIDs_distribution(0, 196);
for(auto _ : state)
for(auto i = 0; i < 10; i++)
benchmark::DoNotOptimize(Query3(&db, countryIDs_distribution(generator)));
DestroyIndices(&db);
db.indices = nullptr;
FreeDatabaseTables(db);
}
BENCHMARK(Query3Benchmark)->Range(1024, 256 * 1024)->Unit(benchmark::kMicrosecond);
BENCHMARK_MAIN();
#include "solution.h"
int Query1(struct Database* db, int managerID, int price) {
(void)db; // prevent compiler warning about unused variable
(void)managerID; // prevent compiler warning about unused variable
(void)price; // prevent compiler warning about unused variable
return 0;
}
int Query2(struct Database* db, int discount, int date) {
(void)db; // prevent compiler warning about unused variable
(void)discount; // prevent compiler warning about unused variable
(void)date; // prevent compiler warning about unused variable
return 0;
}
int Query3(struct Database* db, int countryID) {
(void)db; // prevent compiler warning about unused variable
(void)countryID; // prevent compiler warning about unused variable
return 0;
}
void CreateIndices(struct Database* db) {
(void)db; // prevent compiler warning about unused variable
}
void DestroyIndices(struct Database* db) {
/// Free database indices
db->indices = NULL;
}
#ifndef _SOLUTION_H_
#define _SOLUTION_H_
#include "database.h"
#ifdef __cplusplus
extern "C" {
#endif
int Query1(struct Database* db, int managerID, int price);
int Query2(struct Database* db, int discount, int date);
int Query3(struct Database* db, int countryID);
void CreateIndices(struct Database* db);
void DestroyIndices(struct Database* db);
#ifdef __cplusplus
}
#endif
#endif
#define CATCH_CONFIG_MAIN
#include "data_generator.h"
#include "database.h"
#include "solution.h"
#include <catch2/catch.hpp>
int managerIDs[10] = {7, 26, 103, 14, 77, 42, 54, 112, 5, 115};
int prices[10] = {200, 1205, 7221, 6590, 1800, 750, 968, 3500, 4550, 5225};
int discounts[10] = {10, 5, 80, 90, 70, 40, 55, 85, 7, 35};
int dates[10] = {2, 12, 7, 21, 18, 5, 13, 15, 8, 11};
int countryIDs[10] = {5, 105, 41, 52, 70, 38, 191, 11, 7, 39};
int query1_results[10] = {2, 9, 32, 14, 22, 5, 5, 8, 53, 21};
int query2_results[10] = {16687, 73210, 46038, 64247, 86670, 33833, 63174, 72352, 53691, 51191};
int query3_results[10] = {468, 1277, 801, 665, 950, 1118, 500, 571, 979, 709};
TEST_CASE("Queries 1 Solution works", "[queries]") {
Database db;
GenerateData(db);
CreateIndices(&db);
auto i = GENERATE(range(0, 10));
REQUIRE(Query1(&db, managerIDs[i], prices[i]) == query1_results[i]);
DestroyIndices(&db);
db.indices = nullptr;
FreeDatabaseTables(db);
}
TEST_CASE("Queries 2 Solution works", "[queries]") {
Database db;
GenerateData(db);
CreateIndices(&db);
auto i = GENERATE(range(0, 10));
REQUIRE(Query2(&db, discounts[i], dates[i]) == query2_results[i]);
DestroyIndices(&db);
db.indices = nullptr;
FreeDatabaseTables(db);
}
TEST_CASE("Queries 3 Solution works", "[queries]") {
Database db;
GenerateData(db);
CreateIndices(&db);
auto i = GENERATE(range(0, 10));
REQUIRE(Query3(&db, countryIDs[i]) == query3_results[i]);
DestroyIndices(&db);
db.indices = nullptr;
FreeDatabaseTables(db);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment