Smalltalk по-русски
воскресенье, Сентябрь 21, 2008
Smalltalk и Все-Все-Все: Белка-Рыба наносит ответный удар
Лого движка SquirrelFish

Не успел я запостить статью об оптимизациях как в ST так и современных JavaScript-движках, как появилось дополнение: Apple выпустила SquirrelFish Exterme (SFX). Если SF был просто хорошим интерпретатором, то SFX продвинулся далеко вперёд.

И так, SFX использует:

  • Оптимизации в байткоде.
  • PIC.
  • JIT.
  • JIT для регулярных выражений.

JIT для регулярных выражений нас сейчас не интересует. Просто JIT - это понятно (Кстати, JIT в SFX не использует LLVM, возможно по соображениям скорости кодогенерации).

Что такое PIC мы уже рассмотрели. Хотя и тут есть один момент. В ST реализовать PIC относительно просто - ведь, не смотря на динамичность языка, существующие классы относительно стабильны и объекты принадлежат одному и тому же классу. В JS (и, тем более, в Self) же схема любого объекта может быть изменена на лету. Что равносильно в ST порождению новых классов. В V8 эту проблему решают введением скрытых классов на которые мапяться объекты с одинаковыми схемами. В SFX похоже используется аналогичная техника: каждый объект имеет некий StructureID. Объекты имеющие одну и ту же схему имеют один и тот же StructureID. Соответственно, диспетчер в PIC проверяет совпадение StructureID.

А вот оптимизации в байткоде мы еще не рассматривали. Эти оптимизации появились еще в оригинальном ST-80 (если не раньше, в ST-76) и призваны были соптимизировать диспетчеризацию сообщений наряду с глобальным кешем методов (что это - рассказано в предыдущей заметке). Эти оптимизации включают в себя "специальные селекторы" и "статические предсказания типов".

Спецселекторы это скорее не классическая оптимизация, а читерство. Суть оптимизации в инлайнинге базовых управляющих потоком выполнения структур (простите за косноязычее, но как сказать проще я не знаю). Т.е. ряд посылок сообщений компилируется не в обычную посылку сообщения, а сразу в байткод. Пример в Squeak. Вызов любого метода, например "true hash" компилируется в такой байткод:

  pushConstant: true
  send: hash
А "true ifTrue: [self hash]" компилируется в:
  pushConstant: true
  jumpFalse: 22
  self
  send: hash
Т.е. при этом не создаётся блок и используется не реальная посылка сообщения, а генерируется некий спецбайткод. Это приводит к ряду эффектов. Первый - это ускорение. Второй - если выполнить код "1 ifTrue: [self hash]", то вы получите не исключение "MessageNotUnderstood", а "NonBooleanReceiver". Третий эффект заключается в том, что если поменять имплементацию метода, например, в классе True, то на поведении программы это не скажется. Так же бесполезно добавлять метод "ifTrue:" в другие классы - поскольку сообщение такое не посылается, то и вызвать такой метод без рефлексии в обычном случае не получится. И, напоследок, вызвать такой спецметод можно всё таки не только через рефлексию. Реальная посылка сообщения генерируется и если компилятор не может проинлайнить блок кода. Например, "1 ifTrue: aBlock" генерирует:
  pushConstant: true
  pushTemp: 0
  send: ifTrue:
Отсюда мораль: менять такие спецметоды не стоит, во избежание всяческих чудес в поведении программы. Набор спецселекторов обычно влючает в себя "ifTrue:ifFalse", "on:do:", "timesRepeat:", "whileTrue:". Подробнее о спецселекторах можно прочитать в статье об устройстве компилятора в ST в разделе "Заинлайненный (почти) ifNil: (VW)".

Статические предсказания типов это уже именно оптимизация, а не грубый чит. Он основан на статистике, что большая доля сообщений таких как "+" имеет одинаковые классы, как у получателя так и аргумента. Соответсвенно, реализация таких методов - это примитив, который проверяет тип аргумента. Выглядит в Squeak это так:

SmallInteger>>+ aNumber 
  "Primitive. Add the receiver to the argument and answer with the result
  if it is a SmallInteger. Fail if the argument or the result is not a
  SmallInteger  Essential  No Lookup. See Object documentation  whatIsAPrimitive."

  <primitive: 1>
  ^ super + aNumber

Float>>+ aNumber 
  "Primitive. Answer the sum of the receiver and aNumber. Essential.
  Fail if the argument is not a Float. See Object documentation
  whatIsAPrimitive."

  <primitive: 41>
  ^ aNumber adaptToFloat: self andSend: #+

