D неявно приведенные типы Vector (T) - PullRequest
1 голос
/ 10 января 2012

Сравнить фрагменты кода 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 на ~ 50 мс быстрее, чем B. Любая причина, почему этоявляется?Если A быстрее, я бы хотел использовать его, но я не могу заставить Vector2! Double неявно приводить к плавающему Vector2! Независимо от того, что я пытаюсь.Любая идея о том, как я могу неявно приводить эти типы?Или есть какой-то аргумент, почему я не должен неявно приводить их?

Я настраиваю GDC и LDC для выполнения этого теста с этими компиляторами, но кто-нибудь знает, если это проблема оптимизации только для DMD

Ответы [ 2 ]

3 голосов
/ 10 января 2012

Два разных экземпляра одного и того же шаблона не имеют ничего общего, кроме двух совершенно разных типов в том, что касается компилятора. Вы можете объявить

struct VectorFloat
{
    ...
}

struct VectorDouble
{
    ...
}

вместо шаблонизации Vector2, и это не имеет значения. Vector2!float и Vector!double - это совершенно разные типы. И с любыми типами, которые вы объявляете, если вам нужны способы конвертации между ними, вам придется объявлять их - будь то opCast, alias this, конструкторы или что-то еще. Я полагаю, что единственный способ получить неявное преобразование в работу - это alias this, хотя, как указывает ratchet freak , неявное преобразование между числами с плавающей запятой и двойными числами - это не как обычно работает D и, возможно, это плохая идея.

Относительно того, почему A быстрее, чем B, я не знаю. Я бы на самом деле ожидал, что все будет наоборот. Но в зависимости от того, что именно делает компилятор, он может измениться позже и может легко варьироваться от компилятора к компилятору. И поскольку вы видите разницу только в 50 мс за 10 миллионов итераций, я бы выбрал версию, которая имеет больше смысла с точки зрения API (в зависимости от того, что вы думаете). Хотя я бы поспорил с первым, просто потому, что не думаю, что это хорошая идея для неявного преобразования между float и double, но это зависит от вас, так как это ваш код.

Кстати, вы можете использовать std.conv.to вместо прямого вызова opCast, если хотите. Это менее подвержено ошибкам, так как с тех пор вы будете кричать, если вы испортите определение opCast, в то время как компилятор, скорее всего, все равно просто сделает это, поскольку приведение очень тупой инструмент.

1 голос
/ 10 января 2012

Вы не должны неявно приводить double к одной точности float в точке, точно так же, как вы не можете неявно преобразовать из long в int, потому что вы потеряете точность.

Большинство языков требуют, чтобы вы ясно указывали, когда хотите потерять точность при конвертации. и будет лучше, если вы будете следовать существующим соглашениям на языке, а не применять свои собственные.

...