比较代码片段A:
struct Vector2(T) {
// ...
auto opCast(U)() {
return U(x, y);
}
void opOpAssign(string op)(Vector2 vector) {
mixin ("x" ~ op ~ "= vector.x;");
mixin ("y" ~ op ~ "= vector.y;");
}
}
void main() {
auto fVec = Vector2!float(1.5, 1.5);
auto dVec = Vector2!double(1.5, 1.5);
// Benchmark: Loop following 10 million times.
fVec += cast(Vector2!float) dVec;
dVec -= cast(Vector2!double) fVec;
}
与B:
struct Vector2(T) {
// ...
void opOpAssign(string op, U)(Vector2!U vector) {
mixin ("x" ~ op ~ "= vector.x;");
mixin ("y" ~ op ~ "= vector.y;");
}
}
void main() {
auto fVec = Vector2!float(1.5, 1.5);
auto dVec = Vector2!double(1.5, 1.5);
// Benchmark: Same as A.
fVec += dVec;
dVec -= fVec;
}
在我的基准测试(DMD,Win7)中,A比B快约50ms。这是什么原因?如果A更快我想使用它,但我不能得到Vector2!double来隐式地转换为Vector2!float无论我尝试什么。关于如何隐式投射这些类型的任何想法?或者是否有一些争论为什么我不应该隐含地施放它们?
我正在设置GDC和LDC来与这些编译器进行此基准测试,但有人知道这是否只是DMD优化问题?
答案 0 :(得分:3)
就编译器而言,同一模板的两个不同实例没有比两个完全独立的类型更多的共同点。你可以声明
struct VectorFloat
{
...
}
struct VectorDouble
{
...
}
而不是模板化Vector2
,它没有任何区别。 Vector2!float
和Vector!double
是完全不同的类型。对于您声明的任何类型,如果您想要在它们之间进行转换,您将必须声明它们 - 无论是opCast
,alias this
,构造函数还是其他任何类型。我相信,你将隐式转换工作的唯一方法是使用alias this
,尽管ratchet freak指出,隐式转换浮点数和双精度数是不 D通常如何运作,可以说是一个坏主意。
至于为什么A比B快,我不知道。我本来希望它是另一种方式。但是,根据编译器究竟在做什么,它可能会在以后发生变化,并且它可能很容易因编译器而异。而且由于你只看到超过 1000万次迭代的50ms差异,我会从API的角度来看更合理的版本(无论你认为是什么)。我会争论第一个,因为我不认为在float和double之间隐式转换是个好主意,但这取决于你,因为它是你的代码。
顺便说一下,如果需要,可以使用std.conv.to而不是直接调用opCast
。这种方式不那么容易出错,因为如果你搞砸定义opCast
它就会大喊大叫,而编译器更有可能只是这样做,因为演员是一个非常钝的工具。
答案 1 :(得分:1)
你不应该隐式地将double
强制转换为单个精度float
点,就像你不能隐式地从long
转换为{{1}因为你会失去精确度。
大多数语言要求您在转换时想要丢失精确度时明确说明。并且最好是遵循语言中的现有约定而不是强制执行自己的约定。