← Архив: Common Lisp

[я опять бояню] sbcl, sse

Author: · 09.05.2010 18:15
· original author: allchemist

Разбавлю брутальное многостраничное обсуждение...

По интернетам ходят слухи, что sbcl поддерживает SSE* инструкции, кто-то говорит, что только на x86-64, кто-то - что везде. У меня под рукой пока только 32-битный атом, поэтому интересуюсь у общественности.
* Что нужно сделать, чтобы получить профит? Есть какие-то системные функции в дуже сдохшей sb-simd, или он сам компилит с поддержкой sse?
* Ткните плиз в код sbcl'а, где это реализуется. большая охота посмотреть, но во внутренностях sbcl секу не так сильно, как хотелось бы.
ЗЫ. Поздравляю общественность с днем Победы, а форум - с обновлением :)
· original author: dmitry_vk
Чтобы получить профит от SSE на x86-64, достаточно использовать операции с FPU (не знаю, насколько широко SSE2 используется, но используется - это факт). Дальше, некоторые библиотеки умеют получать сами получать профит от SSE2 (например, sb-cga (computational graphics algebra)), добавляя VOP'ы в SBCL.
Смотреть можно, например, в src/compiler/x86-64/float.lisp, там можно поискать по мнемоникам SSE2.
· original author: allchemist
sb-cga я смотрел довольно плотно. sse-примочки тамошние не запускаются (по той же причине, ибо кроме атома N270 ничего нет).Сам код кстати вызывает много вопросов. Например, почему там не используются кватернионы в четырехвекторном представлении, которые имеют 100500 преимуществ перед матрицами поворота, особенно на задачах 3d-графики?
Посмотрел sbcl-ный код и понял, что не смогу в нем разобраться (ну только за ооочень много времени), и с VOPы тоже имеют устрашающий вид, поэтому придется побыть в роли мартышки и ждать, пока разработчики сами все сделают :(
Кстати, в коде, имеющем отношение к массивам, иногда встречаются коменты типа "а вот здесь можно улучшить перформанс, но влом". :) так что есть надежда .
> достаточно использовать операции с FPU