В ST традиционно, если примитив отрабатывает нормально, то возврата из него не происходит. Т.е. на ST код после примитива управление переходит только если примитив провалился.
Соответсвенно примитив отрабатывает быстро, если предсказание типа было успешным и медленне, если нет. Пример из Squeak:
Time millisecondsToRun: [10000000 timesRepeat: [1 + 1]]   "1)=>  697"
Time millisecondsToRun: [10000000 timesRepeat: [5.0 + 1]] "2)=> 2303"
Time millisecondsToRun: [10000000 timesRepeat: [1 + 5.0]] "3)=> 2408"
С первым случаем всё понятно - предсказание типов успешное и код отрабатывает почти в 4 раза быстрее, чем во 2-м и 3-м случаях. А вот почему 2-й вариант быстрее 3-го? Во втором случае трасса такая:
Float>>+
Number>>adaptToFloat:andSend:
Float>>+
В третьем же варианте, трасса больше на одну отправку сообщения:
SmallInteger>>+ 
Integer>>+
Float>>adaptToInteger:andSend:
Float>>+
В VW реализация кардинально другая, но разница в скорости там еще более заметна:
Time millisecondsToRun: [10000000 timesRepeat: [1 + 1]]   "1) =>  38"
Time millisecondsToRun: [10000000 timesRepeat: [5.0 + 1]] "2) => 120"
Time millisecondsToRun: [10000000 timesRepeat: [1 + 5.0]] "3) => 289"

Судя по короткому описанию именно подобие "статического предсказания типов" и реализовано в SFX.

Ярлыки:

Comments:
Белка-рыба название какое-то не-концептуальное, хоть и по-английски звучит хорошо. Как бы не получился проект такой попытка скрестить рыбу с белкой и белку с рыбой. Хотя если это Apple...

Посты оч интересны, хотя не во всем разбирался.
 
Так ты пиши, что не понятно. А я попробую уточнить. Потому что бывало, когда я иногда через полгода не мог понять, чегой там я был такого накорябал :)
 
Так что получается, "управляющие структуры", про которые в большинстве книжек о смоллтолке пишется, что их в смоллтолке нету, а вместо них якобы есть "обычные сообщения", на самом деле всё-таки не сообщения а всё-таки управляющие структуры?

Безобразие!
 
На низком уровне - ты прав, безобразие :) На более высоком, это всё таки не такие управляющие структуры как в других языках.

Во-первых, всё сделано в рамках синтаксиса отправки сообщения. Минус подхода - нельзя поменять имплементацию "управляющего метода". Но это маленький минус, ибо попытка поменять поведение базовой структуры может развалить всю систему. Гораздо больший минус - так как это не обычная посылка сообщения, то её нельзя перехватить (например в прокси). Это привело к тому, что в GLORP нельзя для объектов, которые могут быть прокси, использовать метод isNil (спецселектор). Так как прокси всегда есть, то этот метод всегда будет выдавать "false". Пришлось добавить особый метод isNIL, который можно перехватить. Плюс подхода - можно добавлять свои управляющие структуры, которые будут выглядеть и работать так же как и встроенные. Известный пример - "ifNil:". Кстати, в последнее время, когда машины стали быстрее, такие методы используются повсеместно, вместо более быстрой комбинации "... == nil ifTrue: [...".

Это конечно не приятно, но таких мест в ST гораздо меньше чем в других языках, и исправляются они меньшей кровью.

Во-вторых, это, всё таки, особенности реализации. Эту оптимизацию можно отключить (см. статью про компилятор). При чем, в рамках работы над AOStA в 2003 Элиот Миранда убрал спецселекторы из компилятора, ибо AOStA умеет сама инлайнить такие методы уже перед JIT. Отключение спецселекторов дало просадку производительности в 10% (думаю, когда машины были медленнее, то и эффект был больше).
 
Спасибо Андрей, очень интересные посты. Узнал для себя много нового.
 
Отправить комментарий

<< Home

Популярные статьи
:: Smalltalk?!
:: Почему Smalltalk?
:: Great Leap Forward from Java to Smalltalk

Последние сообщения
:: Smalltalk и Все-Все-Все
:: [Squeak] Новый сайт Squeakland
:: [Squeak] Squeak для iPhone
:: [Squeak] SqueakDBX
:: [Squeak] Monticello 2
:: [GST] GNU Smalltalk 3.0.4 release
:: MagLev - Gemstone for Ruby
:: [Squeak] JSqueak, Potato
:: [Squeak] WxSqueak 0.5
:: [STX] Smalltalk/X 5.4.1

Архив
Предыдущие новости / Декабрь 2004 / Январь 2005 / Февраль 2005 / Март 2005 / Апрель 2005 / Май 2005 / Июнь 2005 / Июль 2005 / Август 2005 / Сентябрь 2005 / Октябрь 2005 / Ноябрь 2005 / Декабрь 2005 / Январь 2006 / Февраль 2006 / Март 2006 / Апрель 2006 / Май 2006 / Июнь 2006 / Июль 2006 / Сентябрь 2006 / Октябрь 2006 / Ноябрь 2006 / Декабрь 2006 / Январь 2007 / Февраль 2007 / Март 2007 / Апрель 2007 / Май 2007 / Июнь 2007 / Август 2007 / Сентябрь 2007 / Ноябрь 2007 / Январь 2008 / Март 2008 / Май 2008 / Июнь 2008 / Июль 2008 / Август 2008 / Сентябрь 2008 / Октябрь 2008

Atom Feed
Smalltalk по-русски


Powered by Blogger