diff --git a/.monadid b/.monadid index 6b183d8c435da77218cceed399691999e14e5d00..e49988717e13f1a6e85621552a021e1365bcba12 100644 --- a/.monadid +++ b/.monadid @@ -1,4 +1,4 @@ monad identification file This file is used for monad directory identification Built by toole1 on linux4.ews.illinois.edu -Build Date: Mon Oct 17 22:44:50 CDT 2011 +Build Date: Mon Oct 24 23:45:56 CDT 2011 diff --git a/monad b/monad index 3977504d4c2559942bcada38cf547f0627a53d5d..9d1ca1f8b1c4c91babed89d8bd64d628d923b52b 100755 Binary files a/monad and b/monad differ diff --git a/monad_shared.cpp b/monad_shared.cpp index 1e53aaaea5a7fe34b64bd15c983e9cf59e79b9e8..7fa7f419c8d071070628a532a222d9ec53b83d41 100644 --- a/monad_shared.cpp +++ b/monad_shared.cpp @@ -19,6 +19,7 @@ const char * date = "15 July 2011"; } const char * unit_test::pass_string = "~~PASSED~~"; +const size_t header_length = 64; void printInfo() { @@ -47,18 +48,16 @@ void set_error_message() void header(const string & title) { cout << title << "..." << endl - << "================================" << endl; + << string(header_length, '=') << endl; } void warning(const string & message) { cerr << endl - << "********************************" - "********************************" << endl + << string(header_length, '*') << endl << "WARNING!" << endl << message << endl - << "********************************" - "********************************" << endl << endl; + << string(header_length, '*') << endl << endl; } @@ -113,7 +112,7 @@ void testname(const unit_test & curr_test, int32_t max_testname_len, int32_t max void detailed_info(const unit_test & curr_test) { - std::cout << "--------------------------------" << endl + std::cout << string(header_length, '-') << endl << curr_test.name; if (curr_test.is_valgrind) std::cout << " (run under valgrind)"; std::cout << " [" << curr_test.points << " points]" << endl; @@ -133,7 +132,7 @@ void detailed_info(const unit_test & curr_test) cout << curr_test.timeout << "ms timeout)" << endl; std::cout << "Output:" << endl - << "--------------------------------" << endl; + << string(header_length, '-') << endl; // Tab the output over to distinguish it from the test case if (output != "") diff --git a/proxy.cpp b/proxy.cpp index fd71b8dfb3666cc4175a3b82f804368efbcd17ed..bc8454f1dbc67336e6b18bae197032b5bf0af140 100644 --- a/proxy.cpp +++ b/proxy.cpp @@ -60,19 +60,28 @@ namespace proxy vector<unit_test> * global_tests = NULL; output_check_map * global_output_checks = NULL; - double runtime_ratio[TIME_COUNT] = + double time_constant(size_t smaller, size_t larger) { return 1.0; } + double time_logn (size_t smaller, size_t larger) { return log(larger) / log(smaller); } + double time_linear (size_t smaller, size_t larger) { return (double)larger / smaller; } + double time_nlogn (size_t smaller, size_t larger) { return (larger*log(larger)) / (smaller*log(smaller)); } + double time_nsquared(size_t smaller, size_t larger) { return ((double)larger*larger) / ((double)smaller*smaller); } + double time_cubed (size_t smaller, size_t larger) { return ((double)larger*larger*larger) / ((double)smaller*smaller*smaller); } + double time_infinity(size_t smaller, size_t larger) { return std::numeric_limits<double>::max(); } + + runtime_ratio_func runtime_ratio[TIME_COUNT] = { - 1.0, - 4.0, - 5.20411998, // for 400/100 -// 8.0, - 16.0, - 64, - std::numeric_limits<double>::max() + time_constant, + time_logn, + time_linear, + time_nlogn, + time_nsquared, + time_cubed, + time_infinity }; const char * runtime_str[TIME_COUNT] = { "O(1)", + "O(logn)", "O(n)", "O(nlogn)", // "O(nrootn)", @@ -401,7 +410,7 @@ void RunTests::output_detailed_tests_info(int32_t score) if (!tests[test_i].passed() || opts::verbose) output::detailed_info(tests[test_i]); - cout << endl << "--------------------------------" << endl; + cout << endl << string(64, '-') << endl; // TODO (toole1): poor style, should be refactored to monad_shared::output } diff --git a/proxy.h b/proxy.h index 96c53bc157c8acea1e80a8ad1ed96c93f53cf9ca..68ee6aa87b2450e99329769fc75c35aaebc53702 100644 --- a/proxy.h +++ b/proxy.h @@ -6,9 +6,10 @@ #ifndef MONAD_PROXY_H #define MONAD_PROXY_H -#include <cmath> +#include <math.h> #include <iostream> +#include <functional> #include <limits> #include <map> #include <string> @@ -24,6 +25,7 @@ namespace proxy { + using namespace std; using namespace monad_shared; class RunTests; @@ -242,6 +244,7 @@ inline std::string assert_equals_help(T expected, T actual, const char * expstr, enum proxy_runtime_t { CONSTANT_TIME = 0, + LOGN_TIME, N_TIME, NLOGN_TIME, // NROOTN_TIME, @@ -253,35 +256,51 @@ enum proxy_runtime_t namespace proxy { - extern double runtime_ratio[TIME_COUNT]; + typedef double (*runtime_ratio_func)(size_t, size_t); + extern runtime_ratio_func runtime_ratio[TIME_COUNT]; extern const char * runtime_str[TIME_COUNT]; + struct TimeIterationsData + { + double timePerCall; + size_t iterations; + uint64_t totalTime; + }; + + template <typename Generator, typename Timer> TimeIterationsData timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size); + template <typename Generator, typename Timer> TimeIterationsData timeIterations (Generator gen, Timer timeFunctor, size_t input_size); + template <typename GenResult, typename GenArg, typename Timer> TimeIterationsData timeIterations (GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size); + template <typename Generator, typename Timer> - uint64_t timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size); - template <typename Generator, typename Timer> - uint64_t timeIterations(Generator gen, Timer timeFunctor, size_t input_size); - template <typename GenResult, typename GenArg, typename Timer> - uint64_t timeIterations(GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size); + bool assert_time_impl(Generator gen, Timer functor, proxy_runtime_t expectedTime, size_t size1 = 100, size_t size2 = 400); } -#define ASSERT_TIME(gen, functor, expectedTime) \ +#define ASSERT_TIME3(gen, functor, expectedTime) \ do { \ - uint64_t diff400 = proxy::timeIterations(gen, functor, 400); \ - uint64_t diff100 = proxy::timeIterations(gen, functor, 100); \ - std::cout << diff400 << std::endl; \ - std::cout << diff100 << std::endl; \ - double ratio = (double)(diff400)/(diff100); \ - double diffFromExpected = abs(ratio - proxy::runtime_ratio[expectedTime]); \ - double diffFromWrong = abs(ratio - proxy::runtime_ratio[expectedTime + 1]); \ - std::cout << ratio << std::endl; \ - if (diffFromWrong < diffFromExpected) \ - FAIL(string("Runtime was larger than ") + proxy::runtime_str[expectedTime] + util::to_string(ratio)); \ + if (proxy::assert_time_impl(gen, functor, expectedTime)) \ + FAIL(string("Runtime was larger than ") + proxy::runtime_str[expectedTime]); \ } while(0) +#define ASSERT_TIME5(gen, functor, expectedTime, size1, size2) \ + do { \ + if (proxy::assert_time_impl(gen, functor, expectedTime, size1, size2)) \ + FAIL(string("Runtime was larger than ") + proxy::runtime_str[expectedTime]); \ + } while(0) + +// Crazy hack for overloading! +// Arg counting from: +// http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/ +// Overloading tips: +// http://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros +#define ASSERT_TIME_SIXTH_ARG(a, b, c, d, e, f, ...) f + +#define ASSERT_TIME(...) \ + ASSERT_TIME_SIXTH_ARG(__VA_ARGS__, ASSERT_TIME5, 0, ASSERT_TIME3, 0, 0) (__VA_ARGS__) + namespace proxy { template <typename Generator, typename Timer> -uint64_t timeIterations(Generator gen, Timer timeFunctor, size_t input_size) +TimeIterationsData timeIterations(Generator gen, Timer timeFunctor, size_t input_size) { return timeIterationsImpl( bind1st(mem_fun(&Generator::operator()), &gen), @@ -290,27 +309,86 @@ uint64_t timeIterations(Generator gen, Timer timeFunctor, size_t input_size) } template <typename GenResult, typename GenArg, typename Timer> -uint64_t timeIterations(GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size) +TimeIterationsData timeIterations(GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size) { return timeIterationsImpl(ptr_fun(gen), timeFunctor, input_size); } template <typename Generator, typename Timer> -uint64_t timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size) +TimeIterationsData timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size) +{ + const uint64_t min_time = 1000000; // in microseconds + std::vector<typename Generator::result_type *> inputs; + inputs.reserve(2000); // arbitrary, guess at how big it will be + + // Using pointers here allows us to avoid copying if the compiler supports copy elision + // Since we're intentionally using large inputs, this could potentially have a significant effect on speed + // We're also going to do something else weird here. Instead of generating a fixed number of inputs, we're + // going to generate inputs for a fixed time. + size_t max_iterations = 0; + for (uint64_t genstart = util::process_clock(); util::process_clock() - genstart < min_time; max_iterations++) + inputs.push_back(new typename Generator::result_type(gen(input_size))); + + typename Generator::result_type warmup_temp = gen(1); + timeFunctor(warmup_temp); // Warm up time functor (i.e. initialize statics) + + size_t succeeded_iterations; + uint64_t starttime = util::process_clock(); + for (succeeded_iterations = 0; succeeded_iterations < max_iterations && util::process_clock() - starttime < min_time;) + for (uint32_t i = 0; i < 10 && succeeded_iterations < max_iterations; i++, succeeded_iterations++) + timeFunctor(*inputs[succeeded_iterations]); + uint64_t endtime = util::process_clock(); + + for (size_t i = 0; i < max_iterations; i++) + delete inputs[i]; + + TimeIterationsData result; + result.timePerCall = static_cast<double>(endtime - starttime) / succeeded_iterations; + result.iterations = succeeded_iterations; + result.totalTime = endtime - starttime; + return result; +} + +inline void timeIterationsOutput(size_t size, const TimeIterationsData & data) { - const size_t num_iterations = 2000; - std::vector<typename Generator::result_type> inputs; - inputs.reserve(num_iterations); - for (size_t i = 0; i < num_iterations; i++) - inputs.push_back(gen(input_size)); - - uint32_t starttime = util::process_clock(); - for (size_t i = 0; i < num_iterations; i++) - timeFunctor(inputs[i]); - uint32_t endtime = util::process_clock(); - return endtime - starttime; + std::cout << "Input size " << size << ": " + << data.iterations << " iterations in " << data.totalTime/1000 << " ms " + << "for an average of " << data.timePerCall << " us per call" << endl; } +template <typename Generator, typename Timer> +bool assert_time_impl(Generator gen, Timer functor, proxy_runtime_t expectedTime, size_t size1, size_t size2) +{ + TimeIterationsData diff0 = timeIterations(gen, functor, 1); + TimeIterationsData diff1 = timeIterations(gen, functor, size1); + TimeIterationsData diff2 = timeIterations(gen, functor, size2); + timeIterationsOutput( 1, diff0); + timeIterationsOutput(size1, diff1); + timeIterationsOutput(size2, diff2); + double ratio = (diff2.timePerCall - diff0.timePerCall) / (diff1.timePerCall - diff0.timePerCall); + + double expected_ratio = proxy::runtime_ratio[expectedTime](size1, size2); + double toohigh_ratio = proxy::runtime_ratio[expectedTime + 1](size1, size2); + double diffFromExpected = fabs(ratio - expected_ratio); + double diffFromWrong = fabs(ratio - toohigh_ratio); + + std::cout << "Actual ratio: " << ratio << std::endl; + std::cout << "Expected ratio: " << expected_ratio << std::endl; + std::cout << "Wrong/high ratio: " << toohigh_ratio << std::endl; + std::cout << "Diff from expected: " << diffFromExpected << std::endl; + std::cout << "Diff from wrong: " << diffFromWrong << std::endl; + +#if 0 // This does not seem to be important. A sample of two iterations seems to work. + const size_t min_iters = 100; + if (diff0.iterations < min_iters || diff1.iterations < min_iters || diff2.iterations < min_iters) + { + std::cout << "Too few iterations: Code was too slow to be able to judge runtime accurately" << std::endl; + return true; + } +#endif + + return (diffFromWrong < diffFromExpected); +} inline int32_t bitflags(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long e)