Использование типов С# для выражения единиц измерения

Я пытаюсь получить то, что я называю системой единиц измерения, обернув двойную структуру. У меня есть структуры С#, такие как Meter, Second, Degree и т.д. Моя первоначальная идея заключалась в том, что после того, как компилятор будет встроен в все, что у меня получилось бы так же, как если бы использовались double.

Мои явные и неявные операторы просты и понятны, а компилятор действительно встраивает их, но код с Meter и Second в 10 раз медленнее, чем тот же код, используя double.

Мой вопрос заключается в следующем: почему С# -компилятор не может сделать код, используя Second, оптимальным, как код с использованием double, если он все равно встраивает?

Вторая определяется следующим образом:

struct Second
{
    double _value; // no more fields.

    public static Second operator + (Second left, Second right) 
    { 
        return left._value + right._value; 
    }
    public static implicit Second operator (double value) 
    { 
        // This seems to be faster than having constructor :)
        return new Second { _value = value };
    }

    // plenty of similar operators
}

Update:

Я не спрашивал, подходит ли структура. Он делает.

Я не спрашивал, будет ли код встроен. JIT делает это.

Я проверил операции сборки, выпущенные во время выполнения. Они были разными для кода:

var x = new double();
for (var i = 0; i < 1000000; i++)
{ 
    x = x + 2;
    // Many other simple operator calls here
}

и вот так:

var x = new Second();
for (var i = 0; i < 1000000; i++)
{ 
    x = x + 2;
    // Many other simple operator calls here
}

В разборке не было никаких инструкций по вызову, поэтому операции были фактически встроены. Но разница значительна. Тесты производительности показывают, что использование Second похоже на 10 раз медленнее, чем использование double.

Итак, мои вопросы (внимание!): почему JIT-код IA64 отличается для вышеуказанных случаев? Что можно сделать, чтобы сделать структуру так быстро, как дважды? Кажется, нет никакой теоретической разницы между двойным и вторым, какова глубокая причина различий, которые я видел?

Ответ 1

Это мое мнение, пожалуйста, напишите комментарий, если вы не согласны, вместо молчаливого downvoting.

Компилятор С# не устанавливает его. JIT-компилятор, но для нас это недетерминированно, потому что поведение JITER не просто.

В случае double операторы не вызываются. Операнды добавляются прямо в стек, используя код операции add. В вашем случае вызывается метод op_Add плюс три struct копирования в стек и из него.

Чтобы оптимизировать его, начните с замены struct на class. Он, по крайней мере, минимизирует количество копий.

Ответ 2

Компилятор С# ничего не делает - JIT может это сделать, но не обязан. Однако он все равно должен быть очень быстрым. Я бы, вероятно, удалил неявное преобразование в +, хотя (см. Ниже использование конструктора) - еще один оператор для просмотра:

private readonly double _value;
public double Value { get { return _value; } }
public Second(double value) { this._value = value; }
public static Second operator +(Second left, Second right) {
    return new Second(left._value + right._value);
}
public static implicit operator Second(double value)  {
    return new Second(value);
}

Вставка JIT ограничена конкретными сценариями. Будет ли этот код удовлетворять их? Трудно сказать, но он должен работать и работать достаточно быстро для большинства сценариев. Проблема с + заключается в том, что есть код операции IL для добавления удвоений; он почти не работает - где - когда ваш код вызывает несколько статических методов и конструктор; всегда будут какие-то накладные расходы, даже когда они вложены.