Хранение констант в памяти


#1

А зачем хранить константу в памяти?


Обсуждение книги Осипова А.В. «PascalABC.NET: Введение в современное программирование»
#2

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

А Вы где предлагает хранить константы, используемые в программе? В банковской ячейке или под матрасом? ))


#3

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


#4

А это смотря какой компилятор. В свое время сталкивался с ситуацией, когда портилась константа во всей программе (типа размера массива) из за перетирания пары байт в памяти.

Но речь не о том, Даже если компилятор вместо ссылки на константу подставляет ее значение - он куда его помещает? В откомпилированный код. Т.е. все равно в итоге в память.


#5

Если компилятор грамотный, то он понимает, что в программе используется значение константы, поэтому вместо имени константы сразу подставляет её значение. Сама константа в памяти не хранится, и программа о ней ничего не знает. Значение константы, естественно, используется, иначе компилятор вообще бы проигнорировал константу. Насколько я понимаю, не хранятся в программе и переменные, которые в программе не используются.

Признаюсь, что я не знаток компиляторов, поэтому выражаю исключительно своё мнение. Но надеюсь, что знатоки компиляторов не стали бы нагружать программу данными, которые не нужны для работы программы. Ведь программа должна не просто хранить значение константы в памяти, но и ссылаться на неё, то есть использовать константу как переменную.


#6

Давайте установим главное: константа - такая же часть программы, как и все прочее. Программа для выполнения загружается в память компьютера на отведенное ей загрузчиком место. значит и все константы, как бы она не выглядели, будут также загружены в память. В PascalАВС.NЕТ, к примеру, по умлочанию целочисленная константа имеет тип integer, т.е. константа 10 будет представлена 4 байтами 0A 00 00 00 (в персоналках данные представляются “шиворот навыворот” по отношению к обычному переводу числа 10 в двоичную систему). И эти 4 байта так или иначе, в составе программы займут свое место в памяти. Возможно даже, что неоднократно - я не знаю внутренней кухни .NET. И тогда, собственно, в чем же вопрос?


#7

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

То, что Вы пишете, справедливо для скриптовых ЯП. Паскаль компилирует исходный код в исполняемый (точнее в промежуточный). Зачем там хранить константы, я не знаю.

Будет время, посмотрю, как на IL выглядит программа с константами.


#8

Да. Каждой упоминание константы, к примеру, типа integer - заменяется на 1 команду IL у которой среди параметров сохраняется это значение.

И нет смысла пытаться сэкономить тут, хотя бы потому, что даже если кешировать значения констант (то есть 1 раз определять в заголовке сборки и потом ссылаться на них) - чтоб ссылаться тоже надо несколько байт на дескриптор. В теории можно хранить дескриптор в 7-битной кодировке, но всё это усложнение того не стоит.

А вот литеральные строки, на сколько я помню, в IL таки кешируются. Они то всегда >4 байт весят, а значит для них смысл в этом есть.

Ну и ещё, это всё касается IL. IL ещё раз компилируется, превращаясь в машинный код. Но, мне кажется в машинном коде это точно так же выглядит, только названия операторов другие.


#9

Однако, так не показывает признаков кеширования строк:

begin
  
  System.GC.KeepAlive('abc');
  System.GC.KeepAlive('def');
  System.GC.KeepAlive(10);
  
end.

(System.GC.KeepAlive не позволяет оптимизатору удалять ненужные объекты)

    // [1 1 - 1 6]
    IL_0000: nop          

    // [3 13 - 3 22]
    IL_0001: ldstr        "abc"
    IL_0006: call         void [mscorlib]System.GC::KeepAlive(object)
    IL_000b: nop          

    // [4 13 - 4 22]
    IL_000c: ldstr        "def"
    IL_0011: call         void [mscorlib]System.GC::KeepAlive(object)
    IL_0016: nop          

    // [5 13 - 5 22]
    IL_0017: ldc.i4.s     10 // 0x0a
    IL_0019: box          [mscorlib]System.Int32
    IL_001e: call         void [mscorlib]System.GC::KeepAlive(object)
    IL_0023: nop          

Но я проверил хекс-эдитором - если использовать 1 строку несколько раз, в готовом .exe её сохраняет только 1 раз, и явно в заголовке, среди других строк.


#10

Я знаю английский так же хорошо, как компиляторы (то есть ни фига), но здесь есть про хранение констант: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constants

Но в статье речь идёт о значимых типах. Строки было бы разумно хранить и ссылаться на них в коде. Считает ли компилятор число ссылок на строки, я не знаю.

Так что вещественные константы в памяти не хранятся, а ссылочные хз.


#11

При чём тут это? Я понимаю если бы вопрос был “кеширует ли компилятор строки”.

Но на него я вообще то уже ответил. В готовом .exe они таки хранятся только в заголовке файла, а IL код только ссылается на них. Вот только этим занимается не компилятор, а System.Reflection.Emit.

Если говорить про JIT компилятор - снова мимо, ссылки на строки во время выполнения считает CLR. JIT компилятор занимается только компиляцией.

Вещественные и значимые это не одно и то же.

Но, ближе к делу - ещё раз, чётко и ясно, то что уже несколько раз сказал @RAlex:
Весь код программы находится в оперативной памяти, когда выполняется
А значит и константы в нём тоже.

Далее, что написано по вашей ссылке:

when the compiler encounters a constant identifier in C# source code (for example, months), it substitutes the literal value directly into the intermediate language (IL) code that it produces.

Раз гугл переводчик понять так сложно - вот человеческий перевод:

когда компилятор видит имя константы в коде на C# - он записывает литеральное значение прямо в IL код, который в итоге генерируется.

Тут даже ничего не сказано про компилятор паскаля, но вообще он делает то же самое. И как я и сказал - значение константы записывается в IL коде. Вот строчка из IL кода, который я скинул выше:

    IL_0017: ldc.i4.s     10 // 0x0a

Более по-человечески:
Инструкция №0017: load constant | integer 4bytes | short form | 10
Инструкция №0017: Загрузить константу | целочисленную на 4 байта | короткая запись IL оператора | 10

10 тут это параметр, в .exe он хранится сразу после заголовка оператора.

Если будет несколько мест где используется константа - будет несколько IL операторов, у каждого из которых в параметрах указана эта константа.


В коде добавление такого оператора выглядит так:

uses System.Reflection.Emit;

begin
  var il: ILGenerator;
  il.Emit(OpCodes.Ldc_I4_S, 10);
end.

(запускаться, конечно, не будет, но главное анализатор кода работает)

Если уже пускаетесь в догадки о том как это работает - хотя бы посмотрите какие есть перегрузки у ILGenerator.Emit, какие значения есть в OpCodes и что о них сказано на msdn, чтоб эти догадки хотя бы на чём то базировались.


#12

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

К сожалению, всё обсуждение паскаля на форуме превратилось в бесполезную болталку. В итоге и паскаль заболтаете.

С дружеским приветом!


#13

Вот тут я что-то не пойму.

Или я дурак, или лыжи не едут

Вот есть, к примеру, в исходном коде вещественная константа 2.5. В соответствии со стандартом IEEE754 она записывается в шестнадцатиричном коде для представления double как 0x4004000000000000 и занимает в памяти 8 байт, будучи представлена либо константой, либо операндом в случае встраивания в некоторое выражение (в частном случае состоящее из самой константы - это непринципиально). Так откуда взялась мысль, что она в памяти не хранится? А где тогда?

– Мне говорят: не бери в голову! А куда? (М.Жванецкий)