А вот если я хочу сделать пресловутое матричное умножение, что я должен буду сделать, чтобы получить профит? (сорри за очень тупой, наверное, вопрос)
· original author: dmitry_vk
Ну да, SBCLный код немного страшен.
>А вот если я хочу сделать пресловутое матричное умножение, что я должен буду сделать, чтобы получить профит?
Мне кажется, что надо будет вручную реализовать VOPы для него.
Судя по всему, добавить свои VOPы не смертельно сложная задача. Когда компилятор компилирует формы вида (ИМЯ-ФУНКЦИИ Аргументы...), он определяет, как скомпилировать: либо в вызов функции, либо вставить VOP. У каждого VOPа указано:
  1. какую функцию он реализует (опция translate; а также для каких аргументов - с опцией :arg-types)
  2. где должны быть расположены аргументы и возвращаемое значение (опции :args, :results; аргументы VOPа располагаются в регистрах, а SBCL сам заботится о перезагрузке регистров)
  3. требования на свободные регистры для временных переменных (:temporary).
  4. время жизни аргументов и временных переменных (задается опциями для :args и :temporary)
  5. стоимость (опция :cost; учитывается, когда есть несколько VOP'ов для функции)
  6. процедура генерации машинного кода
· original author: allchemist
Почитал немного код и sbcl-internals
Вот, например, смотрю на fsqrt в src/compiler/x86-64/float.lisp
(define-vop (fsqrt)
  (:args (x :scs (double-reg)))
  (:results (y :scs (double-reg)))
  (:translate %sqrt)
  (:policy :fast-safe)
  (:arg-types double-float)
  (:result-types double-float)
  (:note "inline float arithmetic")
  (:vop-var vop)
  (:save-p :compute-only)
  (:generator 1
     (note-this-location vop :internal-error)
     (inst sqrtsd y x)
)
)

:args, :results -  не понятно что такое :scs? то есть вижу, что оно везде есть, но нигде не объяснено.
:translate - я правильно понимаю, что он делает функцию sb-c:%sqrt и ее надо объявить через defknown?
:vop-var, :save-p - совсем не понял
note-this-location - что-то для отладчика,
sqrtsd - SSE2-инструкция.
В целом, простой пример, более-менее ясно.
Да, кстати, что такое frob?
· original author: allchemist
dmitry_vk, я так понял, что вы разбираетесь в кишках sbcl, поэтому можно я вас помучаю?
Есть такая вкусняшка - vector-sap
(define-vop (vector-sap)
  (:translate vector-sap)
  (:policy :fast-safe)
  (:args (vector :scs (descriptor-reg) :target sap))
  (:results (sap :scs (sap-reg)))
  (:result-types system-area-pointer)
  (:generator 2
    (move sap vector)
    (inst add
          sap
          (- (* vector-data-offset n-word-bytes) other-pointer-lowtag)
)
)
)

Хочу определить array-sap по аналогии.
Каков сакральный смысл vector-data-offset, n-word-bytes и other-pointer-lowtag? откуда берутся эти значения?
· original author: dmitry_vk
Я только чуть-чуть разбираюсь, поэтому спрашивать можно, но точность ответа не гарантирована.
(тут в голову пришла мысль о том, что неплохо бы написать статью/пост об этом, но свободного времени мало, к сожалению)
В SBCL используется довольно общая терминология:
  1. storage base (SB)
  2. storage class (SC)
  3. temporary name (TN)

Для каждой архитектуры в SBCL определено несколько Storage base. Storage base - это место, где можно хранить данные. К storage base относятся: регистры CPU, регистры FPU, стек, окружение замыкания. Всего для x86 определены следующие SB: registers, float-registers, stack, constant, immediate-constant (см. src/compiler/x86/vm.lisp). Каждый storage base состоит из отдельных элементов - регистры, ячейки кадра стека. Элементы storage base разделены на классы (которые могут пересекаться). Классы соответствуют группам регистров: регистры общего назначения, регистры для double-float, регистры для single-float, регистры для симолов, регистры для байтов, слов, двойных слов.
В не-x86 портах SBCL среди всех классов регистров отдельно выделяется descriptor-reg - это те регистры, которые сканируются сборщиком мусора - в них лежат указатели на объекты, а в других регистрах указатели на объекты не могут располагаться. Но в x86/x86-64 descriptor-reg отдельно не выделяются (на x86 регистров очень мало, чтобы их разделять на классы, а на x86-64, видимо, по инерции). Т.е., для x86/x86-64 почти все классы регистров процессора совпадают, и можно использовать любой из них.
Когда компилятору необходимо сослаться на какое-то значение, используется структура TN. TN'ы создаются на первых этапах компиляции (при преобразовании исходника в форму IR1 - ICR (Implicit Continuation Representation)), а перед генерацией кода TN'ам назначаются конкретные элементы из Storage base (и вычисляется планирование (scheduling) регистров - когда какие регистры надо сохранить/перезагрузить). Все VOP'ы получают указание на расположение входных данных и результата в виде TN'ов.
VOP'ы ожидают, что значения находятся в определенных местах (потому что последовательности инструкций зависят от того, где расположены данные: в регистрах, на стеке, в FPU). Для этого у VOP'ов при указании аргументов и возвращаемого значения указывается опция :scs (множественное число от SC) - допустимые классы хранения. Дополнительно у VOP'ов указана стоимость (число - первый параметры для generator), которая отражает длительность операции. Эта стоимость используется при выборе размещения TN'ов по элементам SB для оптимизации программы (если для одной операции есть несколько разных VOP'ов).
Что такое :vop-var, точно сказать не могу.
>:translate - я правильно понимаю, что он делает функцию sb-c:%sqrt и ее надо объявить через defknown?
Да
>Да, кстати, что такое frob?
Какое-то жаргонное слово. Как я понимаю, нечто вроде foo, bar, baz и т.п. Нечто маленькое, часть другого большого, с чем удобно работать. http://www.xs4all.nl/~hwiegman/jargon/html/F/frob.html
>Хочу определить array-sap по аналогии.
Определения базовых типов находятся в src/compiler/generic/objdef.lisp и early-objdef.lisp.
vector-data-offset - это смещение поля data в объекте vector (константа сгенерирована макросом define-primitive-object). n-word-bytes - это число байтов в машинном слове (для x86 равно 4, для x86-64 равно 8; см. n-word-bytes определяется через n-word-bits в compiler/generic/early-vm.lisp, а n-word-bits определено в compiler/{x86,x86-64}/parms.lisp). other-pointer-lowtag - это "маленький тэг" для объектов-указателей. (см. http://lisper.ru/forum/messages/2342, http://sbcl-internals.cliki.net/tag%20bit). Тэги определены в compiler/generic/early-objdef.lisp
Для array это выглядит несколько сложнее, т.к. они могут ссылаться на другой array.
VOP для вектора делает следующее: убирает тэг с тэгированного указателя на вектор - тогда получается "чистый" указатель на вектор и прибавляет к нему смещение поля данных.
· original author: allchemist
спасибо за подробный ответ!но это надо переварить...
· original author: 1349
> Хочу определить array-sap по аналогии.
А зачем? :)
· original author: allchemist
> А зачем? :)
А я вот тоже подумал - а зачем, если можно сделать (sb-sys:vector-sap (sb-ext:array-storage-vector array)) ...
Сдернул матричное умножение из blas с помощью cffi - работает, но на маленьких матрицах (3x3) отстает от pure-lisp реализации, хотя на больших (>10x10) уходит в отрыв. Я так понимаю, что это за счет оверхеда cffi'шных функций foreign-alloc.
http://paste.lisp.org/display/99056
Все равно альтернативы sbcl нет, то почему бы не заюзать сразу sb-alien?
Делаю вот так:
http://paste.lisp.org/display/99055
В ответ оно выпадает в осадок с понтом "lisp process terminated, connection closed".


Где-то я неправ, а где - не могу понять.