#include <kdbthread.hpp>
#include <kdbtimer.hpp>

long long iterations = 100000LL; 

long long iterations1 = iterations / 100;

const int benchmarkIterations = 11; 

const std::string filename = "check.txt";

const std::string csvfilename = "data.csv";

std::ofstream dump(filename);
std::ofstream data(csvfilename);

const uint32_t value = 55;
const char * s_value = "55";
const uint32_t othervalue = 66;
const char * s_othervalue = "66";

namespace kdb
{

class Person : public ThreadString
{
public:
	Person(KeySet & ks, ThreadContext &context_) :
		ThreadString(ks,context_, Key("/%layer1%/person",KEY_CASCADING_NAME, KEY_META, "default", "no name", KEY_END))
	{}
	using ThreadString::operator=;

private:
	Person() = delete;
	Person(Person const &) = delete;
	Person(Person &&) = delete;
};

class Nested : public ThreadInteger
{
public:
	Nested(KeySet & ks, ThreadContext &context_) :
	ThreadInteger(ks,context_, Key("/test/%layer1%/%thread%/%layer2%/nested",
					KEY_CASCADING_NAME,
					 KEY_META, "default", s_value,
					 KEY_END))
	{}
	using ThreadInteger::operator=;
};

class Environment : public ThreadBoolean
{
public:
	Environment(KeySet & ks, ThreadContext &context_) :
		ThreadBoolean(ks,context_, Key("/test/%layer1%",KEY_CASCADING_NAME, KEY_META, "default", "1", KEY_END)),
		nested(ks,context_),
		person(ks,context_),
		profile(ks,context_, Key("/%layer1%/profile",KEY_CASCADING_NAME, KEY_META, "default", "default", KEY_END)),
		bm(ks,context_, Key("/%layer1%/%layer2%/%layer3%/%layer4%/%layer5%/%layer6%/%layer7%/%layer8%/%layer9%/",
					KEY_CASCADING_NAME,
					KEY_META, "default", s_value,
					KEY_END))
	{}

	Nested nested;
	Person person;
	ThreadString profile;
	ThreadInteger bm;
};

}

class Layer1 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer1"; }
	std::string operator()() const override
	{ return "1"; }
};
class Layer2 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer2"; }
	std::string operator()() const override
	{ return "2"; }
};
class Layer3 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer3"; }
	std::string operator()() const override
	{ return "3"; }
};
class Layer4 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer4"; }
	std::string operator()() const override
	{ return "4"; }
};
class Layer5 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer5"; }
	std::string operator()() const override
	{ return "5"; }
};
class Layer6 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer6"; }
	std::string operator()() const override
	{ return "6"; }
};
class Layer7 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer7"; }
	std::string operator()() const override
	{ return "7"; }
};
class Layer8 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer8"; }
	std::string operator()() const override
	{ return "8"; }
};
class Layer9 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer9"; }
	std::string operator()() const override
	{ return "9"; }
};

kdb::ThreadInteger::type add_native(uint32_t const & i1,
				uint32_t const & i2)
{ return i1+i2; }

kdb::ThreadInteger::type add_contextual(kdb::ThreadInteger const & i1,
					kdb::ThreadInteger const & i2)
{ return i1+i2; }

