Здравствуйте, Sinclair, Вы писали:
V>>Э нет, переменная — это "инструкция" высокоуровневого ЯП. Программу мы пишем не для времени компиляции, а именно для рантайма. И переменная — это инструкция управления/распределения памяти, где само фактическое распределение выполняется опираясь на типы значений, которые необходимо разместить в некоей памяти. И не надо пытаться строить такую невинность, что это всё далекие абстракции, которыми мы оперируем и не интересуемся реально происходящим. Дудки. Ты именно всегда непосредственно указываешь для переменной область памяти, в которой ее необходимо распределить: будь то глобальная область памяти, локальная или в области памяти, занимаемой объектом (т.е. в виде поля другого типа). Абтрагирование от конкретики происходит только на уровне типов, т.е. мы абстрагируемся лишь от размеров памяти, требуемых для значений (и как побочный эффект — защищаемся от неверной интерпретации этой памяти), больше ни от чего. Остальное мы указываем очень даже конкретно из расчета как нам конкретно надо, чтобы оно работало в рантайм. S>Это неконструктивная позиция. S>Потому, что вот я указал очень конкретно, где что размещать: S>
S>public void Test()
S>{
S> int i = 42;
S>
S>Где у нас i? В стеке? S>Как бы не так! Потому что дальше (через 100 строк) я пишу:
S>
Тогда, извините, ликбез. Замыкания бывают двух типов: by name и by value. Во втором случае, согласно определению, замыкание создает копии локальных переменных, входящих в замыкание. Обрати особое внимание на то, что те переменные, которые были локальны в исходном контексте, будут локальными и для замыкания, в отличие от глобальных или иных переменных. Но замыкание by name, по определению, создает shared значение, доступное по имени как из замыкания, так и из исходного контекста. Поэтому, приводя пример классического замыкания by name, ты лишь подтверждаешь исходный тезис.
Ты же сам включил целевую переменную именно в этот вид замыкания, это же не компилятор за тебя сделал. Коль ты указал ему "инструкцию" замыкания некоей переменной, он послушно ее разместит согласно определения поддерживаемого языком типа замыкания. Т.е. для твоего случая так, чтобы эти данные стали shared м/у контекстом замыкания и исходным контекстом. И поверь, он обеспечит эту семантику даже для случая, когда все прооптимизирует нафик и реально никакого отдельного shared-state не будет (например, в результате escape-анализа). Но т.к. заданная тобой семантика сохраняется, эти вещи для тебя прозрачны.
S>Семантика переменной и размещение переменной — штуки очень разные. Не надо их смешивать.
Покажи, почему разные? Я настаиваю, что семантика переменной чуть более чем полностью определяется ее размещением... ну кроме случая однопоточных программ, где семантика локальной и глобальной переменной в некоторых сценариях одинакова. Но это тоже известно и тоже используется, эффективности ради.
S>>>Я пытался, но ты смотришь не на язык, а на стек, в то время как переменная — понятие языка а не стека и реализации.
V>>Э нет, переменная — это "инструкция" высокоуровневого ЯП. Программу мы пишем не для времени компиляции, а именно для рантайма. И переменная — это инструкция управления/распределения памяти, где само фактическое распределение выполняется опираясь на типы значений, которые необходимо разместить в некоей памяти. И не надо пытаться строить такую невинность, что это всё далекие абстракции, которыми мы оперируем и не интересуемся реально происходящим. Дудки. Ты именно всегда непосредственно указываешь для переменной область памяти, в которой ее необходимо распределить: будь то глобальная область памяти, локальная или в области памяти, занимаемой объектом (т.е. в виде поля другого типа). Абтрагирование от конкретики происходит только на уровне типов, т.е. мы абстрагируемся лишь от размеров памяти, требуемых для значений (и как побочный эффект — защищаемся от неверной интерпретации этой памяти), больше ни от чего. Остальное мы указываем очень даже конкретно из расчета как нам конкретно надо, чтобы оно работало в рантайм. S>А тебя не смущает, что в рантайме может не оказаться того, что ты так старательно и конкретно указывал?
Ты имеешь ввиду суперкомпиляцию? Нет, не смущает. У меня на уровне ЯП есть некий инструментарий при работе с памятью: куча, стек, поля составных типов, глобальные (статические) переменные, где-то еще есть типизированные каналы, shared memory, mapped memory и TLS-переменные. Это суть инструмент, на языке которого я выражаю требуемую семантику происходящего. "Простым" способом все эти классы памяти не являются взаимозаменяемые, т.е. если я "просто" размещу переменную в другом классе памяти, то в общем случае изменится семантика программы. Но если компилятор способен проконтролировать все "входы и выходы", побочные эффекты и всякое такое, что в моей голове все-равно не уложится даже в случае средней по размеру программы, то я не против, чтобы он делал любые замены и подстановки, коль заданная мною семантика сохраняется.
Тебя же не смущает, например, что твоя программа, обращающаяся к неким девайсам, в случае работы в виртуалке обращается к их эмулированным моделям? Если тебе обеспечили исходную семантику работы с девайсом, то ничего с твоей программой не случится.
Здравствуйте, vdimas, Вы писали:
V>Покажи, почему разные? Я настаиваю, что семантика переменной чуть более чем полностью определяется ее размещением...
Не стоит настаивать на очевидных заблуждениях. Вы путаете причину и следствие: размещение переменной определяется её семантикой. Компилятор совершенно однозначно принимает решение, что где размещать, на основе информации, которую я ему дал.
Я внёс в программу маленькое изменение, которое вроде бы никак не повлияло на декларацию переменной. И компилятор в ответ послушно изменил её размещение — прозрачным для меня образом.
А ведь мог и не изменять — приведённый фрагмент полностью семантически эквивалентен вот этому:
public void Test()
{
int i = 42;
foreach(var r in Enumerable.Range(1, 5)) {i+= r};
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
V>>Покажи, почему разные? Я настаиваю, что семантика переменной чуть более чем полностью определяется ее размещением... S>Не стоит настаивать на очевидных заблуждениях. Вы путаете причину и следствие: размещение переменной определяется её семантикой. Компилятор совершенно однозначно принимает решение, что где размещать, на основе информации, которую я ему дал. S>Я внёс в программу маленькое изменение, которое вроде бы никак не повлияло на декларацию переменной. И компилятор в ответ послушно изменил её размещение — прозрачным для меня образом. S>А ведь мог и не изменять — приведённый фрагмент полностью семантически эквивалентен вот этому: S>
S>public void Test()
S>{
S> int i = 42;
S> foreach(var r in Enumerable.Range(1, 5)) {i+= r};
S>}
S>
Ну давай посмотрим, что изменилось. Если в прошлый раз переменная была доступна из двух локальных фреймов (в том, в котором объявлена, и в том, который замкнул ее by name), то теперь только из одного. Но она как была объявленной локальной переменной, с областью видимости внутри объявленного scope, так и осталась (в предыдущем примере область замыкания была подобластью той, где объявлена переменная). Теперь ближе к конкретике: для первого случая, вместо размещения переменной в локальном (т.е. принадлежащем одному потоку, т.е. однопоточному) фрейме компилятор разместил ее в куче, в некоем специально созданном shared (т.е. доступном из многих потоков) фрейме. Почему? Потому как низлежащая платформа не предоставляет встроенных ср-в, т.е. абстракций, для создания таких фреймов, компилятор их эмулирует на чем может. А почему вообще потребовалось создавать этот фрейм? Наверно от того, что переменная хранится по-значению. Если бы переменная оперировала объектом из кучи, никакого фрейма создавать не надо было, мы просто изменили бы семантику переменной включив ее в замыкание by name, а именно, дали бы доступ к значению объявленной локально переменной некоему "продолжению". Напомню, что изначально by name работало только с иммутабельными объектами и есть теорема, где для случая иммутабельных объектов замыкания by name и by value эквивалентны. Так зачем C# эмулирует локальный фрейм для замыканий даже для случая ссылочных объектов? Ах, ну да... ссылочные переменные у нас мутабельны, поэтому приходится расшаривать м/у локальными фреймами переменных не сам целевой объект, а именно ссылку на него, для сохранения исходной семантики мутабельной ссылки. Вот и всё, что происходит...
Ну и еще пройдусь по т.н. спекулятивным оптимизациям, когда переменная может "уйти" или местно ее размещения измениться, даже без указания расшаривания переменной в замыкании. Потому как вижу спекулирование на этих спекулятивных оптимизациях. Заблуждение тут простое: технологии классической компиляции, т.е. технологии compile-time, не в состоянии выяснить семантическую эквивалентность программы до спекулятивной оптимизации и после. Требуется т.н. спекулятивное, т.е. преждевременное, выполнение кода, что и есть техника суперкомпиляции. Это же совсем не то же самое что и обычная компиляция, хоть присутствует общий корень в слове. Для случая суперкомпиляции программа прогоняется в специальном режиме, т.е. в классическом рантайм и затем просто "пишется ответ". Обсуждать св-ва суперкомпиляции можно гораздо шире, чем только в плане переменных. Например, классический компилятор не вычисляет пользовательские ф-ии, даже если на них поданы значения, известные во время компиляции, а суперкомпилятор — запросто. Он просто может уничтожить целые куски кода, например убрав все эти ф-ии, которые использовал в результате прогона, а не то что изменить одну несчастную переменную. Однако, чтобы там ни происходило, все спекулятивные вычисления, вся эта эмуляция выполнения производятся исключительно по заданной нами семантике с гарантированным ее сохранением. Что и требуется от исправного компилятора.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, samius, Вы писали:
V>>>Да о всё о том же, что l-value это не само значение, а классическая ссылка на него. S>>l-value это все-таки место, а не ссылка. Сам же говорил, что в C нет ссылок
V>Ну так "место" — это и есть отсылка к значению, а не само значение. V>Да, говорил, насчет С, что ссылок нет, а ссылочная семантика есть.
V>Помнишь, что l-value бывает не только во время присвоения по известному адресу, т.е. при присвоении значения переменной, но и вычисляемое? Поэтому не так всё просто с "местом".
Какие проблемы с вычисляемым l-value?
V>>>Ну вот, ты уже согласился с тем, что ссылка — это тоже значение. ЧТД. Осталось ответить на вопрос, почему я могу иметь значение некоторого типа, а переменную этого типа создать не могу? S>>Давай все-таки разграничивать о каких значениях мы говорим. Формально значением ссылки является то, на что она ссылается. Ты же говоришь о неком значении, которое является служебной информацией ссылки. Да, ты можешь создать переменную, которая будет содержать служебную информацию ссылки. Но значением ссылки будет не эта информация, а та, на которую она ссылается посредством этой служебной информации.
V>Это будет "значением по ссылке", именно так его принято называть.
Первый раз слышу.
V>>>Ну коль язык это инструмент, то мне таки надо знать, что же этот инструмент делает. S>>А делать инструмент может нечто значительно отличающееся от языковых терминов. Ты можешь находить некоторые соответствия, но делать по ним выводы о сущностях уровня языка в общем случае неверно.
V>Не может, иначе инструмент станет негоден. Он должен что-то делать согласно спецификации. Причем, сама спецификация, в свою очередь, построена на основе общепринятой терминологии, а не какой-то специальной-абстрактной.
Я не понимаю, на чем ты настаиваешь? Ты же сам говоришь, что переменной в рантайме может не быть. Где в спецификации написано что в рантайме должны быть переменные и что они должны соответствовать языковым переменным?
V>>>Да это не принципиально. Этот прием даже в бусте пользуют, когда надо хранить/копировать/передавать именно ссылку, а не указуемое значение. Главное здесь что — мы оперируем ссылкой, ссылающейся на всё тот же целевой объект, т.е. сохраняем единственно требуемую от ссылки семантику. Остальное никого не интересует. S>>Мы не оперируем ссылкой, мы создаем новую ссылку каждый раз.
V>Именно, только это от ссылки и требуется — при копировании их, не копировать целевое значение. Больше никакой семантической нагрузки они не несут.
Я говорю о том, что значением переменной мы можем оперировать, а ты куда-то уводишь.
V>>>Разве? С каких пор поле объекта перестало быть разновидностью переменной? S>>с тех пор, когда поле объекта стало ссылкой.
V>А что в этом случае страшное произошло? Память не из адреса объекта под ссылочную переменную выделилась или что-то еще?
Ничего такого не произошло, то же что и с локальной ссылкой.
S>>Я уже писал, что ссылка ссылается на место. Это место может быть переменной. А может быть местом в куче.
V>Это ты первый раз в этом посте написал, до этого настаивал исключительно на переменной.
Лень искать, но я писал уже об этом. V>Так по-немногу и доберемся до цели.
S>>Ты опять применяешь термин языка к рантайму. Представь что мы где-то в дебрях рекурсии без хвостовой оптимизации (пусть факториала). Стек кончился. Чем память забита? Да хрен, знает, а живая переменная лишь одна!
V>Если в стеке нет временных переменных, а только адреса возвратов, то сложно не переделать в хвостовую рекурсию... А если там параметры — то они суть классические локальные переменные. "Язык рантайма" тут не при чем.
Э не, ты не путай. Локальная переменная она одна! А чего-там в стеке понапихано — большой вопрос.
V>>>Ну и как можно оперировать константным адресом, с которым я сравнил ссылку? Распечатать его числовое представление для целей отладки? S>>И это тоже. А еще можем взять адрес адреса и куда-нибудь передать.
V>Внимание, вопрос! Зачем это надо для адреса, который константа? (с которым сравнили ссылку) V>Серьезно, аж интересно стало.
Ну так это дело десятое. Серьезно — лень выдумывать. Важно что адрес адреса взять можем, а адрес ссылки — нет.
V>>>ldloc грузит само значение в стек, результатом будет копия значения ссылки на стеке. S>>Значение ссылки — это то место, на которое она ссылается.
V>Это на ЯВУ, а в опкодах CLI — это адрес. После прочтения значения ссылки надо делать ldobj (или как там его)...
Вот видишь в опкодах оказывается и ссылки-то нет, а есть адрес. А ты пытаешься еще какую-то параллель проводить между рантаймом и ЯВУ...
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, samius, Вы писали:
S>>А тебя не смущает, что в рантайме может не оказаться того, что ты так старательно и конкретно указывал?
V>Ты имеешь ввиду суперкомпиляцию? Нет, не смущает. У меня на уровне ЯП есть некий инструментарий при работе с памятью: куча, стек, поля составных типов, глобальные (статические) переменные, где-то еще есть типизированные каналы, shared memory, mapped memory и TLS-переменные. Это суть инструмент, на языке которого я выражаю требуемую семантику происходящего. "Простым" способом все эти классы памяти не являются взаимозаменяемые, т.е. если я "просто" размещу переменную в другом классе памяти, то в общем случае изменится семантика программы. Но если компилятор способен проконтролировать все "входы и выходы", побочные эффекты и всякое такое, что в моей голове все-равно не уложится даже в случае средней по размеру программы, то я не против, чтобы он делал любые замены и подстановки, коль заданная мною семантика сохраняется.
Ну т.е. ты признаешь что между рантаймом и ЯВУ параллели может не быть, тем не менее настаиваешь на том, что бы именно по рантайму делать выводы о сущностях ЯВУ?
V>Тебя же не смущает, например, что твоя программа, обращающаяся к неким девайсам, в случае работы в виртуалке обращается к их эмулированным моделям? Если тебе обеспечили исходную семантику работы с девайсом, то ничего с твоей программой не случится.
Так это аргумент против твоих доводов.
Вообще говоря, программа не работает с девайсом. Она работает по большому счету с командами. Даже если в программе есть высокоуровневая абстракция девайса, то эта абстракция лишь в голове разработчика. Ни в рантайме, ни в ЯВУ никакого девайса нет, есть порт и функции чтения/записи данных.
Здравствуйте, samius, Вы писали:
V>>Помнишь, что l-value бывает не только во время присвоения по известному адресу, т.е. при присвоении значения переменной, но и вычисляемое? Поэтому не так всё просто с "местом". S>Какие проблемы с вычисляемым l-value?
Противоречило предыдущим твоим рассуждениям относительно ссылок на переменные.
V>>А что в этом случае страшное произошло? Память не из адреса объекта под ссылочную переменную выделилась или что-то еще? S>Ничего такого не произошло, то же что и с локальной ссылкой.
Не понял ответа. Локальная ссылка на локальную же переменную является константой времени компиляции, как это можно сравнивать с иммутабельным объектом времени исполнения?
V>>Если в стеке нет временных переменных, а только адреса возвратов, то сложно не переделать в хвостовую рекурсию... А если там параметры — то они суть классические локальные переменные. "Язык рантайма" тут не при чем. S>Э не, ты не путай. Локальная переменная она одна! А чего-там в стеке понапихано — большой вопрос.
Нет, не одна, иначе она бы назвалась не локальной, а глобальной. А локальных переменных ровно столько, сколько раз вызвали процедуру, в которой они объявлены. По определению локальной переменной.
V>>Внимание, вопрос! Зачем это надо для адреса, который константа? (с которым сравнили ссылку) V>>Серьезно, аж интересно стало. S>Ну так это дело десятое. Серьезно — лень выдумывать. Важно что адрес адреса взять можем, а адрес ссылки — нет.
Дык, вопрос и стоит, почему для случая указателяконстанты это важно? Нецжели не понял намека на то, что у обычного указателя нам адрес нужен только для мутации значения указателя, например, для передачи как out-параметра в процедуру. Но это не работает для указателя-константы, так что вопрос в силе.
V>>>>ldloc грузит само значение в стек, результатом будет копия значения ссылки на стеке. S>>>Значение ссылки — это то место, на которое она ссылается.
V>>Это на ЯВУ, а в опкодах CLI — это адрес. После прочтения значения ссылки надо делать ldobj (или как там его)... S>Вот видишь в опкодах оказывается и ссылки-то нет, а есть адрес. А ты пытаешься еще какую-то параллель проводить между рантаймом и ЯВУ...
Что я вижу? В опкодах она ссылкой и называется, просто можно взять ее адрес в опкодах, в отличие от. Язык C# позволяет взять адрес только ссылочных переменных, ссылающиеся на managed-объекты в куче (1), но не позволяет брать адреса ссылок на другие типы объектов (2). Например, не позволяет брать адрес ссылки на ссылку типа (1). Это ограничение семантики языка, и я их понимаю... бо и так уже запутанно выходит. Ведь ссылка на объект ref-type в куче во всех сценариях ведет себя как типичный value-type. Одна эта фраза ставит новичков в глубокий ступор... а C# позиционировался малость иначе...
Здравствуйте, vdimas, Вы писали:
V>Однако, чтобы там ни происходило, все спекулятивные вычисления, вся эта эмуляция выполнения производятся исключительно по заданной нами семантике с гарантированным ее сохранением. Что и требуется от исправного компилятора.
Ну вот видите — стоило сделать небольшое умственное усилие, и вам удалось признать мою правоту.
Первична именно семантика переменной, определённая в терминах того языка, на котором эта переменная описывается.
А какие там ухищрения нужно делать на нижележащей платформе, чтобы эту семантику обеспечить — дело десятое. В том числе и размещение переменной. Сегодня компилятор шарпа выделяет closure variables в поля объектов, размещённых в куче. Завтра прикрутят escape-анализ — и он будет оставлять переменную в примерах типа того, который я привел, на стеке.
Или вообще переменную устранит.
Всё это — вторично по отношению к программе.
Когда мы говорим о семантике целых чисел в С++, мы не интересуемся тем, как там должен использоваться бит переноса в регистре флагов. Когда мы говорим о семантике переменных, мы не интересуемся тем, где именно во flat-модели памяти расположена эта переменная.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, samius, Вы писали:
V>>>Помнишь, что l-value бывает не только во время присвоения по известному адресу, т.е. при присвоении значения переменной, но и вычисляемое? Поэтому не так всё просто с "местом". S>>Какие проблемы с вычисляемым l-value?
V>Противоречило предыдущим твоим рассуждениям относительно ссылок на переменные.
Я писал, что ссылки бывают не только на переменные.
V>>>А что в этом случае страшное произошло? Память не из адреса объекта под ссылочную переменную выделилась или что-то еще? S>>Ничего такого не произошло, то же что и с локальной ссылкой.
V>Не понял ответа. Локальная ссылка на локальную же переменную является константой времени компиляции, как это можно сравнивать с иммутабельным объектом времени исполнения?
А локальная ссылка на что-нибудь в куче не является константой времени компиляции.
V>>>Если в стеке нет временных переменных, а только адреса возвратов, то сложно не переделать в хвостовую рекурсию... А если там параметры — то они суть классические локальные переменные. "Язык рантайма" тут не при чем. S>>Э не, ты не путай. Локальная переменная она одна! А чего-там в стеке понапихано — большой вопрос.
V>Нет, не одна, иначе она бы назвалась не локальной, а глобальной. А локальных переменных ровно столько, сколько раз вызвали процедуру, в которой они объявлены. По определению локальной переменной.
storage location-ов столько, сколько раз вызвали процедуру. Причем обратиться процедура может только к одному из них — на текущем фрейме. А переменная — она одна и в ЯВУ.
V>>>Внимание, вопрос! Зачем это надо для адреса, который константа? (с которым сравнили ссылку) V>>>Серьезно, аж интересно стало. S>>Ну так это дело десятое. Серьезно — лень выдумывать. Важно что адрес адреса взять можем, а адрес ссылки — нет.
V>Дык, вопрос и стоит, почему для случая указателяконстанты это важно? Нецжели не понял намека на то, что у обычного указателя нам адрес нужен только для мутации значения указателя, например, для передачи как out-параметра в процедуру. Но это не работает для указателя-константы, так что вопрос в силе.
Вопрос "какой смысл" не имеет отношения к тому, что можно взять адрес указателяконстанты. Мы можем свдинуть int влево на 10000 позиций. Смысла в этом немного, но ведь можем!
V>>>Это на ЯВУ, а в опкодах CLI — это адрес. После прочтения значения ссылки надо делать ldobj (или как там его)... S>>Вот видишь в опкодах оказывается и ссылки-то нет, а есть адрес. А ты пытаешься еще какую-то параллель проводить между рантаймом и ЯВУ...
V>Что я вижу? В опкодах она ссылкой и называется, просто можно взять ее адрес в опкодах, в отличие от. Язык C# позволяет взять адрес только ссылочных переменных, ссылающиеся на managed-объекты в куче (1), но не позволяет брать адреса ссылок на другие типы объектов (2).
Язык C# не позволяет брать адрес ссылочных переменных, ссылающихся на managed-объекты в куче. Передача ссылки по ref-у это не взятие адреса ссылки. V>Например, не позволяет брать адрес ссылки на ссылку типа (1). Это ограничение семантики языка, и я их понимаю... бо и так уже запутанно выходит. Ведь ссылка на объект ref-type в куче во всех сценариях ведет себя как типичный value-type. Одна эта фраза ставит новичков в глубокий ступор... а C# позиционировался малость иначе...
Ссылка на объект ref-type в куче далеко не во всех сценариях ведет себя как типичный value-type. Например, мы не можем взять ее адрес Здесь она себя ведет как нетипичный value-type, содержащий ссылку в качестве поля. Его адрес тоже нельзя взять.
Здравствуйте, Sinclair, Вы писали:
V>>Однако, чтобы там ни происходило, все спекулятивные вычисления, вся эта эмуляция выполнения производятся исключительно по заданной нами семантике с гарантированным ее сохранением. Что и требуется от исправного компилятора. S>Ну вот видите — стоило сделать небольшое умственное усилие, и вам удалось признать мою правоту. S>Первична именно семантика переменной, определённая в терминах того языка, на котором эта переменная описывается. S>А какие там ухищрения нужно делать на нижележащей платформе, чтобы эту семантику обеспечить — дело десятое. В том числе и размещение переменной. Сегодня компилятор шарпа выделяет closure variables в поля объектов, размещённых в куче. Завтра прикрутят escape-анализ — и он будет оставлять переменную в примерах типа того, который я привел, на стеке. S>Или вообще переменную устранит.
Оптимизация на основе escape-анализа — это техника суперкомпиляции, т.е. ближе к технологии рантайм по сути происходящего, бо происходит эмулирование работы программы. Бывает же оптимизациями по показаниям профайлера, например, — тоже лишь техникой compile-time эта задача нерешабельна.
S>Всё это — вторично по отношению к программе.
Почему? Ну, проэмулировал шарп семантику замыкания by value через эмуляцию фрейма в виде объекта на куче, какие проблемы если он сделал это корректно? Мы ведь уже договорились недавно, что даже фреймы под локальные переменные не обязаны жить в "родном" стеке процессора, нас вовсе не подробности реализации фреймов интересуют. Зато интересует факт, что содержимое каждого фрейма должен быть видно лишь одному потоку исполнения (до тех пор, пока некую переменную не заберут в замыкание by name).
S>Когда мы говорим о семантике целых чисел в С++, мы не интересуемся тем, как там должен использоваться бит переноса в регистре флагов. Когда мы говорим о семантике переменных, мы не интересуемся тем, где именно во flat-модели памяти расположена эта переменная.
Ну да, конкретный адрес не важен, а вот обеспечение видимости/локальности, уникальности экземпляра и т.д. — важно, т.е. важно обеспечение неких характеристик, присущим некоему memory storage class, именно оно и составляет семантику (о чем и говорил выше по ветке). Какое-никакое представление/модель происходящего в случае каждого storage class в голове держать надо во время написания программы. Как пример — отличие статического поля от экземплярного поля объекта. Как ни крути, а понимать в чем отличие — придется, даже пусть каждый понимает это отличие по-своему (существует как минимум 2 модели реализации статических полей, в случае их размещения по известным адресам в compile-time или динамически в runtime).
V>>Ты имеешь ввиду суперкомпиляцию? Нет, не смущает. У меня на уровне ЯП есть некий инструментарий при работе с памятью: куча, стек, поля составных типов, глобальные (статические) переменные, где-то еще есть типизированные каналы, shared memory, mapped memory и TLS-переменные. Это суть инструмент, на языке которого я выражаю требуемую семантику происходящего. "Простым" способом все эти классы памяти не являются взаимозаменяемые, т.е. если я "просто" размещу переменную в другом классе памяти, то в общем случае изменится семантика программы. Но если компилятор способен проконтролировать все "входы и выходы", побочные эффекты и всякое такое, что в моей голове все-равно не уложится даже в случае средней по размеру программы, то я не против, чтобы он делал любые замены и подстановки, коль заданная мною семантика сохраняется. S>Ну т.е. ты признаешь что между рантаймом и ЯВУ параллели может не быть, тем не менее настаиваешь на том, что бы именно по рантайму делать выводы о сущностях ЯВУ?
Здравствуйте, vdimas, Вы писали:
V>Оптимизация на основе escape-анализа — это техника суперкомпиляции, т.е. ближе к технологии рантайм по сути происходящего, бо происходит эмулирование работы программы. Бывает же оптимизациями по показаниям профайлера, например, — тоже лишь техникой compile-time эта задача нерешабельна.
Это неважно. Дотнет — среда управляемая, может и в рантайме оптимизировать.
S>>Всё это — вторично по отношению к программе.
V>Почему? Ну, проэмулировал шарп семантику замыкания by value через эмуляцию фрейма в виде объекта на куче, какие проблемы если он сделал это корректно? Мы ведь уже договорились недавно, что даже фреймы под локальные переменные не обязаны жить в "родном" стеке процессора, нас вовсе не подробности реализации фреймов интересуют. Зато интересует факт, что содержимое каждого фрейма должен быть видно лишь одному потоку исполнения (до тех пор, пока некую переменную не заберут в замыкание by name).
Вы опять путаете семантику программы и детали её реализации.
V>Ну да, конкретный адрес не важен, а вот обеспечение видимости/локальности, уникальности экземпляра и т.д. — важно, т.е. важно обеспечение неких характеристик, присущим некоему memory storage class, именно оно и составляет семантику (о чем и говорил выше по ветке).
Зачем вы вводите этот memory storage class, если он не имеет отношения к "настоящей" памяти? И в спецификации языка он тоже не встречается.
V> Какое-никакое представление/модель происходящего в случае каждого storage class в голове держать надо во время написания программы. Как пример — отличие статического поля от экземплярного поля объекта. Как ни крути, а понимать в чем отличие — придется, даже пусть каждый понимает это отличие по-своему (существует как минимум 2 модели реализации статических полей, в случае их размещения по известным адресам в compile-time или динамически в runtime).
В это понимание совершенно не обязаны входить заблуждения типа "статическое поле — это прямой адрес, а экземплярное — это адрес + смещение". Семантика полей в дотнете такова, что статическое поле хранит ровно одно значение в пределах домена, а экземплярное — по значению на каждый экземпляр. Всё. Каким конкретно образом обеспечивается доступ к этим полям — знать необязательно. А зачастую ещё и противопоказано, т.к. провоцирует на написание implementation-dependent кода.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>В это понимание совершенно не обязаны входить заблуждения типа "статическое поле — это прямой адрес, а экземплярное — это адрес + смещение". Семантика полей в дотнете такова, что статическое поле хранит ровно одно значение в пределах домена, а экземплярное — по значению на каждый экземпляр. Всё. Каким конкретно образом обеспечивается доступ к этим полям — знать необязательно. А зачастую ещё и противопоказано, т.к. провоцирует на написание implementation-dependent кода.
Извини, коллега, но ты как с другой вселенной. Я прекрасно понимаю, о чем ты, но не верится, что об этом можно вот так всерьез на весь интернет. Особенно когда постоянно приходится работать над быстродействием дотнетных программ. Я понятия не имею, чем вы там на работе занимаетесь, но сколько видел или участвовал в проектах... так вот, "абстрактные" программисты, не понимающие суть происходящего, для более-менее серьезных проектов банально профнепригодны. Не нужны.
Здравствуйте, vdimas, Вы писали: V>Извини, коллега, но ты как с другой вселенной. Я прекрасно понимаю, о чем ты, но не верится, что об этом можно вот так всерьез на весь интернет. Особенно когда постоянно приходится работать над быстродействием дотнетных программ. Я понятия не имею, чем вы там на работе занимаетесь, но сколько видел или участвовал в проектах... так вот, "абстрактные" программисты, не понимающие суть происходящего, для более-менее серьезных проектов банально профнепригодны. Не нужны.
У нас с вами разное понимание термина "суть".
Для вас — это то, что вы изучили в начале своей "профильной" карьеры. Поэтому все современные абстракции вы пытаетесь сначала перевести на уровень воображаемого вами процессора x86.
Для меня — это семантика, и умение свободно переходить от одного уровня абстракции к другому и обратно.
Моё понимание устройства программы делится на чётко выраженные уровни. Вот у нас уровень архитектуры приложения; вот у нас конструкции ЯП, которые эту архитектуру воплощают; вот MSIL, в который превращаются эти конструкции; вот x86, в который JIT-тится MSIL; вот микрокод, в котором работает этот x86, и особенности реальной архитектуры современных многоядерных процессоров.
Всё это — отдельно.
Благодаря этой отдельности я могу следить, на чём основаны те или иные предположения, и будут ли они оставаться справедливыми при замене того или иного уровня абстракции.
Я насмотрелся (да и сам таким был) на людей, которые сращивают все эти слои в голове в монолит. Им крайне некомфортно переходить на другие платформы, т.к. они не понимают границы применимости предположений.
Мир ведь не исчерпывается только дотнетом на x86. Есть ещё веб-программирование с его стеком AJAX-технологий; там очень многое нужно делать совсем не так, как представляется правильным в дотнетной настольной программе.
Есть, скажем, базы данных, где зазор между SQL и его физическим планом зачастую значительно сильнее, чем между C++ программой и её скомпилированным представлением.
И умение переходить от одной СУБД к другой, одновременно представляя себе последствия для производительности, невозможно получить при монолитном представлении о "сути" SQL.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, Sinclair, Вы писали:
S>>В это понимание совершенно не обязаны входить заблуждения типа "статическое поле — это прямой адрес, а экземплярное — это адрес + смещение". Семантика полей в дотнете такова, что статическое поле хранит ровно одно значение в пределах домена, а экземплярное — по значению на каждый экземпляр. Всё. Каким конкретно образом обеспечивается доступ к этим полям — знать необязательно. А зачастую ещё и противопоказано, т.к. провоцирует на написание implementation-dependent кода.
V>Извини, коллега, но ты как с другой вселенной. Я прекрасно понимаю, о чем ты, но не верится, что об этом можно вот так всерьез на весь интернет. Особенно когда постоянно приходится работать над быстродействием дотнетных программ. Я понятия не имею, чем вы там на работе занимаетесь, но сколько видел или участвовал в проектах... так вот, "абстрактные" программисты, не понимающие суть происходящего, для более-менее серьезных проектов банально профнепригодны. Не нужны.
"Две основные формы вербального синкретизма — синкретизм рассуждения, проявляющийся во взаимном искажении высказываний, рассматриваемых вместе, и синкретизм понимания, который заключается в том, что понимание текста или фразы начинается не с анализа деталей, а с создания схемы целого, дающей смысл отдельным словам."
Здравствуйте, Sinclair, Вы писали:
S>Для вас — это то, что вы изучили в начале своей "профильной" карьеры. Поэтому все современные абстракции вы пытаетесь сначала перевести на уровень воображаемого вами процессора x86.
Ох, знал бы ты, для скольких я архитектур на асме писал многие годы... Для x86 — меньше всего, если брать именно в асме или низкоуровневые/встраиваемые.
S>Для меня — это семантика, и умение свободно переходить от одного уровня абстракции к другому и обратно.
И какие проблемы? На некоторых архитектурах привычные тебе вещи вообще делаются задом наперед и с очень многими ограничениями. В этих случаях тем более надо понимать, что происходит, чтобы лажи не напороть.
Абстракция позволяет абстрагироваться от подробностей, согласен. Но если ты знаешь, например, что замыкание создаст объект-локальный-фрейм в куче, потом делегат, то будешь пытаться писать код так, чтобы это замыкание не создавалось внути критического цикла мильон раз. Иначе твоя программа банально не взлетит. Ты просто не будешь вылезать из профайлера каждые следующие десяток накиданных строчек кода, работать некогда будет.
S>Моё понимание устройства программы делится на чётко выраженные уровни. Вот у нас уровень архитектуры приложения; вот у нас конструкции ЯП, которые эту архитектуру воплощают; вот MSIL,
Даже уже MSIL не важен, достаточно более крупно: память в куче, в поле объекта, локальная на стеке, статическая. Достаточно иметь представление, какие имеем затраты для каждого из популярных сценариев и можно начинать писать адекватные программы.
S>Благодаря этой отдельности я могу следить, на чём основаны те или иные предположения, и будут ли они оставаться справедливыми при замене того или иного уровня абстракции.
Да ты не можешь ни за чем следить, если не будешь понимать взаимосвязь различных уровней абстракции. И ведь ты прекрасно понимаешь, тем не менее регулярно вижу как ты толкаешь окружающим мантру, что типа это необязательно. А это неправда. Сколько разработчиков в своей жизни видел, столько раз убеждался, что еще как обязательно. Да и любого более-менее опытного разработчика с этого сайта возьми — обидится ведь, если предположить, что он пользуется инструментом не зная практически в совершенстве, как инструмент устроен и работает.
S>Я насмотрелся (да и сам таким был) на людей, которые сращивают все эти слои в голове в монолит. Им крайне некомфортно переходить на другие платформы, т.к. они не понимают границы применимости предположений.
Какие такие другие платформы? Сколько платформ/языков/поколений ты сменил за время своего стажа в IT? Откуда ты вообще мог взять такое странное мнение? Мне пока кажется, что ты споришь с надуманной для самого себя "потенциальной проблемой", которой реально нет и не было. Чем больше понимаешь устройство любой платформы, тем более комфортно под нее программировать. Это тебе по опыту десятка с лишним платформ и архитектур, включая микропрограммные. Для сравнения, из известного тебе: подробно изучал платформу Джаву в 96-м (именно саму платформу, включая JNI), и так и не мог заставить себя юзать это поделие. Первый дотнет показал в 3 раза большую живость аналогичных программ, только поэтому сразу заслужил внимание. Работа GC тоже замерялась с пристрастием в первый же день экспериментов в 2002-м.
S>Мир ведь не исчерпывается только дотнетом на x86. Есть ещё веб-программирование с его стеком AJAX-технологий; там очень многое нужно делать совсем не так, как представляется правильным в дотнетной настольной программе.
Серьезно?
А больше нет никаких областей, где "очень многое нужно делать совсем не так, как представляется правильным в дотнетной настольной программе"? Ну это уже совсем уровень аргументов, мягко говоря...
S>Есть, скажем, базы данных, где зазор между SQL и его физическим планом зачастую значительно сильнее, чем между C++ программой и её скомпилированным представлением.
Это если не владеть реляционной алгеброй и не изучать основные приемы отображения деклараций SQL на примитивы реляционной алгебры в пору своего образования. Тогда план запроса будет для тебя просто темный лес, разумеется, и что делать с индексами, когда делать и почему именно так — ты никогда не поймешь... это же не уровень SQL, это же уже не та абстракция, правильно?
Хороший пример, кстати, насчет SQL, в точку. Там вообще до 2-3-х порядков проседания производительности получить как 2 пальца об асфальт, если не понимать происходящее. Даже такую, казалось бы, мелочь, что есть varchar, а что есть text.
S>И умение переходить от одной СУБД к другой, одновременно представляя себе последствия для производительности, невозможно получить при монолитном представлении о "сути" SQL.
Ровно наоборот.
Не понимая устройства типов данных конкретной СУБД, и правил по их обработке размещению, и не владея реляционной алгеброй, твой SQL превращается в гранату в руках обезъяны. И да, работал не только с MS SQL и Oracle. Хотя и этих достаточно, если сравнивать типы данных и подходы к хранению записей.
S>Благодаря этой отдельности я могу следить, на чём основаны те или иные предположения, и будут ли они оставаться справедливыми при замене того или иного уровня абстракции.
так тебе vdimas верно и говорит, что единственно инвариантным утверждением при переходе между разными уровнями абстракции остается то, что переменная использует локальную адресацию с семантикой address := F(context, local-address), причем address — это тоже локальный адрес, но уже в каком-то другом более большом контексте .
если брать переменную из функции, то в качестве context-а будет stack-frame функции. при исполнении этот stack-frame может отображаться на stack исполнения, на регистры или на heap(отдельный объект), если используется замыкание.
если брать static переменную, то в качестве context-а будет сборка.
если брать переменную из объекта, то в качестве context-а будет сам объект
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, samius, Вы писали:
S>>Ссылка на объект ref-type в куче далеко не во всех сценариях ведет себя как типичный value-type. Например, мы не можем взять ее адрес
V>Можем в опкодах. А в языке можем создавать ссылку на эту ссылку.
Может в опкодах и можно взять адрес ссылки на управляемый объект, но будет ли такой код верифицирован? Насколько я понимаю, предлагаемое тобой противоречит стабильности работы GC. Да и речь была за C#, а не про опкоды.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, samius, Вы писали:
S>>Ну т.е. ты признаешь что между рантаймом и ЯВУ параллели может не быть, тем не менее настаиваешь на том, что бы именно по рантайму делать выводы о сущностях ЯВУ?
V>Не так. Оптимизация приемами суперкомпиляция — это скорее рантайм, а не compile-time. Посмотри рядом Синклеру чуть подробней обрисовал: http://www.rsdn.ru/forum/philosophy/4523815.1.aspx
Для того что бы выкидывать поля, переменные, структуры, функции и т.п. не нужна никакая суперкомпиляция и рантаймы. Даже если оптимизация выполнена приемами суперкомпиляции, это ничего не меняет в отношении исходного кода и двоичного.