E
Danke für die Antworten!
Habe mal etwas herumgetestet. Ist jetzt nicht ganz aussagekräftig da 3 mögliche Typen immer noch recht wenig sind, wenn man weitere Typen hinzufügt steigt ja die Komplexität die richtige Operation auszuwählen.
#include <cassert>
#include <iostream>
#include <chrono>
#include <vector>
#include <cstdlib>
#include <algorithm>
#include <stdexcept>
struct invalid_operation : std::runtime_error
{
invalid_operation(char const* msg)
: std::runtime_error(msg)
{ }
};
struct Any
{
Any()
: type(Type::None)
{ }
Any(int Int)
: type(Type::Int)
{
data.Int = Int;
}
Any(double Float)
: type(Type::Float)
{
data.Float = Float;
}
enum class Type : unsigned
{
None = 0,
Int = 1,
Float = 2
} type;
union Data
{
int Int;
double Float;
} data;
};
struct Any2;
struct VTable
{
Any2 (*add)(Any2 const&, Any2 const&);
Any2 (*mul)(Any2 const&, Any2 const&);
Any::Type type;
};
extern VTable NoneVTable;
extern VTable IntVTable;
extern VTable FloatVTable;
struct Any2
{
Any2()
: vtable(&NoneVTable)
{ }
Any2(int Int)
: vtable(&IntVTable)
{
data.Int = Int;
}
Any2(double Float)
: vtable(&FloatVTable)
{
data.Float = Float;
}
VTable const* vtable;
union Data
{
int Int;
double Float;
} data;
};
VTable NoneVTable =
{
[](Any2 const&, Any2 const&) -> Any2
{
throw invalid_operation("add");
},
[](Any2 const&, Any2 const&) -> Any2
{
throw invalid_operation("mul");
},
Any::Type::None
};
VTable IntVTable =
{
[](Any2 const& lhs, Any2 const& rhs) -> Any2
{
switch(rhs.vtable->type)
{
case Any::Type::Int:
return Any2(lhs.data.Int + rhs.data.Int);
case Any::Type::Float:
return Any2(lhs.data.Int + rhs.data.Float);
case Any::Type::None:
throw invalid_operation("add");
}
assert(false);
},
[](Any2 const& lhs, Any2 const& rhs) -> Any2
{
switch(rhs.vtable->type)
{
case Any::Type::Int:
return Any2(lhs.data.Int * rhs.data.Int);
case Any::Type::Float:
return Any2(lhs.data.Int * rhs.data.Float);
case Any::Type::None:
throw invalid_operation("mul");
}
assert(false);
},
Any::Type::Int
};
VTable FloatVTable =
{
[](Any2 const& lhs, Any2 const& rhs) -> Any2
{
switch(rhs.vtable->type)
{
case Any::Type::Int:
return Any2(lhs.data.Float + rhs.data.Int);
case Any::Type::Float:
return Any2(lhs.data.Float + rhs.data.Float);
case Any::Type::None:
throw invalid_operation("add");
}
assert(false);
},
[](Any2 const& lhs, Any2 const& rhs) -> Any2
{
switch(rhs.vtable->type)
{
case Any::Type::Int:
return Any2(lhs.data.Float * rhs.data.Int);
case Any::Type::Float:
return Any2(lhs.data.Float * rhs.data.Float);
case Any::Type::None:
throw invalid_operation("mul");
}
assert(false);
},
Any::Type::Float
};
Any add1(Any lhs, Any rhs)
{
switch(lhs.type)
{
case Any::Type::Int:
{
switch(rhs.type)
{
case Any::Type::Int:
return Any(lhs.data.Int + rhs.data.Int);
case Any::Type::Float:
return Any(lhs.data.Int + rhs.data.Float);
case Any::Type::None:
throw invalid_operation("add");
}
}
case Any::Type::Float:
{
switch(rhs.type)
{
case Any::Type::Int:
return Any(lhs.data.Float + rhs.data.Int);
case Any::Type::Float:
return Any(lhs.data.Float + rhs.data.Float);
case Any::Type::None:
throw invalid_operation("add");
}
}
case Any::Type::None:
throw invalid_operation("add");
}
assert(false);
}
constexpr unsigned combined(Any::Type left, Any::Type right)
{
return static_cast<int>(left) | (static_cast<int>(right) << 16);
}
Any add2(Any lhs, Any rhs)
{
switch (combined(lhs.type, rhs.type))
{
case combined(Any::Type::Int, Any::Type::Int): return Any(lhs.data.Int + rhs.data.Int);
case combined(Any::Type::Int, Any::Type::Float): return Any(lhs.data.Int + rhs.data.Float);
case combined(Any::Type::Float, Any::Type::Int): return Any(lhs.data.Float + rhs.data.Int);
case combined(Any::Type::Float, Any::Type::Float): return Any(lhs.data.Float + rhs.data.Float);
default:
if(lhs.type == Any::Type::None || rhs.type == Any::Type::None)
throw invalid_operation("add");
}
assert(false);
}
Any add3(Any lhs, Any rhs)
{
static Any(*lookup[3][3])(Any&&, Any&&)
{
{
[](Any&&, Any&&) -> Any { throw invalid_operation("add"); },
[](Any&&, Any&&) -> Any { throw invalid_operation("add"); },
[](Any&&, Any&&) -> Any { throw invalid_operation("add"); }
},
{
[](Any&&, Any&&) -> Any { throw invalid_operation("add"); },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Int + rhs.data.Int ; },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Int + rhs.data.Float ; }
},
{
[](Any&&, Any&&) -> Any { throw invalid_operation("add"); },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Float + rhs.data.Int ; },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Float + rhs.data.Float ; }
}
};
return lookup[static_cast<unsigned>(lhs.type)][static_cast<unsigned>(rhs.type)](std::move(lhs), std::move(rhs));
}
Any2 add4(Any2 lhs, Any2 rhs)
{
return lhs.vtable->add(lhs, rhs);
}
Any mul1(Any lhs, Any rhs)
{
switch(lhs.type)
{
case Any::Type::Int:
{
switch(rhs.type)
{
case Any::Type::Int:
return Any(lhs.data.Int * rhs.data.Int);
case Any::Type::Float:
return Any(lhs.data.Int * rhs.data.Float);
case Any::Type::None:
throw invalid_operation("mul");
}
}
case Any::Type::Float:
{
switch(rhs.type)
{
case Any::Type::Int:
return Any(lhs.data.Float * rhs.data.Int);
case Any::Type::Float:
return Any(lhs.data.Float * rhs.data.Float);
case Any::Type::None:
throw invalid_operation("mul");
}
}
case Any::Type::None:
throw invalid_operation("mul");
}
assert(false);
}
Any mul2(Any lhs, Any rhs)
{
switch (combined(lhs.type, rhs.type))
{
case combined(Any::Type::Int, Any::Type::Int): return Any(lhs.data.Int * rhs.data.Int);
case combined(Any::Type::Int, Any::Type::Float): return Any(lhs.data.Int * rhs.data.Float);
case combined(Any::Type::Float, Any::Type::Int): return Any(lhs.data.Float * rhs.data.Int);
case combined(Any::Type::Float, Any::Type::Float): return Any(lhs.data.Float * rhs.data.Float);
default:
if(lhs.type == Any::Type::None || rhs.type == Any::Type::None)
throw invalid_operation("mul");
}
assert(false);
}
Any mul3(Any lhs, Any rhs)
{
static Any(*lookup[3][3])(Any&&, Any&&)
{
{
[](Any&&, Any&&) -> Any { throw invalid_operation("mul"); },
[](Any&&, Any&&) -> Any { throw invalid_operation("mul"); },
[](Any&&, Any&&) -> Any { throw invalid_operation("mul"); }
},
{
[](Any&&, Any&&) -> Any { throw invalid_operation("mul"); },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Int * rhs.data.Int ; },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Int * rhs.data.Float ; }
},
{
[](Any&&, Any&&) -> Any { throw invalid_operation("mul"); },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Float * rhs.data.Int ; },
[](Any&& lhs, Any&& rhs) -> Any { return lhs.data.Float * rhs.data.Float ; }
}
};
return lookup[static_cast<unsigned>(lhs.type)][static_cast<unsigned>(rhs.type)](std::move(lhs), std::move(rhs));
}
Any2 mul4(Any2 lhs, Any2 rhs)
{
return lhs.vtable->mul(lhs, rhs);
}
Any randomAny()
{
if(std::rand() % 2)
return Any(std::rand());
else
return Any(double(std::rand()));
}
Any2 randomAny2()
{
if(std::rand() % 2)
return Any2(std::rand());
else
return Any2(double(std::rand()));
}
template< typename T,
T Add(T lhs, T rhs),
T Mul(T lhs, T rhs),
T Random(),
unsigned N>
void test(char const* name)
{
using namespace std;
using namespace std::chrono;
vector<T> m1(N * N), m2(N * N), m3(N * N, T(0));
srand(0xAFFEAFFE);
generate(m1.begin(), m1.end(), Random);
generate(m2.begin(), m2.end(), Random);
auto start = high_resolution_clock::now();
for(unsigned i = 0; i < N; ++i)
{
for(unsigned j = 0; j < N; ++j)
{
for(unsigned k = 0; k < N; ++k)
{
T tmp = Mul(m1[i * N + k], m2[k * N + j]);
m3[i * N + j] = Add(m3[i * N + j], tmp);
}
}
}
auto end = high_resolution_clock::now();
cout << name << "\t: " << duration_cast<milliseconds>(end - start).count() << "ms" << endl;
}
double addDouble(double lhs, double rhs)
{
return lhs + rhs;
}
double mulDouble(double lhs, double rhs)
{
return lhs * rhs;
}
double randomDouble()
{
return std::rand();
}
int main()
{
test<double, addDouble, mulDouble, randomDouble, 700>("double ");
test<Any, add1, mul1, randomAny, 700>("version1");
test<Any, add2, mul2, randomAny, 700>("version2");
test<Any, add3, mul3, randomAny, 700>("version3");
test<Any2, add4, mul4, randomAny2, 700>("version4");
}
[ethon@desktop-fedora-20 WorkDir]$ ./any
double : 439ms
version1 : 6388ms
version2 : 6849ms
version3 : 6462ms
version4 : 7770ms
[ethon@desktop-fedora-20 WorkDir]$ ./any
double : 442ms
version1 : 6188ms
version2 : 6454ms
version3 : 6459ms
version4 : 7791ms
[ethon@desktop-fedora-20 WorkDir]$ ./any
double : 446ms
version1 : 6184ms
version2 : 6530ms
version3 : 6442ms
version4 : 7820ms
g++ -Wall -Wextra -O3 -std=c++11
Wenn noch jemand eine Idee hat, bitte her damit!
TyRoXx schrieb:
JITter versuchen in der Regel die Typen herzuleiten, damit Typunterscheidungen wegfallen können. LuaJIT und so ziemlich alle JavaScript-Implementierungen machen das seit Jahren so.
Stimmt. Aber das geht nicht immer. Und dann wird's interessant.