<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://wiki.linuxformat.ru/wiki/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://wiki.linuxformat.ru/wiki/index.php?action=history&amp;feed=atom&amp;title=LXF107%3A%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%B2_Sun_Studio</id>
		<title>LXF107:Шаблоны в Sun Studio - История изменений</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.linuxformat.ru/wiki/index.php?action=history&amp;feed=atom&amp;title=LXF107%3A%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%B2_Sun_Studio"/>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/index.php?title=LXF107:%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%B2_Sun_Studio&amp;action=history"/>
		<updated>2026-05-13T07:33:21Z</updated>
		<subtitle>История изменений этой страницы в вики</subtitle>
		<generator>MediaWiki 1.19.20+dfsg-0+deb7u3</generator>

	<entry>
		<id>http://wiki.linuxformat.ru/wiki/index.php?title=LXF107:%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%B2_Sun_Studio&amp;diff=7753&amp;oldid=prev</id>
		<title>Yaleks: Новая: {{Цикл/Sun Studio}} == Идем на дело == : ''ЧАСТЬ 2 Мы покончили с теорией, и пришла пора взяться за дело – сегодня ...</title>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/index.php?title=LXF107:%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%B2_Sun_Studio&amp;diff=7753&amp;oldid=prev"/>
				<updated>2009-05-01T19:14:13Z</updated>
		
		<summary type="html">&lt;p&gt;Новая: {{Цикл/Sun Studio}} == Идем на дело == : &amp;#039;&amp;#039;ЧАСТЬ 2 Мы покончили с теорией, и пришла пора взяться за дело – сегодня ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Цикл/Sun Studio}}&lt;br /&gt;
