Чому асинхронні автомати класів (а не конструкцій) у Roslyn?


86

Давайте розглянемо цей дуже простий метод асинхронізації:

static async Task myMethodAsync() 
{
    await Task.Delay(500);
}

Коли я компілюю це за допомогою VS2013 (попередній компілятор Roslyn), сформований автомат-стан є структурою.

private struct <myMethodAsync>d__0 : IAsyncStateMachine
{  
    ...
    void IAsyncStateMachine.MoveNext()
    {
        ...
    }
}

Коли я компілюю його з VS2015 (Roslyn), згенерований код такий:

private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
    ...
    void IAsyncStateMachine.MoveNext()
    {
        ...
    }
}

Як бачите, Roslyn генерує клас (а не структуру). Якщо я добре пам'ятаю, перші реалізації підтримки async / await у старому компіляторі (я думаю, CTP2012) також генерували класи, а потім його було змінено на struct з причин продуктивності. (у деяких випадках ви можете повністю уникнути боксу та розподілу купи ...) (Див. це )

Хтось знає, чому це знову змінили в Росліні? (Я не маю жодних проблем з цим, я знаю, що ця зміна прозора і не змінює поведінку будь-якого коду, мені просто цікаво)

Редагувати:

Відповідь від @Damien_The_Unbeliever (і вихідний код :)) imho все пояснює. Описана поведінка Roslyn стосується лише побудови налагодження (і це потрібно через обмеження CLR, зазначене в коментарі). У Release він також генерує структуру (з усіма перевагами цього ..). Отже, це видається дуже розумним рішенням для підтримки редагування та продовження та кращої продуктивності у виробництві. Цікаві речі, дякую за всіх, хто брав участь!


2
Я підозрюю, що вони вирішили, що складність (змінні структури) не варта. asyncМетоди майже завжди мають справжню асинхронну точку - таку, awaitяка дає контроль, що вимагатиме того, щоб структура все одно була позначена. Я вважаю, що структури лише зменшать тиск на пам’ять для asyncметодів, які випадково працюють синхронно.
Стівен Клірі

Відповіді:


112

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

І тут, у рядку 60 AsyncRewriter , ми знаходимо:

// The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class.
var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct;

Отже, хоча є певна привабливість щодо використання structs, найкращим варіантом, очевидно, був обраний великий виграш, якщо дозволити редагуванню та продовженню працювати в межах asyncметодів.


18
Дуже хороший улов! І виходячи з цього, ось що я також виявив: Це трапляється лише тоді, коли ви створюєте його під час налагодження (має сенс, саме тоді ви робите EnC ..), але у Release вони створюють структуру (очевидно EnableEditAndContinue в цьому випадку хибна .. .). До речі. Я також спробував вивчити код, але не знайшов цього. Дуже дякую!
gregkalapos

3

Важко дати остаточну відповідь на щось подібне (якщо хтось із команди компілятора не заходить :)), але є кілька моментів, які ви можете врахувати:

Виконання "бонусу" конструкцій завжди є компромісом. В основному ви отримуєте наступне:

  • Ціннісна семантика
  • Можливе розподіл стека (можливо, навіть реєстру?)
  • Уникнення опосередкованості

Що це означає у справі очікування? Ну насправді ... нічого. Існує лише дуже короткий проміжок часу, протягом якого державна машина знаходиться в стеку - пам’ятайте, awaitефективно робить a return, тому стек методів гине; державна машина десь має бути збережена, і це "десь" точно є на купу. Термін служби стека не підходить асинхронному коду :)

Окрім цього, державна машина порушує деякі хороші вказівки щодо визначення структур:

  • structs має бути не більше 16 байт - державна машина містить два покажчики, які самі по собі акуратно заповнюють обмеження 16 байт на 64 біти. Окрім цього, існує сама держава, тому вона перевищує "межу". Це не є великою справою, оскільки цілком ймовірно, що воно коли-небудь передається лише за допомогою посилання, але зверніть увагу, що це не зовсім відповідає варіанту використання для структур - структури, яка в основному є посилальним типом.
  • structs повинні бути незмінними - ну, це, мабуть, не потребує особливих коментарів. Це державна машина . Знову ж таки, це не велика справа, оскільки структура - це автоматично згенерований код та приватний, але ...
  • structs логічно повинні представляти одне значення. Безумовно, тут не так, але це вже випливає з того, що в першу чергу є мінливий стан.
  • Його не слід часто вводити в бокс - тут не проблема, оскільки ми використовуємо дженерики скрізь . Зрештою держава знаходиться десь на купі, але принаймні це не відбувається (автоматично). Знову ж таки, той факт, що він використовується лише внутрішньо, робить це майже порожнім.

І звичайно, все це у випадку, коли немає закриття. Коли у вас є місцеві жителі (або поля), які перетинають awaits, стан додатково роздувається, обмежуючи корисність використання структури.

З огляду на все це, підхід до класу, безумовно, є більш чистим, і я не очікував би помітного збільшення продуктивності від використання structзамість нього. Всі об'єкти , задіяні мають однакову тривалість життя, так що єдиний спосіб поліпшити продуктивність пам'яті було б зробити все з них structз (магазин в деякому буфері, наприклад) - що неможливо в загальному випадку, звичайно. І більшість випадків, коли ви б використовували awaitспочатку (тобто деякі асинхронні роботи вводу-виводу), вже залучають інші класи - наприклад, буфери даних, рядки ... Навряд чи ви б awaitщось, що просто повертається, 42не виконуючи жодних виділення купи.

Врешті-решт, я б сказав, що єдиним місцем, де ви дійсно побачите реальну різницю в продуктивності, були б тести. А оптимізація для тестів - це, м’яко кажучи, дурна ідея ...


Вам не завжди потрібен член команди компілятора, коли ви можете піти і прочитати джерело, і вони залишили корисний коментар :-)
Damien_The_Unbeliever

3
@Damien_The_Unbeliever Так, це, безумовно, була чудова знахідка, я вже підтримав вашу відповідь: P
Luaan

1
Структура дуже допомагає, якщо код не працює асинхронно, наприклад, дані вже знаходяться в буфері.
Ян Рінгроуз,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.