Збирач сміття в Go: вирішення проблеми чуйності в Go 1.5

Даний матеріал являє собою переклад блог посту, який в реальному часі ведуть хлопці з Sourcegraph з конференції GopherCon 2015, яка проходить в ці дні в Денвері, Колорадо. Повне відео і слайди доповіді будуть додані до посту, як тільки будуть доступні.

Річард Л. Хадсон (Рік) знаменитий за своїми роботами в управлінні пам'яттю, включаючи винахід алгоритмів Train, Sapphire і Mississippi Delta, а так само GC stack maps, які дозволили реалізувати збирання сміття в статично-типізованих мовах на кшталт Java, C # і Go. Під його авторством були опубліковані документи про рантайми мов, управління пам'яттю, багатопоточність, синхронізацію, моделі пам'яті і транзакційну пам'ять. Зараз Рік є одним з членів команди Go в Google і працює над проблемами збирача сміття та рантайму.

В економіці існує поняття «замкнутого циклу», коли один процес посилює інший, що призводить до посилення першого. Історично, у світі комп'ютерів такий «замкнутий цикл» був між розвитком заліза і софту. Процесори ставали швидше, що сприяло написанню більш потужного софту, що, в свою чергу, стимулювало подальше підвищення швидкості процесорів і потужностей комп'ютера в цілому. Цей цикл працював приблизно до 2004-го, коли Закон Мура перестав працювати.

У наші дні дворазове збільшення кількості транзисторів не означає дворазове прискорення програм. Більше транзисторів = більше ядер процесора, але програми не еволюціонували настільки, щоб використовувати більше ядер. І саме через те, що сучасні програми не використовують на повну котушку багатоядерність, хлопці, які розробляють процесори, просто не додають більше ядер. Цикл заглух.

Довгострокова місія Go полягає в тому, щоб перезавантажити цей цикл, давши світу більше паралельних, конкурентних програм. Більш короткострокова місія - збільшити прийняття Go світовим співтовариством розробників. І одна з найбільших претензій до Go в даний момент - це занадто довгі паузи збирача сміття.

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

  • додати на комп'ютери відстеження очей, і запускати збирач сміття, коли ніхто не дивиться
  • показувати іконку очікування мережі під час фази складання сміття, і звинувачувати в паузах не збирач сміття, а затримки мережі або що-небудь ще

Але Расс Кокс, чомусь, ці ідеї завалив на корені, і вони вирішили закатати рукави і насправді поліпшити збирач сміття в Go. Вони прийшли до алгоритму, який зменшує паузи GC, але робить програму трішки повільнішою. Програми на Go стануть трохи повільнішими в обмін на гарантовано низькі паузи фаз складання сміття.

Які затримки ми можемо розрізняти?

  • Наносекунда: Грейс Хоппер порівняла час з дистанцією. Наносекунда - це 11.8 дюймів.
  • Мікросекунда: 5.4 мікросекунд це час, за який світло проходить 1 милю у вакуумі.
  • Мілісекунди:
    • 1: Послідовне читання 1МБ з SSD.
    • 20: Читання 1МБ з диска, що обертається.
    • 50: Поріг сприйняття (поріг реакції)
    • 50+: Різні затримки мережі
    • 300: Моргання ока

Так скільки роботи зі складання сміття ми можемо встигнути в одну мілісекунду?

Java GC проти Go GC

Go:

  • тисячі горутин
  • синхронізація через канали
  • рантайм написаний на Go, використовуючи його особливості, як і звичайний код
  • контроль просторової локальності (можна вбудовувати структури, внутрішні покажчики (& foo.field))

Java:

  • десятки потоків Java
  • синхронізація через об "єкти/локи
  • рантайм написаний на C
  • об'єкти пов'язані через індекси

Найбільшою відмінністю тут є питання просторової локальності. У Java все є індексом, у Go ж ви можете вбудовувати структури одна в іншу. Коли доводиться заходити за посиланнями на багато рівнів углиб, це може створювати масу проблем складальнику сміття.

Основи складальника сміття

Ось короткий приклад роботи збирача сміття. Зазвичай, вона складається з двох фаз:

  1. Фаза сканування: визначити, які речі в купі досяжні. Сюди входять покажчики в стеках, регістри, глобальні змінні, і далі, покажчики в купі.
  2. Фаза маркування: прохід за графом покажчиків. Позначити об'єкти, як досягнуті під час виконання програми. З точки зору збирача сміття, найпростіше «зупинити світ», щоб покажчики не змінювалися під час цієї фази взагалі. По-справжньому паралельний складальник сміття зробити дуже складно, тому що покажчики постійно змінюються. Програма використовує так звані «бар'єри на запис» (write barriers), щоб повідомляти складальнику сміття, що цей об'єкт не потрібно захоплювати. На практиці, правда, бар'єри на запис можуть давати результат навіть гірше, ніж «зупинка миру».

Складальник сміття в Go

Новий алгоритм збирача сміття Go використовує комбінацію бар'єрів на запис і коротких пауз для «зупинки миру». Ось його фази:

Ось як виглядала робота збирача сміття в Go 1.4:

А ось як вона виглядає в Go 1.5:

Зауважте, що паузи для «зупинки миру» набагато коротші. Під час роботи паралельного збирача сміття, він використовує 25% процесора.

Ось результати тестів:

У попередніх версіях Go, паузи збирача сміття були в загальному випадку набагато довшими і зростали в міру зростання розміру купи. У Go 1.5 паузи збирача сміття стали більш, ніж на порядок менше.

Якщо наблизити цифри, то видно, що все ще є слабка позитивна кореляція між розміром купи і тривалістю пауз GC. Але вони знають, в чому причина і вона буде усунена в Go 1.6.

Є невеликий штраф у продуктивності з новим алгоритмом, але цей штраф зменшується в міру того, як зростає обсяг купи.

Дивлячись у майбутнє

Повідомте людям, що паузи збирача сміття більше не є проблемою в Go. Дивлячись у майбутнє, вони планують затюнити збирач сміття для досягнення ще більш коротких пауз, більшої продуктивності і більшої передбачуваності. Вони хочуть знайти оптимальне співвідношення в цьому компромісі. Розробка Go 1.6 буде залежати від зворотного зв'язку, тому вони дуже чекають відгуків спільноти.

Новий збирач сміття зробить Go ще більш підходящою заміною для мов з ручним керуванням пам'яттю.

Посилання

Слайди: talks.golang.org/2015/go-gc.pdf

Додатково про складальника сміття в Go

Go 1.5 concurrent garbage collector pacing

Go 1.4+ Garbage Collection (GC) Plan and Roadmap