== Идем на дело ==&lt;br /&gt;
: ''ЧАСТЬ 2 Мы покончили с теорией, и пришла пора взяться за дело – сегодня '''Станислав Механошин''' покажет, как использовать встраиваемые шаблоны Sun Studio в реальном проекте!''&lt;br /&gt;
&lt;br /&gt;
Недавно мне пришлось столкнуться с проектом, в котором&lt;br /&gt;
изрядную долю времени занимала многократно вызываемая&lt;br /&gt;
функция memset16, которая, как легко догадаться по названию, ведет себя аналогично стандартной функции memset, за исключением того, что записывает в память не одинаковые байты, а одинаковые&lt;br /&gt;
слова. Библиотечная memset хорошо известна компилятору и близка к&lt;br /&gt;
оптимальной, но с memset16 все оказалось не так легко.&lt;br /&gt;
&lt;br /&gt;
Мне требовалось не только написать оптимальный код для этой&lt;br /&gt;
функции, но и добиться ее встраивания по месту вызова, чтобы уменьшить накладные расходы на сам вызов, т.к. результаты профилировки&lt;br /&gt;
показали, что данная функция вызывается часто и обычно с небольшим&lt;br /&gt;
количеством данных. При подобной постановке задачи выбор определенно падает на встраиваемые шаблоны.&lt;br /&gt;
&lt;br /&gt;
Описывать функцию не надо, т.к. она уже используется приложением. Иными словами, мы имеем дело с прозрачной заменой определения&lt;br /&gt;
функции:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;extern uint16_t* memset16(uint16_t* s, int c, size_t len);&amp;lt;/source&amp;gt;&lt;br /&gt;
Итак, создадим ее шаблон:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;.inline memset16,0&lt;br /&gt;
;/ обнулить старшую часть регистра с символом-заполнителем&lt;br /&gt;
movzwq %si,%rsi&lt;br /&gt;
;/ получить 4 копии символа в rax&lt;br /&gt;
movq $0x0001000100010001,%rax&lt;br /&gt;
imulq %rsi,%rax&lt;br /&gt;
;/ запомнить указатель на массив для возврата&lt;br /&gt;
movq %rdi,%r8&lt;br /&gt;
;/ количество итераций...&lt;br /&gt;
movq %rdx,%rcx&lt;br /&gt;
;/ по 4 слова за итерацию&lt;br /&gt;
shrq $2,%rcx&lt;br /&gt;
;/ заполнение содержимым rax памяти по адресу rdi&lt;br /&gt;
repnz&lt;br /&gt;
stosq&lt;br /&gt;
;/ заполнить остаток, не кратный 4-м словам&lt;br /&gt;
movq %rdx,%rcx&lt;br /&gt;
andq $3,%rcx&lt;br /&gt;
repnz&lt;br /&gt;
stosw&lt;br /&gt;
;/ возвращаемое значение – адрес массива&lt;br /&gt;
movq %r8,%rax&lt;br /&gt;
.end&amp;lt;/source&amp;gt;&lt;br /&gt;
В данном случае я использую строковые функции для записи памяти, причем для повышения производительности первая часть функции&lt;br /&gt;
записывает по 8 байт за раз, и лишь не кратный восьми остаток пишет&lt;br /&gt;
в память словами.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что параметры принимаются соответственно в регистрах rdi, rsi и rdx, а возвращаемое значение помещается в регистр rax.&lt;br /&gt;
&lt;br /&gt;
Теперь требуется перекомпиляция всего приложения с добавлением&lt;br /&gt;
memory.il (имя файла с шаблоном) к, например, CFLAGS в Makefile.&lt;br /&gt;
&lt;br /&gt;
Использование такого шаблона в моем случае позволило улучшить&lt;br /&gt;
производительность приложения примерно на 15%. Однако при переходе с AMD на платформу Intel выяснилось, что использование записи по&lt;br /&gt;
8 байт за инструкцию – не самое выгодное решение. Поэтому для Intel-платформ была создана отдельная версия шаблона, более длинная, но&lt;br /&gt;
использующая запись по 16 байт за один раз:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;.inline memset16,0&lt;br /&gt;
;/ запомнить указатель на массив для возврата&lt;br /&gt;
movq %rdi,%r8&lt;br /&gt;
;/ проверка длины на ноль&lt;br /&gt;
testq %rdx,%rdx&lt;br /&gt;
;/ и выход, если ноль&lt;br /&gt;
je 9f&lt;br /&gt;
;/ обнулить старшую часть регистра с символом-заполнителем&lt;br /&gt;
movzwq %si,%rsi&lt;br /&gt;
;/ получить 4 копии символа в rsi&lt;br /&gt;
movq $0x0001000100010001,%rcx&lt;br /&gt;
imulq %rcx,%rsi&lt;br /&gt;
;/ если длина массива меньше 288&lt;br /&gt;
cmpq $288,%rdx&lt;br /&gt;
;/ перейти к заполнению строковыми инструкциями&lt;br /&gt;
jbe 5f&lt;br /&gt;
movq %rdi,%rcx&lt;br /&gt;
;/ проверка адреса массива на выравнивание на 16 байт&lt;br /&gt;
andq $15,%rcx&lt;br /&gt;
;/ переход к заполнению movdqa, если адрес выровнен&lt;br /&gt;
je 2f&lt;br /&gt;
shrq $1,%rcx&lt;br /&gt;
;/ если адрес нечетный, т.е. выравнивания не добиться&lt;br /&gt;
;/ перейти к заполнению строковыми инструкциями&lt;br /&gt;
jc 5f&lt;br /&gt;
;/ количество символов, которые необходимо записать&lt;br /&gt;
;/ до достижения выравнивания&lt;br /&gt;
negq %rcx&lt;br /&gt;
addq $8,%rcx&lt;br /&gt;
;/ и коррекция счетчика на эту величину&lt;br /&gt;
subq %rcx,%rdx&lt;br /&gt;
;/ символ-заполнитель в rax&lt;br /&gt;
movq %rsi,%rax&lt;br /&gt;
;/ и запись максимум 7 символов до достижения выравнивания&lt;br /&gt;
rep&lt;br /&gt;
stosw&lt;br /&gt;
;/ получаем 8 копий символа-заполнителя в xmm0&lt;br /&gt;
2: movdq %rsi,%xmm0&lt;br /&gt;
punpcklqdq %xmm0,%xmm0&lt;br /&gt;
;/ вычисление целого количества записей по&lt;br /&gt;
;/ 8 символов из xmm0, которые не переполнят массив&lt;br /&gt;
movq %rdx,%rcx&lt;br /&gt;
andq $0x38,%rcx&lt;br /&gt;
;/ и коррекция счетчика для проверки в конце цикла&lt;br /&gt;
subq %rcx,%rdx&lt;br /&gt;
;/ коррекция адреса массива с учетом смещения в следующем цикле&lt;br /&gt;
leaq -128(%rdi,%rcx,2),%rdi&lt;br /&gt;
;/ и вычисление адреса перехода внутрь цикла для&lt;br /&gt;
;/ получения количества итераций, кратного 8&lt;br /&gt;
shrq $3,%rcx&lt;br /&gt;
negq %rcx&lt;br /&gt;
leaq 1f(%rcx,%rcx,4),%rcx&lt;br /&gt;
;/ переход внутрь цикла&lt;br /&gt;
jmp *%rcx&lt;br /&gt;
;/ 1-байтная корректировка длины 1-го movdqa для корректности&lt;br /&gt;
перехода&lt;br /&gt;
nop&lt;br /&gt;
;/ 8 записей по 8 символов за инструкцию&lt;br /&gt;
3: movdqa %xmm0,(%rdi)&lt;br /&gt;
movdqa %xmm0,16(%rdi)&lt;br /&gt;
movdqa %xmm0,32(%rdi)&lt;br /&gt;
movdqa %xmm0,48(%rdi)&lt;br /&gt;
movdqa %xmm0,64(%rdi)&lt;br /&gt;
movdqa %xmm0,80(%rdi)&lt;br /&gt;
movdqa %xmm0,96(%rdi)&lt;br /&gt;
movdqa %xmm0,112(%rdi)&lt;br /&gt;
;/ корректировка указателя&lt;br /&gt;
1: addq $0x80,%rdi&lt;br /&gt;
;/ и счетчика символов&lt;br /&gt;
subq $0x40,%rdx&lt;br /&gt;
;/ и зацикливание, если он больше или равен нулю&lt;br /&gt;
jae 3b&lt;br /&gt;
;/ корректировка счетчика с учетом вычитания до цикла&lt;br /&gt;
4: addq $0x40,%rdx&lt;br /&gt;
;/ выход, если счетчик обнулился&lt;br /&gt;
je 9f&lt;br /&gt;
;/ запись остатка, не кратного 8 символам или не выровненного&lt;br /&gt;
5: movq %rsi,%rax&lt;br /&gt;
;/ количество итераций&lt;br /&gt;
movq %rdx,%rcx&lt;br /&gt;
;/ деленное на 4, т.к. Запись по 4 слова за раз&lt;br /&gt;
shrq $2,%rcx&lt;br /&gt;
;/ собственно запись в массив по 8 байт&lt;br /&gt;
rep&lt;br /&gt;
stosq&lt;br /&gt;
;/ остаток, не кратный 4-м словам&lt;br /&gt;
movq %rdx,%rcx&lt;br /&gt;
andq $3,%rcx&lt;br /&gt;
;/ запись по одному слову&lt;br /&gt;
rep&lt;br /&gt;
stosw&lt;br /&gt;
;/ адрес массива – возвращаемое значение функции&lt;br /&gt;
9: movq %r8,%rax&lt;br /&gt;
end&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь следует обратить внимание на использование меток. Хотя в&lt;br /&gt;
шаблоне и можно использовать обычные текстовые метки, этого лучше&lt;br /&gt;
не делать. Дело в том, что текст шаблона вставляется в получившийся ассемблер компилируемого модуля практически дословно. Если вы&lt;br /&gt;
используете шаблон только один раз в единице трансляции, ничего&lt;br /&gt;
страшного не случится; однако если вы вызовете такую функцию хотя&lt;br /&gt;
бы дважды, то получите сообщение о повторно определенных символах.&lt;br /&gt;
Здесь на помощь приходят локальные метки, для которых каждый раз&lt;br /&gt;
создается новый уникальный символ.&lt;br /&gt;
&lt;br /&gt;
Имена локальных меток – это цифры от 1 до 9, причем для адресации такой метки необходимо указать суффикс b или f, что означает&lt;br /&gt;
ссылку на ближайшую такую метку соответственно назад или вперед,&lt;br /&gt;
как в примере выше. Таким образом, хотя возможных имен локальных&lt;br /&gt;
меток всего девять, их можно определить практически неограниченное&lt;br /&gt;
количество.&lt;br /&gt;
&lt;br /&gt;
Небольшое замечание по использованию имен функций: имя, указанное в шаблоне, должно быть таким, как того ожидает компилятор, т.е.&lt;br /&gt;
со всеми декорациями, присущими языку.&lt;br /&gt;
&lt;br /&gt;
=== Оптимизация шаблонов ===&lt;br /&gt;
При использовании высоких уровней оптимизации, текст шаблона&lt;br /&gt;
совершенно не обязательно будет вставлен в программу «как есть».&lt;br /&gt;
Ассемблер шаблона так же участвует в платформенно-зависимых оптимизациях, осуществляемых кодогенератором, как и остальное тело&lt;br /&gt;
функции. В частности, регистры, через которые передаются параметры и возвращается вычисляемое значение, будут выбраны исходя из&lt;br /&gt;
того факта, чтобы они уже содержали нужные переменные к моменту&lt;br /&gt;
использования шаблона. Например, рассмотрим простой шаблон, складывающий два числа:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;.inline add_int,0&lt;br /&gt;
addl %esi,%edi&lt;br /&gt;
movl %edi,%eax&lt;br /&gt;
.end&amp;lt;/source&amp;gt;&lt;br /&gt;
и тестовую функцию, складывающую три числа:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;extern int add_int(int,int);&lt;br /&gt;
int sum(int x, int y, int z)&lt;br /&gt;
{&lt;br /&gt;
return x+add_int(y,z);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
После компиляции с использованием команды &lt;br /&gt;
 cc -fast -m64 -S add.c add.il &lt;br /&gt;
мы можем увидеть такой ассемблер:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;sum:&lt;br /&gt;
.CG1: subq $8,%rsp&lt;br /&gt;
/ ASM INLINE BEGIN: add_int&lt;br /&gt;
leal (%rsi,%rdx),%eax ;/ line : 5&lt;br /&gt;
/ ASM INLINE END&lt;br /&gt;
addl %edi,%eax ;/ line : 5&lt;br /&gt;
addq $8,%rsp ;/ line : 5&lt;br /&gt;
ret ;/ line : 5&amp;lt;/source&amp;gt;&lt;br /&gt;
{{Врезка&lt;br /&gt;
|Заголовок=Что дальше?&lt;br /&gt;
|Содержание=На этом наш экскурс во встраиваемые шаблоны&lt;br /&gt;
подходит к концу, однако возможности Sun&lt;br /&gt;
Studio им, разумеется, не исчерпываются. Есть&lt;br /&gt;
еще что-нибудь,&lt;br /&gt;
что вы хотели&lt;br /&gt;
бы узнать (но&lt;br /&gt;
боялись спросить)? Черкните&lt;br /&gt;
нам письмо&lt;br /&gt;
на [mailto:letters@linuxformat.ru letters@linuxformat.ru]&lt;br /&gt;
и сообщите об&lt;br /&gt;
этом!&lt;br /&gt;
|Ширина=150px}}&lt;br /&gt;
Обратите внимание, что параметры y и z функции sum расположены,&lt;br /&gt;
соответственно, в регистрах rsi и rdx, в то время как у функции add_int&lt;br /&gt;
они ожидаются в регистрах rdi и rsi. Однако мы можем видеть, что пересылок между регистрами в данном случае не происходит. Более того, не&lt;br /&gt;
оптимально описанные в шаблоне сложение и пересылка были превращены компилятором в одну инструкцию lea.&lt;br /&gt;
&lt;br /&gt;
К сожалению, из-за особенностей следования фаз оптимизации&lt;br /&gt;
операции со стеком не были удалены компилятором, как это было бы&lt;br /&gt;
сделано, используй мы просто сложение. Это показывает, что использование встроенных шаблонов все же накладывает некоторые ограничения&lt;br /&gt;
на оптимизацию функций, где они используются, хотя эти ограничения&lt;br /&gt;
и сведены к минимуму.&lt;br /&gt;
&lt;br /&gt;
Существует, однако, случай, когда шаблон будет использован в&lt;br /&gt;
месте вызова дословно, без изменений и оптимизаций. Это происходит, если транслятор ассемблера шаблона не понимает написанного в&lt;br /&gt;
нем. В этом случае его текст будет вставлен в результирующий ассемблер как есть, а параметры переданы строго в соответствии с ABI. Так,&lt;br /&gt;
например, все псевдооперации ассемблера не понимаются транслятором шаблонов. Например, вы можете захотеть вставить выравнивание&lt;br /&gt;
перед циклом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;.align 16&lt;br /&gt;
1:&lt;br /&gt;
;/ ...&lt;br /&gt;
decq %rdi&lt;br /&gt;
jne 1b&amp;lt;/source&amp;gt;&lt;br /&gt;
Это допустимо и может использоваться, однако .align не понимается&lt;br /&gt;
транслятором, т.к. эта директива известна лишь собственно программе-ассемблеру. В результате такой шаблон будет использован без оптимизации (что не означает, что вся функция, куда он будет встроен, не будет&lt;br /&gt;
оптимизирована).&lt;/div&gt;</summary>
		<author><name>Yaleks</name></author>	</entry>

	</feed>