__attribute__((noinline)) void benchmark_nothing()
{
	static Timer t("nothing");

	t.start();
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_native()
{
	kdb::ThreadInteger::type val = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("native cmp noif");
	t.start();

for (long long i=0; i<iterations; ++i)
{
	x ^= add_native(val, val);
}

	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_native_sum()
{
	kdb::ThreadInteger::type val = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("native noif sum");
	t.start();

for (long long i=0; i<iterations; ++i)
{
	x += add_native(val, val);
}

	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_native_sum_asm()
{
	kdb::ThreadInteger::type val = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("native sum asm");
	t.start();

for (long long i=0; i<iterations; ++i)
{
	x += add_native(val, val);
	__asm__("");
}

	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_contextual_noif(kdb::Environment & s)
{
	s.nested = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("context cmp noif");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= add_contextual(s.nested, s.nested);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_contextual_noif_sum_asm(kdb::Environment & s)
{
	s.nested = value;
	long long x = 0;

	static Timer t("context sum asm");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x += add_contextual(s.nested, s.nested);
		__asm__("");
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_contextual_noif_sum(kdb::Environment & s)
{
	s.nested = value;
	long long x = 0;

	static Timer t("context noif sum");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x += add_contextual(s.nested, s.nested);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_hashmap()
{
	std::unordered_map<std::string, kdb::ThreadInteger::type> hashmap;
	hashmap["user/abc/test/hello"] = 5;
	hashmap["user/abc/test/h1"] = 6;
	hashmap["user/abc/test/h2"] = 7;
	hashmap["user/abc/test/test"] = 9;
	hashmap["user/abc/nested/hello"] = 12;
	kdb::ThreadInteger::type x = 0;

	static Timer t("hashmap");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= hashmap["user/abc/nested/notav"];
	}
	t.stop();
	std::cout << t;
	dump << "hashmap " << x << std::endl;

}

__attribute__((noinline)) void benchmark_hashmap_find()
{
	std::unordered_map<std::string, kdb::ThreadInteger::type> hashmap;
	hashmap["user/abc/test/hello"] = 5;
	hashmap["user/abc/test/h1"] = 6;
	hashmap["user/abc/test/h2"] = 7;
	hashmap["user/abc/test/test"] = 9;
	hashmap["user/abc/nested/hello"] = 12;
	kdb::ThreadInteger::type x = 0;

	static Timer t("hashmap find");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		auto it = hashmap.find("user/abc/nested/hello");
		if (it != hashmap.end())
		{
			x ^= it->second;
		}
	}
	t.stop();

	std::cout << t;
	dump << "hashmap " << x << std::endl;
}

__attribute__((noinline)) void benchmark_evaluate_no_sub(kdb::Environment & s)
{
	static Timer t("evaluate no sub");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("some/string/but/%no%/%sub%/%that%/will/work");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate(kdb::Environment & s)
{
	static Timer t("evaluate");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("some/%application%/%profile%/%at%/will/work");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate0(kdb::Environment & s)
{
	static Timer t("evaluate0");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("nolayer/_________________________________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate1(kdb::Environment & s)
{
	static Timer t("evaluate1");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/_________________________________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate2(kdb::Environment & s)
{
	static Timer t("evaluate2");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/________________________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate3(kdb::Environment & s)
{
	static Timer t("evaluate3");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/%layer3%/_______________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate4(kdb::Environment & s)
{
	static Timer t("evaluate4");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/%layer3%/%layer4%/______________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate5(kdb::Environment & s)
{
	static Timer t("evaluate5");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/%layer3%/%layer4%/%layer5%/_____________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_kslookup()
{
	static Timer t("kslookup");
	using namespace kdb; 
	kdb::KeySet ks( 100,
					*kdb::Key("user/hello/some", KEY_END),
					*kdb::Key("user/hello/a/key", KEY_END),
					*kdb::Key("user/hello/b/key", KEY_END),
					*kdb::Key("user/hello/c/key", KEY_END),
					*kdb::Key("user/hello/d/key", KEY_END),
					*kdb::Key("user/other", KEY_END),
					KS_END);
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		ks.lookup("user/notfound");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer1>()([&]{
					s.nested = othervalue;});

	static Timer t("layer with");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().with<Layer1>()([]{});
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_withx(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer1>()([&]{
					s.nested = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer withx");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().with<Layer1>()([&]{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with1valout(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer1>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with1valout");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().activate<Layer1>();
		x ^= add_contextual(s.bm, s.bm);
		s.context().deactivate<Layer1>();
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with1val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer1>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with1val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With1Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with2val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with2val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With2Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with3val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with3val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With3Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with4val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with4val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With4Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with5val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with5val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With5Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with6val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with6val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With6Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with7val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with7val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With7Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with8val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with8val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()
			.with<Layer8>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With8Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with9val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with9val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()
			.with<Layer8>()
			.with<Layer9>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With9Val " << x << std::endl;
}

kdb::ThreadInteger::type g_x = 0;

__attribute__((noinline)) void benchmark_layer_withgval9(kdb::Environment & s)
{
	s.bm = value;

	g_x = 0; 
	static Timer t("layer withgval9");
	t.start();
	for (long long i=0; i<iterations1; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()
			.with<Layer8>()
			.with<Layer9>()([&]
		{
			g_x ^= add_contextual(s.bm, s.bm);
		});
		g_x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << t.name << g_x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_withnoalloc9(kdb::Environment & s)
{
	s.bm = value;

	kdb::ThreadInteger::type x = 0;
	static Timer t("layer withnoalloc");
	t.start();
	std::shared_ptr<kdb::Layer> l1 = std::make_shared<Layer1>();
	std::shared_ptr<kdb::Layer> l2 = std::make_shared<Layer2>();
	std::shared_ptr<kdb::Layer> l3 = std::make_shared<Layer3>();
	std::shared_ptr<kdb::Layer> l4 = std::make_shared<Layer4>();
	std::shared_ptr<kdb::Layer> l5 = std::make_shared<Layer5>();
	std::shared_ptr<kdb::Layer> l6 = std::make_shared<Layer6>();
	std::shared_ptr<kdb::Layer> l7 = std::make_shared<Layer7>();
	std::shared_ptr<kdb::Layer> l8 = std::make_shared<Layer8>();
	std::shared_ptr<kdb::Layer> l9 = std::make_shared<Layer9>();
	for (long long i=0; i<iterations1; ++i)
	{
		s.context()
			.withl(l1)
			.withl(l2)
			.withl(l3)
			.withl(l4)
			.withl(l5)
			.withl(l6)
			.withl(l7)
			.withl(l8)
			.withl(l9, [&]()
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << t.name << g_x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with1act(kdb::Environment & s)
{
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with1act");
	t.start();
	s.context().activate<Layer1>();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= add_contextual(s.bm, s.bm);
	}
	s.context().deactivate<Layer1>();
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with1outin(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});

	static Timer t("layer with1outin");
	t.start();
	s.context()
		.with<Layer1>()([&]
	{
		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with0out(kdb::Environment & s)
{
	static Timer t("with 0 layer");
	s.bm = value;

	kdb::ThreadInteger::type x = 0;
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	dump << t.name << x << std::endl;
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with1out(kdb::Environment & s)
{
	static Timer t("with 1 layer");
	t.start();
	s.context()
		.with<Layer1>()([&]
	{
		kdb::ThreadInteger::type x = 0;
		s.bm = value;

		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with2out(kdb::Environment & s)
{
	static Timer t("with 2 layer");
	t.start();

s.context().with<Layer1>().with<Layer2>()([&] {
	s.bm = value;
	kdb::ThreadInteger::type x = 0;
	for (long long i=0; i<iterations; ++i)
	{ x ^= add_contextual(s.bm, s.bm); }
	dump << x << std::endl; });

	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with3out(kdb::Environment & s)
{
	static Timer t("with 3 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with4out(kdb::Environment & s)
{
	static Timer t("with 4 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with5out(kdb::Environment & s)
{
	static Timer t("with 5 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with6out(kdb::Environment & s)
{
	static Timer t("with 6 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with7out(kdb::Environment & s)
{
	static Timer t("with 7 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with8out(kdb::Environment & s)
{
	static Timer t("with 8 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()
		.with<Layer8>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with9out(kdb::Environment & s)
{
	static Timer t("with 9 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()
		.with<Layer8>()
		.with<Layer9>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with9outsum(kdb::Environment & s)
{
	static Timer t("with 9 layer sum");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()
		.with<Layer8>()
		.with<Layer9>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x += add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_activation(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer2>()([&]{
					s.nested = othervalue;});

	static Timer t("layer activation");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().activate<Layer2>();
		s.context().activate<Layer1>();
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_activationval(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer2>()([&]{
					s.nested = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer activationval");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().activate<Layer2>();
		x ^= add_contextual(s.nested, s.nested);
		s.context().activate<Layer1>();
		x ^= add_contextual(s.nested, s.nested);
	}
	t.stop();
	std::cout << t;
	dump << "Layer ActivationVal " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_withN(kdb::Environment &, long long N)
{
	kdb::Coordinator c;
	kdb::ThreadContext tc(c);
	kdb::KeySet ks;
	kdb::ThreadInteger ti(ks,tc, kdb::Key("/test/nolayer", KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END));
	ti = 5;
	kdb::ThreadInteger::type x = ti;

	std::vector<std::shared_ptr<kdb::ThreadInteger>> vi;
	for (long long i = 0; i<N; ++i)
	{
		std::ostringstream os;
		os << "/test/%layer1%/";
		os << i;

		vi.push_back(std::make_shared<kdb::ThreadInteger>(ks,tc, kdb::Key(os.str().c_str(), KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END)));
	}

	static Timer *ts [10]{new Timer("layer with0"), new Timer("layer with1"), new Timer("layer with2"), new Timer("layer with3"), new Timer("layer with4"), new Timer("layer with5"), new Timer("layer with6"), new Timer("layer with7"), new Timer("layer with8"), new Timer("layer with9")};
	Timer & t = *ts[N];
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		tc.with<Layer1>()([&]{
			x ^= add_contextual(ti,ti);
		});
		x ^= add_contextual(ti,ti);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_switch(kdb::Environment & s)
{
	s.nested = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer switch");
	t.start();

for (long long i=0; i<iterations/2; ++i)
{
	s.context().activate<Layer1>();
	x ^= add_contextual(s.nested, s.nested);
	s.context().deactivate<Layer1>();
	x ^= add_contextual(s.nested, s.nested);
}

	t.stop();
	std::cout << t;
	dump << "layer switch " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_switchN(kdb::Environment &, long long N)
{
	kdb::Coordinator c;
	kdb::ThreadContext tc(c);
	kdb::KeySet ks;
	kdb::ThreadInteger ti(ks,tc, kdb::Key("/test/nolayer", KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END));
	ti = 5;
	kdb::ThreadInteger::type x = ti;

	std::vector<std::shared_ptr<kdb::ThreadInteger>> vi;
	for (long long i = 0; i<N; ++i)
	{
		std::ostringstream os;
		os << "/test/%layer1%/";
		os << i;

		vi.push_back(std::make_shared<kdb::ThreadInteger>(ks,tc, kdb::Key(os.str().c_str(), KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END)));
	}

	static Timer *ts [10]{new Timer("layer switch0"), new Timer("layer switch1"), new Timer("layer switch2"), new Timer("layer switch3"), new Timer("layer switch4"), new Timer("layer switch5"), new Timer("layer switch6"), new Timer("layer switch7"), new Timer("layer switch8"), new Timer("layer switch9")};
	Timer & t = *ts[N];
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		tc.activate<Layer1>();
		x ^= add_contextual(ti,ti);
		tc.deactivate<Layer1>();
		x ^= add_contextual(ti,ti);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_sync(kdb::Environment & s)
{
	s.nested = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("sync layers");
	t.start();

for (long long i=0; i<iterations; ++i)
{
	s.context().syncLayers();
}

	t.stop();
	std::cout << t;
	dump << "layer switch " << x << std::endl;
}

#include <unistd.h>
#ifdef _WIN32
# include <winsock2.h>
#endif

void computer_info()
{
	char hostname[1024];
	gethostname(hostname, 1023);
	std::cout << std::endl;
	std::cout << std::endl;
	std::cout << "hostname " << hostname << std::endl;
#ifdef __GNUC__
	std::cout << "gcc: " << __GNUC__  << std::endl;
#endif
#ifdef __INTEL_COMPILER
	std::cout << "icc: " << __INTEL_COMPILER  << std::endl;
#endif
#ifdef __clang__
	std::cout << "clang: " << __clang__ << std::endl;
#endif
	std::cout << "sizeof(int) " << sizeof(int) << std::endl;
	std::cout << "sizeof(long) " << sizeof(long) << std::endl;
	std::cout << "sizeof(long long) " << sizeof(long long) << std::endl;
	std::cout << "sizeof(Integer::type) " << sizeof(kdb::ThreadInteger::type) << std::endl;
	std::cout << "value " << value << std::endl;
	std::cout << "othervalue " << othervalue << std::endl;
	std::cout << "iterations " << iterations << std::endl;
	std::cout << "filename " << filename << std::endl;
	std::cout << std::endl;
}

int main(int argc, char**argv)
{
	kdb::Coordinator gc;
	kdb::ThreadContext c(gc);
	kdb::KeySet ks;
	kdb::Environment s(ks, c);
	computer_info();
	s.nested = 20;
	s.bm = 20;
	std::cout << std::endl;

	if (argc==2)
	{
		iterations = atoll(argv[1]);
		iterations1 = iterations / 100;
	}

	for (int i=0; i<benchmarkIterations; ++i)
	{
		std::cout << i << std::endl;

		benchmark_evaluate_no_sub(s);

		benchmark_layer_sync(s);
		benchmark_layer_with(s);
		benchmark_layer_withx(s);
		for (int j=0; j<10; ++j)
		{
			benchmark_layer_withN(s, j);
		}
		benchmark_layer_switch(s);
		for (int j=0; j<10; ++j)
		{
			benchmark_layer_switchN(s, j);
		}

		benchmark_evaluate(s);
		benchmark_evaluate0(s);
		benchmark_evaluate1(s);
		benchmark_evaluate2(s);
		benchmark_evaluate3(s);
		benchmark_evaluate4(s);
		benchmark_evaluate5(s);

		benchmark_kslookup();

		benchmark_hashmap();
		benchmark_hashmap_find();
	}

	data << "value,benchmark" << std::endl;
}
#include <kdbthread.hpp>
#include <kdbtimer.hpp>

// long long iterations = 100000000000LL; // elitebenchmark lookup
long long iterations = 100000LL; // elitebenchmark with/activate
// long long iterations = 100LL; // valgrind

// not needed in benchmarks:
long long iterations1 = iterations / 100;

const int benchmarkIterations = 11; // is a good number to not need mean values for median

const std::string filename = "check.txt";

const std::string csvfilename = "data.csv";

std::ofstream dump(filename);
std::ofstream data(csvfilename);



const uint32_t value = 55;
const char * s_value = "55";
const uint32_t othervalue = 66;
const char * s_othervalue = "66";

namespace kdb
{

class Person : public ThreadString
{
public:
	Person(KeySet & ks, ThreadContext &context_) :
		ThreadString(ks,context_, Key("/%layer1%/person",KEY_CASCADING_NAME, KEY_META, "default", "no name", KEY_END))
	{}
	using ThreadString::operator=;

private:
	Person() = delete;
	Person(Person const &) = delete;
	Person(Person &&) = delete;
};

class Nested : public ThreadInteger
{
public:
	Nested(KeySet & ks, ThreadContext &context_) :
	ThreadInteger(ks,context_, Key("/test/%layer1%/%thread%/%layer2%/nested",
					KEY_CASCADING_NAME,
					 KEY_META, "default", s_value,
					 KEY_END))
	{}
	using ThreadInteger::operator=;
};

class Environment : public ThreadBoolean
{
public:
	Environment(KeySet & ks, ThreadContext &context_) :
		ThreadBoolean(ks,context_, Key("/test/%layer1%",KEY_CASCADING_NAME, KEY_META, "default", "1", KEY_END)),
		nested(ks,context_),
		person(ks,context_),
		profile(ks,context_, Key("/%layer1%/profile",KEY_CASCADING_NAME, KEY_META, "default", "default", KEY_END)),
		bm(ks,context_, Key("/%layer1%/%layer2%/%layer3%/%layer4%/%layer5%/%layer6%/%layer7%/%layer8%/%layer9%/",
					KEY_CASCADING_NAME,
					KEY_META, "default", s_value,
					KEY_END))
	{}

	Nested nested;
	Person person;
	ThreadString profile;
	ThreadInteger bm;
};

}





// benchmarking

class Layer1 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer1"; }
	std::string operator()() const override
	{ return "1"; }
};
class Layer2 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer2"; }
	std::string operator()() const override
	{ return "2"; }
};
class Layer3 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer3"; }
	std::string operator()() const override
	{ return "3"; }
};
class Layer4 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer4"; }
	std::string operator()() const override
	{ return "4"; }
};
class Layer5 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer5"; }
	std::string operator()() const override
	{ return "5"; }
};
class Layer6 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer6"; }
	std::string operator()() const override
	{ return "6"; }
};
class Layer7 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer7"; }
	std::string operator()() const override
	{ return "7"; }
};
class Layer8 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer8"; }
	std::string operator()() const override
	{ return "8"; }
};
class Layer9 : public kdb::Layer
{
public:
	std::string id() const override
	{ return "layer9"; }
	std::string operator()() const override
	{ return "9"; }
};

kdb::ThreadInteger::type add_native(uint32_t const & i1,
				uint32_t const & i2)
{ return i1+i2; }

kdb::ThreadInteger::type add_contextual(kdb::ThreadInteger const & i1,
					kdb::ThreadInteger const & i2)
{ return i1+i2; }


// very fast benchmarks without any if:

__attribute__((noinline)) void benchmark_nothing()
{
	static Timer t("nothing");

	t.start();
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_native()
{
	kdb::ThreadInteger::type val = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("native cmp noif");
	t.start();
//{benchmark/loop}
for (long long i=0; i<iterations; ++i)
{
	x ^= add_native(val, val);
}
//{end}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_native_sum()
{
	kdb::ThreadInteger::type val = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("native noif sum");
	t.start();
//{benchmark/sumloop}
for (long long i=0; i<iterations; ++i)
{
	x += add_native(val, val);
}
//{end}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_native_sum_asm()
{
	kdb::ThreadInteger::type val = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("native sum asm");
	t.start();
//{benchmark/sumloopasm}
for (long long i=0; i<iterations; ++i)
{
	x += add_native(val, val);
	__asm__("");
}
//{end}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_contextual_noif(kdb::Environment & s)
{
	s.nested = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("context cmp noif");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= add_contextual(s.nested, s.nested);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_contextual_noif_sum_asm(kdb::Environment & s)
{
	s.nested = value;
	long long x = 0;

	static Timer t("context sum asm");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x += add_contextual(s.nested, s.nested);
		__asm__("");
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}


__attribute__((noinline)) void benchmark_contextual_noif_sum(kdb::Environment & s)
{
	s.nested = value;
	long long x = 0;

	static Timer t("context noif sum");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x += add_contextual(s.nested, s.nested);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}


__attribute__((noinline)) void benchmark_hashmap()
{
	std::unordered_map<std::string, kdb::ThreadInteger::type> hashmap;
	hashmap["user/abc/test/hello"] = 5;
	hashmap["user/abc/test/h1"] = 6;
	hashmap["user/abc/test/h2"] = 7;
	hashmap["user/abc/test/test"] = 9;
	hashmap["user/abc/nested/hello"] = 12;
	kdb::ThreadInteger::type x = 0;

	static Timer t("hashmap");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= hashmap["user/abc/nested/notav"];
	}
	t.stop();
	std::cout << t;
	dump << "hashmap " << x << std::endl;

}

__attribute__((noinline)) void benchmark_hashmap_find()
{
	std::unordered_map<std::string, kdb::ThreadInteger::type> hashmap;
	hashmap["user/abc/test/hello"] = 5;
	hashmap["user/abc/test/h1"] = 6;
	hashmap["user/abc/test/h2"] = 7;
	hashmap["user/abc/test/test"] = 9;
	hashmap["user/abc/nested/hello"] = 12;
	kdb::ThreadInteger::type x = 0;

	static Timer t("hashmap find");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		auto it = hashmap.find("user/abc/nested/hello");
		if (it != hashmap.end())
		{
			x ^= it->second;
		}
	}
	t.stop();

	std::cout << t;
	dump << "hashmap " << x << std::endl;
}

__attribute__((noinline)) void benchmark_evaluate_no_sub(kdb::Environment & s)
{
	static Timer t("evaluate no sub");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("some/string/but/%no%/%sub%/%that%/will/work");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate(kdb::Environment & s)
{
	static Timer t("evaluate");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("some/%application%/%profile%/%at%/will/work");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate0(kdb::Environment & s)
{
	static Timer t("evaluate0");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("nolayer/_________________________________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate1(kdb::Environment & s)
{
	static Timer t("evaluate1");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/_________________________________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate2(kdb::Environment & s)
{
	static Timer t("evaluate2");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/________________________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate3(kdb::Environment & s)
{
	static Timer t("evaluate3");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/%layer3%/_______________________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate4(kdb::Environment & s)
{
	static Timer t("evaluate4");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/%layer3%/%layer4%/______________________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_evaluate5(kdb::Environment & s)
{
	static Timer t("evaluate5");
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		s.context().evaluate("%layer%/%layer2%/%layer3%/%layer4%/%layer5%/_____________________/key");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_kslookup()
{
	static Timer t("kslookup");
	using namespace kdb; // needed for KS_END
	kdb::KeySet ks( 100,
					*kdb::Key("user/hello/some", KEY_END),
					*kdb::Key("user/hello/a/key", KEY_END),
					*kdb::Key("user/hello/b/key", KEY_END),
					*kdb::Key("user/hello/c/key", KEY_END),
					*kdb::Key("user/hello/d/key", KEY_END),
					*kdb::Key("user/other", KEY_END),
					KS_END);
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		ks.lookup("user/notfound");
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer1>()([&]{
					s.nested = othervalue;});

	static Timer t("layer with");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().with<Layer1>()([]{});
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_withx(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer1>()([&]{
					s.nested = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer withx");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().with<Layer1>()([&]{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with1valout(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer1>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with1valout");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().activate<Layer1>();
		x ^= add_contextual(s.bm, s.bm);
		s.context().deactivate<Layer1>();
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}


__attribute__((noinline)) void benchmark_layer_with1val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer1>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with1val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With1Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with2val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with2val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With2Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with3val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with3val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With3Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with4val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with4val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With4Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with5val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with5val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With5Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with6val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with6val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With6Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with7val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with7val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With7Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with8val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with8val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()
			.with<Layer8>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With8Val " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with9val(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with9val");
	t.start();
	for (long long i=0; i<iterations1/2; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()
			.with<Layer8>()
			.with<Layer9>()([&]
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << "Layer With9Val " << x << std::endl;
}

kdb::ThreadInteger::type g_x = 0;

__attribute__((noinline)) void benchmark_layer_withgval9(kdb::Environment & s)
{
	s.bm = value;

	g_x = 0; // avoid reference overhead by lambda capture
	static Timer t("layer withgval9");
	t.start();
	for (long long i=0; i<iterations1; ++i)
	{
		s.context()
			.with<Layer1>()
			.with<Layer2>()
			.with<Layer3>()
			.with<Layer4>()
			.with<Layer5>()
			.with<Layer6>()
			.with<Layer7>()
			.with<Layer8>()
			.with<Layer9>()([&]
		{
			g_x ^= add_contextual(s.bm, s.bm);
		});
		g_x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << t.name << g_x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_withnoalloc9(kdb::Environment & s)
{
	s.bm = value;

	kdb::ThreadInteger::type x = 0;
	static Timer t("layer withnoalloc");
	t.start();
	std::shared_ptr<kdb::Layer> l1 = std::make_shared<Layer1>();
	std::shared_ptr<kdb::Layer> l2 = std::make_shared<Layer2>();
	std::shared_ptr<kdb::Layer> l3 = std::make_shared<Layer3>();
	std::shared_ptr<kdb::Layer> l4 = std::make_shared<Layer4>();
	std::shared_ptr<kdb::Layer> l5 = std::make_shared<Layer5>();
	std::shared_ptr<kdb::Layer> l6 = std::make_shared<Layer6>();
	std::shared_ptr<kdb::Layer> l7 = std::make_shared<Layer7>();
	std::shared_ptr<kdb::Layer> l8 = std::make_shared<Layer8>();
	std::shared_ptr<kdb::Layer> l9 = std::make_shared<Layer9>();
	for (long long i=0; i<iterations1; ++i)
	{
		s.context()
			.withl(l1)
			.withl(l2)
			.withl(l3)
			.withl(l4)
			.withl(l5)
			.withl(l6)
			.withl(l7)
			.withl(l8)
			.withl(l9, [&]()
		{
			x ^= add_contextual(s.bm, s.bm);
		});
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	std::cout << t;
	dump << t.name << g_x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with1act(kdb::Environment & s)
{
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer with1act");
	t.start();
	s.context().activate<Layer1>();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= add_contextual(s.bm, s.bm);
	}
	s.context().deactivate<Layer1>();
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_with1outin(kdb::Environment & s)
{
	s.bm = value;
	s.context().with<Layer2>()([&]{
					s.bm = othervalue;});

	static Timer t("layer with1outin");
	t.start();
	s.context()
		.with<Layer1>()([&]
	{
		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with0out(kdb::Environment & s)
{
	static Timer t("with 0 layer");
	s.bm = value;
	
	kdb::ThreadInteger::type x = 0;
	t.start();
	for (long long i=0; i<iterations; ++i)
	{
		x ^= add_contextual(s.bm, s.bm);
	}
	t.stop();
	dump << t.name << x << std::endl;
	std::cout << t;
}


__attribute__((noinline)) void benchmark_layer_with1out(kdb::Environment & s)
{
	static Timer t("with 1 layer");
	t.start();
	s.context()
		.with<Layer1>()([&]
	{
		kdb::ThreadInteger::type x = 0;
		s.bm = value;

		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with2out(kdb::Environment & s)
{
	static Timer t("with 2 layer");
	t.start();
//{benchmark/layer2}
s.context().with<Layer1>().with<Layer2>()([&] {
	s.bm = value;
	kdb::ThreadInteger::type x = 0;
	for (long long i=0; i<iterations; ++i)
	{ x ^= add_contextual(s.bm, s.bm); }
	dump << x << std::endl; });
//{end}
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with3out(kdb::Environment & s)
{
	static Timer t("with 3 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with4out(kdb::Environment & s)
{
	static Timer t("with 4 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with5out(kdb::Environment & s)
{
	static Timer t("with 5 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with6out(kdb::Environment & s)
{
	static Timer t("with 6 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with7out(kdb::Environment & s)
{
	static Timer t("with 7 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}



__attribute__((noinline)) void benchmark_layer_with8out(kdb::Environment & s)
{
	static Timer t("with 8 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()
		.with<Layer8>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}


__attribute__((noinline)) void benchmark_layer_with9out(kdb::Environment & s)
{
	static Timer t("with 9 layer");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()
		.with<Layer8>()
		.with<Layer9>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x ^= add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_with9outsum(kdb::Environment & s)
{
	static Timer t("with 9 layer sum");
	t.start();
	s.context()
		.with<Layer1>()
		.with<Layer2>()
		.with<Layer3>()
		.with<Layer4>()
		.with<Layer5>()
		.with<Layer6>()
		.with<Layer7>()
		.with<Layer8>()
		.with<Layer9>()([&]
	{
		s.bm = value;

		kdb::ThreadInteger::type x = 0;
		for (long long i=0; i<iterations; ++i)
		{
			x += add_contextual(s.bm, s.bm);
		}
		dump << t.name << x << std::endl;
	});
	t.stop();
	std::cout << t;
}


__attribute__((noinline)) void benchmark_layer_activation(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer2>()([&]{
					s.nested = othervalue;});

	static Timer t("layer activation");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().activate<Layer2>();
		s.context().activate<Layer1>();
	}
	t.stop();
	std::cout << t;
}

__attribute__((noinline)) void benchmark_layer_activationval(kdb::Environment & s)
{
	s.nested = value;
	s.context().with<Layer2>()([&]{
					s.nested = othervalue;});
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer activationval");
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		s.context().activate<Layer2>();
		x ^= add_contextual(s.nested, s.nested);
		s.context().activate<Layer1>();
		x ^= add_contextual(s.nested, s.nested);
	}
	t.stop();
	std::cout << t;
	dump << "Layer ActivationVal " << x << std::endl;
}

__attribute__((noinline)) void benchmark_layer_withN(kdb::Environment &, long long N)
{
	kdb::Coordinator c;
	kdb::ThreadContext tc(c);
	kdb::KeySet ks;
	kdb::ThreadInteger ti(ks,tc, kdb::Key("/test/nolayer", KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END));
	ti = 5;
	kdb::ThreadInteger::type x = ti;

	// N additional integers producing overhead
	std::vector<std::shared_ptr<kdb::ThreadInteger>> vi;
	for (long long i = 0; i<N; ++i)
	{
		std::ostringstream os;
		os << "/test/%layer1%/";
		os << i;
		// std::cout << os.str().c_str() << std::endl;
		vi.push_back(std::make_shared<kdb::ThreadInteger>(ks,tc, kdb::Key(os.str().c_str(), KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END)));
	}

	static Timer *ts [10]{new Timer("layer with0"), new Timer("layer with1"), new Timer("layer with2"), new Timer("layer with3"), new Timer("layer with4"), new Timer("layer with5"), new Timer("layer with6"), new Timer("layer with7"), new Timer("layer with8"), new Timer("layer with9")};
	Timer & t = *ts[N];
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		tc.with<Layer1>()([&]{
			x ^= add_contextual(ti,ti);
		});
		x ^= add_contextual(ti,ti);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}


__attribute__((noinline)) void benchmark_layer_switch(kdb::Environment & s)
{
	s.nested = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("layer switch");
	t.start();
//{benchmark/layerswitch}
for (long long i=0; i<iterations/2; ++i)
{
	s.context().activate<Layer1>();
	x ^= add_contextual(s.nested, s.nested);
	s.context().deactivate<Layer1>();
	x ^= add_contextual(s.nested, s.nested);
}
//{end}
	t.stop();
	std::cout << t;
	dump << "layer switch " << x << std::endl;
}


__attribute__((noinline)) void benchmark_layer_switchN(kdb::Environment &, long long N)
{
	kdb::Coordinator c;
	kdb::ThreadContext tc(c);
	kdb::KeySet ks;
	kdb::ThreadInteger ti(ks,tc, kdb::Key("/test/nolayer", KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END));
	ti = 5;
	kdb::ThreadInteger::type x = ti;

	// N additional integers producing overhead
	std::vector<std::shared_ptr<kdb::ThreadInteger>> vi;
	for (long long i = 0; i<N; ++i)
	{
		std::ostringstream os;
		os << "/test/%layer1%/";
		os << i;
		// std::cout << os.str().c_str() << std::endl;
		vi.push_back(std::make_shared<kdb::ThreadInteger>(ks,tc, kdb::Key(os.str().c_str(), KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END)));
	}

	static Timer *ts [10]{new Timer("layer switch0"), new Timer("layer switch1"), new Timer("layer switch2"), new Timer("layer switch3"), new Timer("layer switch4"), new Timer("layer switch5"), new Timer("layer switch6"), new Timer("layer switch7"), new Timer("layer switch8"), new Timer("layer switch9")};
	Timer & t = *ts[N];
	t.start();
	for (long long i=0; i<iterations/2; ++i)
	{
		tc.activate<Layer1>();
		x ^= add_contextual(ti,ti);
		tc.deactivate<Layer1>();
		x ^= add_contextual(ti,ti);
	}
	t.stop();
	std::cout << t;
	dump << t.name << x << std::endl;
}


__attribute__((noinline)) void benchmark_layer_sync(kdb::Environment & s)
{
	s.nested = value;
	kdb::ThreadInteger::type x = 0;

	static Timer t("sync layers");
	t.start();
//{benchmark/layerswitch}
for (long long i=0; i<iterations; ++i)
{
	s.context().syncLayers();
}
//{end}
	t.stop();
	std::cout << t;
	dump << "layer switch " << x << std::endl;
}

#include <unistd.h>
#ifdef _WIN32
# include <winsock2.h>
#endif

void computer_info()
{
	char hostname[1024];
	gethostname(hostname, 1023);
	std::cout << std::endl;
	std::cout << std::endl;
	std::cout << "hostname " << hostname << std::endl;
#ifdef __GNUC__
	std::cout << "gcc: " << __GNUC__  << std::endl;
#endif
#ifdef __INTEL_COMPILER
	std::cout << "icc: " << __INTEL_COMPILER  << std::endl;
#endif
#ifdef __clang__
	std::cout << "clang: " << __clang__ << std::endl;
#endif
	std::cout << "sizeof(int) " << sizeof(int) << std::endl;
	std::cout << "sizeof(long) " << sizeof(long) << std::endl;
	std::cout << "sizeof(long long) " << sizeof(long long) << std::endl;
	std::cout << "sizeof(Integer::type) " << sizeof(kdb::ThreadInteger::type) << std::endl;
	std::cout << "value " << value << std::endl;
	std::cout << "othervalue " << othervalue << std::endl;
	std::cout << "iterations " << iterations << std::endl;
	std::cout << "filename " << filename << std::endl;
	std::cout << std::endl;
}

int main(int argc, char**argv)
{
	kdb::Coordinator gc;
	kdb::ThreadContext c(gc);
	kdb::KeySet ks;
	kdb::Environment s(ks, c);
	computer_info();
	s.nested = 20;
	s.bm = 20;
	std::cout << std::endl;

	if (argc==2)
	{
		iterations = atoll(argv[1]);
		iterations1 = iterations / 100;
	}

	for (int i=0; i<benchmarkIterations; ++i)
	{
		std::cout << i << std::endl;

		// if:
		/*
		benchmark_nothing();
		benchmark_native();
		benchmark_native_sum();
		benchmark_native_sum_asm();
		benchmark_contextual_noif(s);
		benchmark_contextual_noif_sum(s);
		benchmark_contextual_noif_sum_asm(s); // same as xor


		// 1-9 layer activations outside a loop
		benchmark_layer_with0out(s);
		benchmark_layer_with1out(s);
		benchmark_layer_with2out(s);
		benchmark_layer_with3out(s);
		benchmark_layer_with4out(s);
		benchmark_layer_with5out(s);
		benchmark_layer_with6out(s);
		benchmark_layer_with7out(s);
		benchmark_layer_with8out(s);
		benchmark_layer_with9out(s);
		*/

		// 1-9 values with layer activations in a loop
		/*
		benchmark_layer_with1val(s);
		benchmark_layer_with2val(s);
		benchmark_layer_with3val(s);
		benchmark_layer_with4val(s);
		benchmark_layer_with5val(s);
		benchmark_layer_with6val(s);
		benchmark_layer_with7val(s);
		benchmark_layer_with8val(s);
		benchmark_layer_with9val(s);

		benchmark_layer_with1act(s);
		benchmark_layer_with2act(s);

		// obsolete benchmarks not needed for paper:
		benchmark_layer_withnoalloc9(s); // not faster
		benchmark_layer_with9outsum(s); // 30 us
		benchmark_contextual_wrongop(s);
		benchmark_layer_activation(s);
		benchmark_layer_activationval(s);

		benchmark_layer_with1outin(s);
		benchmark_layer_with1valout(s);
		*/
		benchmark_evaluate_no_sub(s);

		benchmark_layer_sync(s);
		benchmark_layer_with(s);
		benchmark_layer_withx(s);
		for (int j=0; j<10; ++j)
		{
			benchmark_layer_withN(s, j);
		}
		benchmark_layer_switch(s);
		for (int j=0; j<10; ++j)
		{
			benchmark_layer_switchN(s, j);
		}

		benchmark_evaluate(s);
		benchmark_evaluate0(s);
		benchmark_evaluate1(s);
		benchmark_evaluate2(s);
		benchmark_evaluate3(s);
		benchmark_evaluate4(s);
		benchmark_evaluate5(s);

		benchmark_kslookup();

		benchmark_hashmap();
		benchmark_hashmap_find();
	}

	data << "value,benchmark" << std::endl;
}

