Можно ли думать о «пусть» и других идиомах Rust с точки зрения сборки? - PullRequest
0 голосов
/ 27 июня 2018

При написании на языке C легко понять, как будут выглядеть задания после компиляции. Я знаю, что Rust - это язык более высокого уровня, который использует функциональные возможности, но, поскольку он называется «системным языком», мне интересно, возможно ли это и в Rust.

Например, я хочу перебрать массив целых чисел и вычислить наибольшее произведение из 3 соседних чисел:

unsigned int a[10] = {4, 2, 3, 8, 1, 0, 7, 4, 9, 2};
unsigned int i, p=1, max=0;
for(i=0; i<8; i++, p=1) {
    p = a[i] * a[i+1] * a[i+2];
    if(p>max) max = p;
}

Эквивалентный код Rust может выглядеть следующим образом (не уверен, что это идиоматический способ):

let a = [4, 2, 3, 8, 1, 0, 7, 4, 9, 2];
let mut max = 0;
for i in 0..8 {
    let p = a[i] * a[i + 1] * a[i + 2];
    if p > max {
        max = p;
    }
}

В C, p - это переменная, которая определена перед циклом и ей каждый раз назначается новое значение; в Rust, с другой стороны, в цикле используется let p, что вызывает путаницу в процедурном смысле.

C ближе к коду сборки? Или можно так же рассуждать о Rust?

1 Ответ

0 голосов
/ 27 июня 2018

TL; DR: вполне возможно рассуждать о коде Rust с точки зрения сборки. Тем не менее, вы должны быть знакомы с функциями безопасности и высокоуровневыми абстракциями, которые он предлагает, чего нет у C.


Если вы хотите подумать о C и Rust с точки зрения сборки, первое, на что нужно обратить внимание: какой компилятор C вы используете?

GCC и Clang / LLVM излучают совершенно разные сборки. В то время как сборка, сгенерированная GCC, довольно близка к коду C , Clang / LLVM, похоже, развертывает цикл в исходном примере . (Это открывает вопрос: «что ближе к сборке - C или C?»)

Rust также использует LLVM, поэтому давайте сравним его с Clang. Это часть сборки Clang:

  imul esi, eax
  mov edx, dword ptr [rdi + 20]
  imul esi, edx
  cmp esi, ecx
  cmovbe esi, ecx

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

Соответствующий фрагмент Сборка ржавчины выглядит следующим образом:

  cmp r8, 5
  je .LBB0_16
  imul eax, edx
  mov esi, dword ptr [rdi + 20]
  imul eax, esi
  cmp eax, ecx
  cmovbe eax, ecx

Единственное отличие состоит в том, что некоторые регистры используются по-разному и что в начале есть сравнение и условный переход. Это проверка границ, выполняемая Rust при индексации в a. У вас этого нет в C - если массив слишком мал, может произойти все что угодно. В Rust вы получаете явную панику, но это происходит за небольшую плату (проверка времени выполнения = больше инструкций).

Если проблема связана с дополнительными затратами, есть аварийный люк: небезопасный код.

p = unsafe {*a.as_ptr().offset(i) * *a.as_ptr().offset(i+1) * *a.as_ptr().offset(i+2)};

С этим изменением мы получаем сборку , которая в основном похожа на сборку C, но мы теряем часть безопасности Rust.


Все это говорит о том, что на самом деле нет сборки, эквивалентной let x = Rust или C int x =. Эти идиомы являются абстракциями более высокого уровня, которые говорят: «Я хочу иметь новую локальную переменную». Компилятор должен решить, что с этим делать. Он может использовать ячейку памяти или доступный регистр, в зависимости от того, что больше подходит, но ЦП не заботится о переменных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...