Как стать автором
Обновить

Поиск констант-«матрешек» для сокращения размера данных в программе

Время на прочтение3 мин
Количество просмотров1.9K

Речь пойдет о безымянных константах в программе, которые часто называют литералами. Если такой литерал нельзя использовать как непосредственный операнд в машинной команде, компилятору приходится выделять – а куда деваться! – этому литералу собственную память и далее оперировать адресом этой памяти.

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

Не поленитесь, проверьте, что, используемый Вами компилятор, например, в операторе типа:

put(’Hello, world’, ’Hello, world’); 

Точно использует один и тот же экземпляр константы ’Hello, world’ в памяти, а не два разных.

Вот мой пример целиком:

test:proc main;
put('Hello world','Hello world');
end test;

Видно, что в выполняемой программе, показываемой отладчиком, используется два раза одна и та же константа по адресу 40CB94

использование одного экземпляра литерала дважды
использование одного экземпляра литерала дважды

Я убежден в том, что почти все компиляторы всегда именно так и сделают, поскольку полезность проверки на строгое совпадение констант для сокращения данных в программе очевидна и такая проверка сравнительно дешева.

А вот следующую проверку, возможно, делает уже не всякий компилятор. Я имею в виду проверку на вхождение одной константы как подстроки в другую константу.

Например, добавив в первую константу примера символ «!» получаем формально две разных текстовых константы, но снова можно использовать лишь один экземпляр в памяти.

put(’Hello, world!’, ’Hello, world’); 

Отличия будут только в загружаемых длинах констант 0Ch и 0Bh.

Использование одного экземпляра литерала как двух вложенных с одним адресом
Использование одного экземпляра литерала как двух вложенных с одним адресом

Чуть интереснее добавление символа «!» еще и начало первой текстовой константы.

put(’!Hello, world!’, ’Hello, world’);
Использование одного экземпляра литерала как двух вложенных с разными адресами
Использование одного экземпляра литерала как двух вложенных с разными адресами

Опять-таки, можно использовать единственный экземпляр константы в памяти, однако, поскольку вхождение подстроки начинается не с первого байта, будут отличаться не только длины, но и адреса загрузок литералов на величину «пропущенных» байт при поиске вхождения подстроки. В примере это адреса 40CB94 и 40CB95.

Имеет ли поиск вложенных друг в друга констант-«матрешек» недостатки? Разумеется, имеет.

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

Конечно, нужные проверки могли бы быть выполнены уже на этапе редактирования связей, т.е. при сборке модулей в единую программу. Но тогда необходимо сохранять информацию о всех литералах во всех модулях и вообще строить последовательность редактирования связей по-другому. Поэтому в используемом мною редакторе связей все это не сделано примерно по тем же причинам, по которых герой к/ф «Берегись автомобиля» Юрий Деточкин отказался от использования автогена: это такая возня…

Второй недостаток проявляется, если обработка литералов идет в компиляторе на одном единственном «проходе», т.е. во время одного и того же чтения исходного текста.

Если по закону пакости компилятору в тексте программы сначала попадется более короткий литерал, а только затем более длинный, включающий в себя короткий как подстроку, то память для короткого уже выделена и «матрешка» из констант в памяти не получается. Но создавать еще один специальный проход компилятора для обработки литералов в таких неудачных случаях, как говорится, «жаба душит»: выигрыш может быть незначителен, а скорость компиляции уменьшается.

Тем не менее, из-за одного «прохода» компилятора по литералам, операторы, например, вида:

if s=’abc’ or s=’abcd’ then …
if s=’abcd’ or s=’abc’ then …

могут потребовать разное число байт для хранения литералов ’abcd’ и ’abc’.

Наконец третий недостаток может быть связан с выравниванием. Часто для повышения производительности процессора данные, в том числе и литералы, выравниваются в памяти, например, по восьмибайтным границам.

В случае константы-подстроки у нее уже может не получиться выравнивания. Правда, для текстовых литералов, особенно длинных, выравнивание не так существенно влияет на производительность, как, например, для данных типа double, всегда имеющих длину 8 байт.

Вывод:

Поиск констант-«матрешек» в программе при компиляции позволяет рациональнее использовать память для хранения таких констант. Отсутствие выравнивания, конечно, может отрицательно сказаться на скорости обращения к памяти при работе с такими константами. Но с другой стороны, в программе с литералами-«матрешками» будет больше обращений к памяти по одинаковым или близким адресам, что сделает работу кэш-памяти эффективнее.

Лично я за то, чтобы всегда искать не только строго совпадающие, но и вложенные литералы. Это дает ощутимый эффект. Например, в программе, которую я сопровождаю (и в которой много литералов) даже при всех указанных ограничениях размер данных сократился на 5600 байт только за счет найденных констант-«матрешек», при этом скорость компиляции практически не изменилась.

Теги:
Хабы:
Всего голосов 6: ↑5 и ↓1+7
Комментарии25

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань