Мысленный эксперимент: ООП без классов

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

Модератор: tyomitch

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 16:59

Не путай. Зона видимости полей и локальных (псевдо-локальных - на счёт их локальности я теперь сомневаюсь) различна.

Ещё раз:
Код: Выделить всё

Delegate foo() as long
Public F as Foo

Sub AnonCreator()
    Dim X as Long
    X = 100
    Set F = AddressOf Delegate as foo
           X = X + 1
           Y = Y + 1
           MsgBox X
           MsgBox Y
     End Delegate
     F()
End sub

AnonCreator()
F()
F()
F()
F()


Какие сообщения по твоему должны выводиться?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 17:06

Зона видимости и время жизни никак не связаны. Даже в обычном VB.

В твоём коде не определена Y.
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 17:07

В твоём коде не определена Y.

И что? Implicit Declaration.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 17:20

Тогда 101, 1, 102, 1, 103, 1, 104, 1, 105, 1
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 17:23

Отлично. А теперь объясни мне, почему значение X сохраняется между вызовами, а значение Y - нет?

Предполагаю ответ: "Потому что X замыкается на AnonCreator::X, а Y ни на что не замыкается".
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 17:28

При каждом входе в функцию её локальные переменные создаются заново.
В AnonCreator входят один раз, поэтому X создаётся один раз.
В анон.метод входят пять раз, поэтому Y создаётся пять раз.
Хорошее объяснение?
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 17:30

Плохое. Потому что намеренно упущен один важный момент.

При каждом входе в функцию её локальные переменные создаются заново.

При каждом выходе из функции эти локальные переменные удаляются.

В AnonCreator входят один раз, поэтому X создаётся один раз.

После чего из AnonCreator-а выходят один раз, поэтому Х, содержащий 101, убивается.

Откуда в последующии разы воскресает число 101 и соотв. 102, 103?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 17:39

Хакер писал(а):
При каждом входе в функцию её локальные переменные создаются заново.

При каждом выходе из функции эти локальные переменные удаляются.

А теперь они удаляются только тогда, когда на них исчезают замыкания.

О чём и шла речь в моём первом посте по поводу времени жизни замыканий.
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 17:43

А теперь они удаляются только тогда, когда на них исчезают замыкания.


Нет, они должны удаляться именно тогда, когда происходит выход из процедуры.

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

Вариант "хранить переменные не в стеке" мне не нравится. Более того, твой принцип мне тоже не нравится :)

Мне нравится, если данный код будет выдавать сообщения

101 1
1 1
1 1
1 1
...
1 1
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 17:52

Хакер писал(а):Вариант "хранить переменные не в стеке" мне не нравится.

ОК, тогда перестань использовать строки и объекты ;-)


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


Ну, и полурелигиозный вопрос: зачем вообще хранить локальные переменные в стеке?

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

Да, я не люблю стек.
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 18:32

tyomitch
Строки и объекты слишком большие, чтобы хранить их в стеке.
Строки и объекты не имеют фиксированного размера, и поэтому при обращении к соседним элементами придётся учитывать как-то размер строк и объектов.

Объект, созданный в процедуре, может быть убить до выхода из этого экземпляра процедуры, а может продолжить жить, и быть убитым на выходе из программы.

В первом случае, в стеке останется дыра, и придётся как-то дефрагментировать его. Это не есть хорошо.

Во втором случае объект останется в стеке. Тогда будет риск что какие-то другие процедуры (особенно рекурсивные) затрут этот объект.

Это опять лишний неэффективынй код.

По этим причинам объекты и строки хранят в других управляемых структурах памяти (в кучах. В кучах как класс. Не обязательно в виндовых кучах. Может быть свой алгоритм управления данными. --На случай если попробуешь придраться, сказав что "что-нибудь" не юзает виндовую кучу для хранения чего-либа)

байты, ворды, дворды, кьюворды и подобные небольшие структуры наоборот эффективно хранить в стеке.

В любом случае, стек необходим и для строк и для объектов, потому что указатели на них (на строки и объекты) всё равно хранятся (хранят... их... там... обычно. Ибо эффективно) в стеке.

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

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


Ну, и полурелигиозный вопрос: зачем вообще хранить локальные переменные в стеке?

А где их хранить?

Создавать в EXE-файле секцию для локальных переменных?
Плохо, ибо:
1) Увеличивается размер EXE-файла.
2) Это не позволит использовать рекурсии.
3) В многопоточных приложениях появится ещё больше геморроя, потому что все подобные локальные переменные ни чем не будут отличаться от глобальных. И две (или более) одновременно работающие процедуры (без всяких даже рекурсий, просто несколько процедур, работающих в разных потоках) будут портить друг-другу локальные переменные.

Выделять под это дело память в ран-тайме?
Плохо, ибо:
1) Нужно где-то хранить указатели на области памяти для каждого экземпляра, в которых находятся ЛП для них (для экземпляров).
Для этого всё равно придётся либо использовать стек (в чём тогда смысл подобного изврата - при обращении к памяти придётся только лишний раз учитывать смещение области памяти ЛП.
Либо не использовать стек, а класть эти адреса в другое место, единожды, для каждой процедуры. В этом случае опять придётся отказаться от рекурсий вообще.
2) Проблема с рекурсией (вытекает из п.1)
3) Проблема с многопоточностью (вытекает из п.1)
4) Будет неэффективно жраться память под ЛП-фреймы экземпляров функций.


Использовать кучу (виндозную либо свою)?
Плохо, ибо:
1) Кучи - слишком интеллектуальны. Они решат все выше-описанные проблемы. Однако стек имеет обыкновение расти и уменьшаться последовательно. Кучи же предусматривают возможность эффективного занатия/освобождения регионов кучи. При попытке программы заАЛЛОЧить себе 10 байт памяти менеждер куч будет искать наименьшую "дырку" в куче, куда поместятся эти 10 байт, и, в случае если такой дырки не окажется, расширять кучу - коммитить новую страницу. Для памяти, использующеся в качестве стека вызовов и для хранения ЛП-фреймов, эта интеллектуальность излишняя - в этой памяти гарантированно не может быть дырок (описанные тобою случае жития переменной после выхода их процедуры - не в счёт :) ). АЛЛОКейтинг всегда должен всегда выделять регион кучи в конце регионов. Регионы будут стоять вполтную и дырок между ними оказаться не может (я уже повторяюсь).

В итоге - виндовые кучи слишком избыточны (и медленны) для наших целей.

Мы можем написать свой собственный менеждер своей собственной кучи. И мы получим структуру, ни чем не отличающуюся от обычного стека, который ты недолюбливаешь, непонятно почему :)

Фух. Блин. И так некогда, а ещё увлёкся написанием постинга :)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 18:52

Хакер писал(а):Ещё раз: если следовать этой логике, переменная, которая будет кому-то нужна после выхода функции останется в стеке, и будет там. Есть риск что её кто-то затрёт. Поэтому, так нельзя делать.

Эта переменная не останется в стеке. И не будет там.
Потому что я не люблю стек, и не ограничен им.


Хакер писал(а):1) Увеличивается размер EXE-файла.

Неинициализированные переменные не занимают места в EXE-файле, ты, знаток PE ;-)

Хакер писал(а):2) Это не позволит использовать рекурсии.

Каждая рекурсивная процедура пусть сама заботится о своём жизнеподдержании. И вообще, поддержка процедур должна быть заботой компилятора, а не процессора.

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

Есть TLS, и даже какая-то фича в PE, который позволяет прямо в образе определять thread-local переменные.
В сферическом вакууме, можно пожелать появления в PE thread-local-ьных секций, например с адресацией через gs: (Никого же не удивляет, что содержимое fs: у каждого потока своё.)

Хакер писал(а):4) Будет неэффективно жраться память под ЛП-фреймы экземпляров функций.

Как они могут занимать вне стека больше места, чем внутри него?


Хакер писал(а):Фух. Блин. И так некогда, а ещё увлёкся написанием постинга :)

Тогда с постингом второй половины текста подожду ещё :-P
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.08.2007 (Ср) 19:06

Эта переменная не останется в стеке. И не будет там.
Потому что я не люблю стек, и не ограничен им.

Ну то ты, а то я.

Неинициализированные переменные не занимают места в EXE-файле, ты, знаток PE

Так :) Фишка не прошла :)
Ну всё равно это не есть гуд.

Каждая рекурсивная процедура пусть сама заботится о своём жизнеподдержании. И вообще, поддержка процедур должна быть заботой компилятора, а не процессора.

Компилятор в принципе не может определить, будет ли рекурсия или нет.

В сферическом вакууме, можно пожелать появления в PE thread-local-ьных секций, например с адресацией через gs: (Никого же не удивляет, что содержимое fs: у каждого потока своё.)

Есть TLS, и даже какая-то фича в PE, который позволяет прямо в образе определять thread-local переменные.

Юзание TLS для локальных переменных ещё больше замедлит программу.

В сферическом вакууме, можно пожелать появления в PE thread-local-ьных секций, например с адресацией через gs: (Никого же не удивляет, что содержимое fs: у каждого потока своё.)

Можно :)
(Было бы здорово, если бы создал топик с хорошим описанием TEB-а (или TIB-а? как правильно?)).

Как они могут занимать вне стека больше места, чем внутри него?

Если функция гарантировано нерекурсиваня и имеет 100 лоновых ЛП, и в данный момент эта функция не вызвана, то 400 байт памяти жрутся, при том, что функция возможно вообще не запустится.

Стек не жрётся, потому что в нём положение ЛП-кадра нефиксированное (и это хорошо - именно это снимает все проблемы). Если процедура не вызвана, на месте этих 400 байт лежит что-то другое.

Тогда с постингом второй половины текста подожду ещё

Пости пости. А я может быть запощу Native DLL свой, ибо для выполнения срочнейшего задания его придётся доделать.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 22.08.2007 (Ср) 19:19

Хакер писал(а):
Каждая рекурсивная процедура пусть сама заботится о своём жизнеподдержании. И вообще, поддержка процедур должна быть заботой компилятора, а не процессора.

Компилятор в принципе не может определить, будет ли рекурсия или нет.

Часто может. (Для всех неэкспортируемых и неимпортирующих функций -- почти всегда может.)


Хакер писал(а):(Было бы здорово, если бы создал топик с хорошим описанием TEB-а (или TIB-а? как правильно?)).

И тот и другой есть. TIB -- первое поле TEB.


Хакер писал(а):
Как они могут занимать вне стека больше места, чем внутри него?

Если функция гарантировано нерекурсиваня и имеет 100 лоновых ЛП, и в данный момент эта функция не вызвана, то 400 байт памяти жрутся, при том, что функция возможно вообще не запустится.

Пустые страницы не занимают физической памяти. Двух гигов виртуальной под все локальные переменные в программе должно хватить ;-)
Пусть компилятор сам распихивает их по страницам так, чтобы используемые совместно лежали совместно. Он умный, пусть работает.
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.08.2007 (Чт) 10:20

Хакер писал(а):Юзание TLS для локальных переменных ещё больше замедлит программу.

К слову, для глобальных оно уже юзаются (в VB6), и небо не упало...
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 23.08.2007 (Чт) 10:25

tyomitch
С чего ты взял что юзается? С того ли только, что увидел выховы Tls*-функций в msvbvm-е?

Но в любом случае, если оно будет юзаться ещё и для локальных переменных, быстрее программа точно не будет.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.08.2007 (Чт) 12:03

Apartment-Model Threading in Visual Basic писал(а):Visual Basic’s implementation of apartment-model threading eliminates conflicts in accessing global data from multiple threads by giving each apartment its own copy of global data


Ну, и вызовы этих функций не только присутствуют в msvbvm, но и происходят (например, при каждом обращении к Err).
Изображение

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 31.08.2007 (Пт) 17:05

К слову, глобальные переменные(те что объявлены в модулях) работают через TLS только в dll с apartment thread, и посему использовать apartment thread model в dll не желательно. скорость это не увеличивает
Но если предположить что мы знаем какие сейчас есть версии windows и будем выпускать патчи при появлении новых можно использовать сразу обращение к fs, без вызова функци и тогда скорость не будет падать вообще.
В сферическом вакууме, можно пожелать появления в PE thread-local-ьных секций, например с адресацией через gs
А в windows можно создать новый сегмент из пользовательского режима? Или подразумевается что фича появится аж в windows?
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 31.08.2007 (Пт) 17:22

Подразумевается.


Далее, в многопоточных ActiveX EXE тоже все обращения к переменным модулей идут через TLS.

И ещё раз: обращения к TLS идут как минимум при каждой файловой операции и при каждом обращении к глобальным объектам (App, Err, etc.), независимо от способа компиляции проги.
Изображение

Пред.

Вернуться в Tyomitch

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

    TopList