Чому компілятор Rust не оптимізує код, припускаючи, що два змінні посилання не можуть мати псевдонім?


301

Наскільки я знаю, псевдонім посилання / вказівника може перешкоджати можливості компілятора генерувати оптимізований код, оскільки вони повинні забезпечувати, щоб згенерований бінарний файл поводився правильно у випадку, коли два посилання / покажчики справді псевдоніми. Наприклад, у наведеному нижче коді С

void adds(int  *a, int *b) {
    *a += *b;
    *a += *b;
}

при компіляції clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)з -O3прапором він випромінює

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)  # The first time
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)  # The second time
   a:    c3                       retq

Тут код зберігається (%rdi)двічі у випадку int *aі int *bпсевдоніму.

Коли ми чітко повідомляємо компілятору, що ці два покажчики не можуть мати псевдонім з restrictключовим словом:

void adds(int * restrict a, int * restrict b) {
    *a += *b;
    *a += *b;
}

Тоді Кланг випустить більш оптимізовану версію двійкового коду:

0000000000000000 <adds>:
   0:    8b 06                    mov    (%rsi),%eax
   2:    01 c0                    add    %eax,%eax
   4:    01 07                    add    %eax,(%rdi)
   6:    c3                       retq

Оскільки Руст переконує (за винятком небезпечного коду), що дві змінні посилання не можуть мати псевдонім, я думаю, що компілятор повинен мати можливість видавати більш оптимізовану версію коду.

Коли я тестую з кодом нижче і компілюю його rustc 1.35.0з -C opt-level=3 --emit obj,

#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
    *a += *b;
    *a += *b;
}

він генерує:

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)
   a:    c3                       retq

Це не скористатися гарантією того, що aі bне може псевдонім.

Це тому, що поточний компілятор Rust все ще знаходиться в розробці і ще не включив псевдонімічний аналіз для оптимізації?

Це тому, що все ж є ймовірність, що могла б aі bпсевдонім, навіть у безпечній Іржі?



76
Побічне зауваження: " Оскільки Руст переконує (за винятком небезпечного коду), що два змінні посилання не можуть мати псевдоніми " - варто згадати, що навіть у unsafeкоді дозволені змінні посилання не дозволені і призводять до невизначеної поведінки. Ви можете мати прізвище необроблених покажчиків, але unsafeкод насправді не дозволяє вам ігнорувати стандартні правила Rust. Це просто поширене неправильне уявлення і, таким чином, варто звернути увагу.
Лукас Калбертодт

6
Мені знадобилось певний час, щоб зрозуміти, на що потрапляє приклад, тому що я не вмію читати asm, тому, якщо це допомагає комусь іншому: це зводиться до того, чи можна переосмислити дві +=операції в тілі . Якщо покажчики НЕ псевдонім, вони можуть, ви навіть можете побачити , що становить у другому ассемблерного лістингу: . Але якщо вони роблять псевдонім, вони не можуть, оскільки до того часу, коли ви додасте його вдруге, воно буде містити інше значення, ніж перший раз (те, яке ви зберігаєте в рядку першого списку asm). adds*a = *a + *b + *bb* + *b2: 01 c0 add %eax,%eax*b4:
dlukes

Відповіді:


364

Іржа спочатку зробив включити LLVM в noaliasатрибут, але цей заподіяну miscompiled код . Коли всі підтримувані версії LLVM більше не будуть компілювати код, він буде знову ввімкнено .

Якщо додати -Zmutable-noalias=yesдо параметрів компілятора, ви отримаєте очікувану збірку:

adds:
        mov     eax, dword ptr [rsi]
        add     eax, eax
        add     dword ptr [rdi], eax
        ret

Простіше кажучи, Руст скрізь ставив еквівалент restrictключового слова C , набагато більш поширений, ніж будь-яка звичайна програма C. Це реалізувало кутові випадки LLVM більше, ніж було вміло правильно впоратися. Виявляється, програмісти C і C ++ просто не використовують так часто, як це використовується в Rust.restrict&mut

Це траплялося кілька разів .

  • Іржа 1,0 - 1,7 - noaliasвключена
  • Іржа від 1,8 до 1,27 - noaliasвідключена
  • Іржа 1,28 до 1,29 - noaliasувімкнено
  • Іржа від 1,30 до ??? - noaliasінвалід

Пов'язані проблеми з іржею


12
Це не дивно. Незважаючи на те, що LLVM широко розширює претензії на багатомовну привітність, спеціально розроблений як бекенд C ++, і він завжди мав сильну тенденцію задихатися від речей, які не схожі на C ++.
Мейсон Уілер

47
@MasonWheeler Якщо перейти до деяких питань, ви можете знайти приклади коду С, які використовують restrictі неправильно компілюють як у Clang, так і в GCC. Це не обмежується мовами, які недостатньо "C ++", якщо ви не зараховуєте C ++ до цієї групи .
Шепмайстер

6
@MasonWheeler: Я не думаю, що LLVM насправді був розроблений навколо правил C або C ++, а навпаки, правил LLVM. Це робить припущення, які зазвичай відповідають дійсності для коду C або C ++, але, наскільки я можу сказати, дизайн спирається на модель залежності від статичних даних, яка не може впоратися з хитрими кутовими випадками. Це було б добре, якби він песимістично припускав залежності даних, які неможливо спростувати, але натомість трактує як операції, які не існують, які б записували сховище з тим самим бітовим шаблоном, що і у нього, і які мають потенційну, але не доказувальну залежність даних від читати і писати.
Supercat

8
@supercat Я декілька разів читав ваші коментарі, але визнаю, що я здивований - я не маю поняття, що вони стосуються цього питання чи відповіді. Не визначена поведінка тут не грає, це "лише" випадок багаторазової оптимізації, що погано взаємодіють між собою.
Шепмайстер

2
@avl_sweden, щоб повторити, це просто помилка . Крок оптимізації розгортання циклу зробив (робить?) Не повністю noaliasвраховані покажчики під час виконання. Він створив нові покажчики на основі вхідних покажчиків, неправильно копіюючи noaliasатрибут, навіть якщо нові вказівники робили псевдоніми.
Шепмайстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.