Большая часть высокопроизводительного кода в современных консольных играх написана с использованием своего рода промежуточного звена между ассемблером и C ++: внутренними особенностями компилятора . Эти конструкции выглядят и анализируются как функции C ++, но фактически транслируются в отдельные машинные инструкции . Так, например, моя функция «зажимать каждое значение вектора V, чтобы быть> = a и <= b» выглядит так
// for each v.x, ensure v.x >= a.x && v.x <= b.x
inline __m128 ClampSIMD( const __m128 &v, const __m128 & a, const __m128 & b )
{
return _mm_max_ps( a, _mm_min_ps( v, b ) );
}
В таких функциях я все еще думаю о конкретных машинных инструкциях , но мне удобно писать их на C, так что мне не нужно беспокоиться о раскраске и планировании регистров, загрузке операций и других скучных деталях.
Вам все еще нужно знать, какие инструкции поддерживает ЦП, особенно потому, что современные компиляторы ужасны при векторизации кода по сравнению с тем, насколько хорошо умный человек может выполнять эту работу. Также иногда тонкие детали того, как вы расположите свой код могут иметь огромное значение для производительности , которые не очевидны без понимания того, что делает машина.
Хотя мы не можем кодировать в сборке, мы все равно много отлаживаем в сборке. Оптимизация компиляторов агрессивно реорганизует код таким образом, что отладчики не могут справиться с этим, поэтому часто при отладке сборки «режима выпуска» лучше всего открывать дизассемблер и таким образом отслеживать код. Этот доклад GDC о «судебной отладке» сбоев иллюстрирует многие причины и способы отладки на этом уровне.