Здравствуйте, gear nuke, Вы писали:
GN>Это, конечно, совершенно понятно: агрументов в защиту твоей теории нет
Хорошо, если не понимаешь цитаты MSDN и Саттера, вот аргумент кодом.
Чтобы не дать компилятору проигнорировать throw() и его отсутствие, разместим вызовы
void cb1(); и void cb2() throw(); в одной единнице трансляции, а реализацию в другой, при этом /LTCG отключим, в остальном — дефолтные параметры сборки релиза.
Итак, 1.cpp
#include"stdafx.h"struct X
{
X() { puts("ctor"); }
~X() { puts("dtor"); }
};
void cb1();
void cb2() throw();
void test1()
{
X x;
cb1();
}
void test2()
{
X x;
cb2();
}
int main()
{
test1();
test2();
}
2.cpp
void cb1() {}
void cb2() throw(){}
Результат:
int main()
{
00401070 push esi
test1();
00401071 call test1 (401010h)
test2();
00401076 mov esi,dword ptr [__imp__puts (4020A0h)]
0040107C push offset string "ctor" (402170h)
00401081 call esi
00401083 call cb2 (4010A0h)
00401088 push offset string "dtor" (402178h)
0040108D call esi
0040108F add esp,8
}
00401092 xor eax,eax
00401094 pop esi
00401095 ret
test2 заинлайнился, и видно, что оптимизатор предположил, что cb2 никогда не бросит.
Из листинга test1 вполне видно, что исключение из cb1 ожидается.
Здравствуйте, Alexander G, Вы писали:
AG>Хорошо, если не понимаешь цитаты MSDN и Саттера
Я их понимаю, просто не так, как тебе хочется. Наверное, потому что я видел реализацию диспетчера исключений и имею представление о его работе, а не увы.
AG>вот аргумент кодом.
Мне, в общем-то, хватит теоретических умозаключений, зачем оно бы так могло бы быть.
А код... посмотрим. Кстати, зачем лишняя сущность struct X? А как на счёт throw() в её *structors? Это повлияет на листинги
AG>
int main()
AG>{
AG>00401070 push esi
AG> test1();
AG>00401071 call test1 (401010h)
AG> test2();
AG>00401076 mov esi,dword ptr [__imp__puts (4020A0h)]
AG>0040107C push offset string "ctor" (402170h)
AG>00401081 call esi
AG>00401083 call cb2 (4010A0h)
AG>00401088 push offset string "dtor" (402178h)
AG>0040108D call esi
AG>0040108F add esp,8
AG>}
AG>00401092 xor eax,eax
AG>00401094 pop esi
AG>00401095 ret
AG>
AG>test2 заинлайнился
Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.
AG>, и видно, что оптимизатор предположил, что cb2 никогда не бросит.
Откуда видно? Мне видно, что диспетчер исключений вызовет unexpected() когда cb2() бросит. Такова его логика — не будет найден хендлер, будет вызван unexpected() за счёт того, что CRT вызвает SetUnhandledExceptionFilter. Если функция не ловит исключение, это делает вызывающий код — оптимизатор здесь его не видит.
AG>Из листинга test1 вполне видно, что исключение из cb1 ожидается. AG>
Тут много мусора. security cookie можно отключить /GS- (вообще, впринципе и 2.cpp не обязателен, листинг все равно будет)
Что же здесь видно. В выделенной строке сохраняется exception state, признак, что объект x сконструирован и при возникновении исключения нужно вызвать его деструктор. Хотя, про деструктор станет видно, если смотреть цепочку
Что касается неразрушения объекта в test2 — это как-то противоречит Стандарту? Попробуй завернуть внутренности testX, или main в try блок, деструкторы будут добавлены в таблицу раскрутки.
AG>Вывод 1: пустой throw() привёл к оптимизации.
А не "дефолтные параметры сборки релиза" привели ли к оптимизации?
AG>Включаем /d1ESrt
AG>Всё остаётся так же, только cb2 отделилась от cb1 и изменилась на:
AG>
AG>Вывод 2: с /d1ESrt пустой throw приводит к пессимизации
Да я уже несколько постов пишу, что /d1ESrt использован мною для пессимизации А ты как хотел, включить оптимизацию, и трактовать её заслуги как последствия (не)использования спецификаторов?
В моём варианте, как раз оптимизация полностью отключена. Поэтому становится видно влияние других, определяющих факторов, а именно того, что написано в исходном тексте.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Лишняя сущность struct X как раз нужна для того, чтобы показать оптимизацию для cb2.
У неё деструктор вызывается не в раскрутке стека, а как обычный вызов функции, и если cb2 таки бросит исключение, то dtor не вызовется.
GN>А как на счёт throw() в её *structors? Это повлияет на листинги
В названных условиях — никак не повлияет, код один в один, даже адреса те же.
AG>>
int main()
AG>>{
AG>>00401070 push esi
AG>> test1();
AG>>00401071 call test1 (401010h)
AG>> test2();
AG>>00401076 mov esi,dword ptr [__imp__puts (4020A0h)]
AG>>0040107C push offset string "ctor" (402170h)
AG>>00401081 call esi
AG>>00401083 call cb2 (4010A0h)
AG>>00401088 push offset string "dtor" (402178h)
AG>>0040108D call esi
AG>>0040108F add esp,8
AG>>}
AG>>00401092 xor eax,eax
AG>>00401094 pop esi
AG>>00401095 ret
AG>>
AG>>test2 заинлайнился
GN>Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.
Да. SEH фрейм стал не нужен благодаря throw(), т.е. throw() это таки оптимизация.
AG>>, и видно, что оптимизатор предположил, что cb2 никогда не бросит.
GN>Откуда видно?
Деструктор после cb2 вызывается как обычная функция.
GN>Что касается неразрушения объекта в test2 — это как-то противоречит Стандарту? Попробуй завернуть внутренности testX, или main в try блок, деструкторы будут добавлены в таблицу раскрутки.
Здравствуйте, Alexander G, Вы писали:
AG>Лишняя сущность struct X как раз нужна для того, чтобы показать оптимизацию для cb2. AG>У неё деструктор вызывается не в раскрутке стека, а как обычный вызов функции, и если cb2 таки бросит исключение, то dtor не вызовется.
Я могу из этого сделать вывод, что невызов деструктора противоречит 15.5.2. Как сделать выводы об оптимизации не знаю (однако, ближе к концу поста, надеюсь расставить всё по своим местам).
GN>>А как на счёт throw() в её *structors? Это повлияет на листинги AG>В названных условиях — никак не повлияет, код один в один, даже адреса те же.
Так я и намекаю, что условия тепличные, а взгляд слишком поверхностный.
В test1() зарегистрировано 2 "раскрутчика стека". При исключении, в зависимости от значения exception state, будет вызван либо __unwindfunclet$?test1@@YGXXZ$0, либо оба обработчика.
0й ehstate устанавливается перед вызовом конструктора X(), а 1й — перед вызовом cb1().
Ключевой момент: упомянутые функции имеют разные спецификаторы исключений
2й вариант без throw() в X. В test1() изменилось лишь разрушение объектов (и кстати видно, что мой компилятор по-старее, не выбрасывает установку -1)
Зато как изменилась test2().
Видно, что зарегистрирован деструктор только одного из объектов — x. Почему так, откуда компилятор "ждёт" исключение? Не надо торопиться с ответом, для него недостаточно исходных данных. Что бы стало достаточно, нужно добавить 3й объект типа X:
Вот только теперь вопросов не остаётся, похоже, что компилятор "не ждёт" исключения из cb2(). Я не знаю, как можно было сделать такой вывод на основании оригинального кода, разве что угадать!
Так, выходит я сдался? Отнюдь, эта долгая прилюдия лишь что бы показать, что не всё так просто, как хотелось бы:
AG>>>, и видно, что оптимизатор предположил, что cb2 никогда не бросит.
GN>>Откуда видно? AG>Деструктор после cb2 вызывается как обычная функция.
Ничего не доказывает этот деструктор. Да, читай выше — я даже как бы показал, что компилятор не ждет исключения. Почему? А потому что смотрим внимательно на пример:
И деструкторы при этом будут вызваны как обычные функции, если enexpected() "вернёт" правильное исключение, никаких нарушений 15.5.2!
Почему же отсутствие throw() так меняет поведение? Потому что в этом случае у функции нет нужды проверять, не выкинула ли она не то исключение. Очевидно, что исключение будет ловиться вызывающим кодом, что и видно на примерах.
GN>>Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.
AG>Да. SEH фрейм стал не нужен благодаря throw(), т.е. throw() это таки оптимизация.
SEH фрейм никуда не исчез, он переехал из test2() в cb2(). При отсутствии /d1ESrt смог его "оптимизировать" (выкинуть).
На самом деле, разнеся объявления и реализацию по разным единицам трансляции, был поставлен не тот эксперимент.
В конкретном случае с std::exception (о котором и был мой исходный пост) компилятор видит определение. Если бы было указано throw(), ему пришлось бы делать оптимизацию — "поднимать" установку SEH фрейма в вызывающие функции, и после анализа графа вызовов выкидавать его совсем. Часто он справляется, кроме сложных случаев (/d1ESrt). Что бы упростить ему жизнь, разработчики отказались от спецификатора.
GN>>Что касается неразрушения объекта в test2 — это как-то противоречит Стандарту? Попробуй завернуть внутренности testX, или main в try блок, деструкторы будут добавлены в таблицу раскрутки.
AG>Нет, нифига. Сам попробуй.
Спасибо. На ковыряние C++ EH MSVC я и без того потратил не менее 200 часов
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
GN>Ничего не доказывает этот деструктор. Да, читай выше — я даже как бы показал, что компилятор не ждет исключения. Почему? А потому что смотрим внимательно на пример: GN>Исключение перехватывается внутри cb2()! GN>И деструкторы при этом будут вызваны как обычные функции, если enexpected() "вернёт" правильное исключение, никаких нарушений 15.5.2!
Ну, так это с /d1ESrt , а не в реальной жизни.
GN>Почему же отсутствие throw() так меняет поведение? Потому что в этом случае у функции нет нужды проверять, не выкинула ли она не то исключение. Очевидно, что исключение будет ловиться вызывающим кодом, что и видно на примерах.
GN>>>Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.
AG>>Да. SEH фрейм стал не нужен благодаря throw(), т.е. throw() это таки оптимизация.
GN>SEH фрейм никуда не исчез, он переехал из test2() в cb2(). При отсутствии /d1ESrt смог его "оптимизировать" (выкинуть).
Этот ключ даже не документирован и вряд ли кем-то используется в продакшн, а без него скорее код с пустой спецификацией исключений будет лучше оптимизирован.
GN>На самом деле, разнеся объявления и реализацию по разным единицам трансляции, был поставлен не тот эксперимент.
GN>В конкретном случае с std::exception (о котором и был мой исходный пост) компилятор видит определение.
То, что компилятор видит определение влияет на оптимизацию следующим образом: компилятор может и без throw() понять, что функция никогда не бросит.
GN>Если бы было указано throw(), ему пришлось бы делать оптимизацию — "поднимать" установку SEH фрейма в вызывающие функции, и после анализа графа вызовов выкидавать его совсем.
Что значит пришлось бы поднимать установку SEH фрейма в вызывающие функции? В случае throw() компилятор просто всегда верит, что функция не бросит исключение.
GN>Спасибо. На ковыряние C++ EH MSVC я и без того потратил не менее 200 часов
Я не предлагал ковырять, я просто предлагал проверить, что никакой terminate в описаных условиях не вызывается.
Здравствуйте, Alexander G, Вы писали:
GN>>И деструкторы при этом будут вызваны как обычные функции, если enexpected() "вернёт" правильное исключение, никаких нарушений 15.5.2! AG>Ну, так это с /d1ESrt , а не в реальной жизни.
В "реальной жизни" описанная ситуация невозможна, спецификаторы исключений якобы не поддержаны, так что опять нет нарушения 15.5.2.
AG>т.е. здесь
Этот ключ даже не документирован и вряд ли кем-то используется в продакшн, а без него скорее код с пустой спецификацией исключений будет лучше оптимизирован.
Я наконец-то понял, ты пытаешься рассуждать об общем случае? А я отвечал на вопрос АТ!
GN>>В конкретном случае с std::exception (о котором и был мой исходный пост) компилятор видит определение.
AG>То, что компилятор видит определение влияет на оптимизацию следующим образом: компилятор может и без throw() понять, что функция никогда не бросит.
Разумеется, за исключением нюанса — не должна бросить по контракту. Но у компилятора есть некоторая "глубина анализа", дальше которой он не видит. То есть когда речь идёт о длинных цепочках функций, вызывающих одна другую, оптимизатор может не потянуть.
GN>>Если бы было указано throw(), ему пришлось бы делать оптимизацию — "поднимать" установку SEH фрейма в вызывающие функции, и после анализа графа вызовов выкидавать его совсем.
AG>Что значит пришлось бы поднимать установку SEH фрейма в вызывающие функции?
Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.
AG>Я не предлагал ковырять, я просто предлагал проверить, что никакой terminate в описаных условиях не вызывается.
Не вижу связи между terminate() и оптимизацией std::exception. Я предлагал изучить больше примеров перед попыткой делать выводы.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Этот ключ даже не документирован и вряд ли кем-то используется в продакшн, а без него скорее код с пустой спецификацией исключений будет лучше оптимизирован.
GN>Я наконец-то понял, ты пытаешься рассуждать об общем случае? А я отвечал на вопрос АТ!
Какой оптимизиции может помочь отсутсвие throw() ?
GN>Разумеется, за исключением нюанса — не должна бросить по контракту. Но у компилятора есть некоторая "глубина анализа", дальше которой он не видит. То есть когда речь идёт о длинных цепочках функций, вызывающих одна другую, оптимизатор может не потянуть.
Может протянуть, может не протянуть, в любом случае, насколько я вижу, хуже от пустого throw() (с /EHs , без /d1... ) не будет.
AG>>Что значит пришлось бы поднимать установку SEH фрейма в вызывающие функции?
GN>Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.
Можно реальный пример, когда SEH фрейм ставится снаружи функции с throw()-спецификацией ?
же есть пример.
AG>Может протянуть, может не протянуть, в любом случае, насколько я вижу, хуже от пустого throw() (с /EHs , без /d1... ) не будет.
Не-не, погоди Давай переформулируем твое предложение как следует. Вынесем необходимые условия из скобочек:
"С ключем /EHs и без /d1ESrt, в тех случаях, которые ты видел, присутсвие throw() не ухудьшает ситуацию".
А теперь пожалуйста — применяй индукцию для обобщения Я, впрочем, не спорю, что где-то так и есть.
В случае с std::exception это позволяет получить оптимальный код при всех возможных вариантах ключей. С /d1ESrt он лучше, без — по крайней мере, не хуже.
Вот кстати из libcomo, и полагаю везде так:
// This header exists solely for portability. Normally it just includes
// the header <exception>.
// The header <exception> contains low-level functions that interact
// with a compiler's exception-handling mechanism. It is assumed to
// be supplied with the compiler, rather than with the library, because
// it is inherently tied very closely to the compiler itself.
и это пишут неплохо разбирающиеся в вопросе люди, они почему-то считают, что если так сделано — значит так надо. Можно поискать объяснения, как это делаю я (сначала -теоретические предпосылки, потом ищем подтверждения на практике). Можно говорить, что это плохо, и лучше по-другому и показывать что в другом случае может быть лучше по-другому, вместо хоть какого-то обоснование в теории.
Может оно надо, что бы результирующий код получался идентичным независимо от ключей компиляции? (libc то собрана с определенными). Вот тут я соглашусь, что с оптимизацией поторопился как с первопричиной. А пока других кандидатов на причины нет — буду придерживаться своей теории.
GN>>Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.
AG>Можно реальный пример, когда SEH фрейм ставится снаружи функции с throw()-спецификацией ?
Я видимо неясно выражаюсь, иначе почему выкидываешь половину контекста? "и после анализа графа вызовов выкидавать его совсем".
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
GN>В случае с std::exception это позволяет получить оптимальный код при всех возможных вариантах ключей. С /d1ESrt он лучше, без — по крайней мере, не хуже.
Как с — не важно. Без — не факт, что не хуже, но никогда не лучше.
GN>Вот кстати из libcomo, и полагаю везде так: GN>
GN>// This header exists solely for portability. Normally it just includes
GN>// the header <exception>.
GN>// The header <exception> contains low-level functions that interact
GN>// with a compiler's exception-handling mechanism. It is assumed to
GN>// be supplied with the compiler, rather than with the library, because
GN>// it is inherently tied very closely to the compiler itself.
GN>
и это пишут неплохо разбирающиеся в вопросе люди, они почему-то считают, что если так сделано — значит так надо.
То, что следует использовать стандартную реализацию, это очевидно, но из этого нельзя сджелать никаких выводов, почему в стандартнй реализации сделано так или иначе.
GN>Можно поискать объяснения, как это делаю я (сначала -теоретические предпосылки, потом ищем подтверждения на практике).
Теоретические предпосылки для оптимизации при наличии throw() просты: компилятор получает дополнительную информацию, при этом не обеспечивает никаких дополнительных гарантий. Соответственно он может или воспользоваться возмоностью для оптимизации, или нет, но испортить не может. То что throw() это __declspec(nothrow), а __delcpsec(nothrow) служит для оптимизации — это в MSDN сказано.
GN>Может оно надо, что бы результирующий код получался идентичным независимо от ключей компиляции? (libc то собрана с определенными). Вот тут я соглашусь, что с оптимизацией поторопился как с первопричиной. А пока других кандидатов на причины нет — буду придерживаться своей теории.
Оптимизация не только не первопричина, но и не причина вообще.
У меня есть более правдоподобные версии.
1. Т.к. спецификация исключений не поддерживается, решили не требовать от разработчиков их писать в наследниках и не указывать их в стандартной библиотеке и её документации.
2. Так сложилось исторически: в VC8 (я на самом деле приводил листинги VC8) там тоже нет спецификаций исключений. Видимо по какой-либо причние их изначально не указали, в последствии не стали добавлять, чтобы не ломать совместимость со старым кодом.
GN>>>Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.
AG>>Можно реальный пример, когда SEH фрейм ставится снаружи функции с throw()-спецификацией ?
GN>Я видимо неясно выражаюсь, иначе почему выкидываешь половину контекста? "и после анализа графа вызовов выкидавать его совсем".
Т.е. из-за throw() происходит такая штука, как "поднятие" обработчика исключений в вызывающую функцию, при этом поднянтый обработчик всегда выбрасывается? Не верю. Я считаю, что всё проще. Обработчик, который якобы поднимается, никогда не генерируется вообще. Я поверю в наличие "поднятого" ообработчика только кода увижу ситуацию, когда он не выбрасывается.
Здравствуйте, Alexander G, Вы писали:
AG>Как с — не важно.
Почему считаешь не важным /d1ESrt? Потому что не используешь сам. Разработчики компилятора не могут руководствоваться такими соображениями. Им необходимо учесть все варианты.
AG>То, что следует использовать стандартную реализацию, это очевидно, но из этого нельзя сджелать никаких выводов, почему в стандартнй реализации сделано так или иначе.
Из этого следует вывод, что разработчикам виднее, я других и не делал.
AG>Теоретические предпосылки для оптимизации при наличии throw() просты: компилятор получает дополнительную информацию, при этом не обеспечивает никаких дополнительных гарантий. Соответственно он может или воспользоваться возмоностью для оптимизации, или нет, но испортить не может. То что throw() это __declspec(nothrow), а __delcpsec(nothrow) служит для оптимизации — это в MSDN сказано.
Да, там сказано что это лучше в одном из случаев (/EHs). При этом там сказано вот что еще:
This attribute tells the compiler that the declared function and the functions it calls never throw an exception.
то есть речь о функциях без определения. std::exception не тот случай, компилятор определение видит, чем ему помогает спецификатор?
AG>У меня есть более правдоподобные версии.
AG>1. Т.к. спецификация исключений не поддерживается, решили не требовать от разработчиков их писать в наследниках и не указывать их в стандартной библиотеке и её документации.
Спецификации поддерживаются.
AG>2. Так сложилось исторически:
Были другие breaking changes, пережили. Я скорее поверю, что /d1ESrt не документируют по историческим причинам. Даже боюсь представить что будет, если он станет широкоизвестен.
AG>Я поверю в наличие "поднятого" ообработчика только кода увижу ситуацию, когда он не выбрасывается.
Она в самом первом моем посте. Просто у тебя зуб на /d1ESrt
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, gear nuke, Вы писали:
AG>>Как с — не важно.
GN>Почему считаешь не важным /d1ESrt? Потому что не используешь сам. Разработчики компилятора не могут руководствоваться такими соображениями. Им необходимо учесть все варианты.
Учесть все варианты — возможно. Оптимизировать имеет смысл лишь для практически используемых вариантов.
AG>>Теоретические предпосылки для оптимизации при наличии throw() просты: компилятор получает дополнительную информацию, при этом не обеспечивает никаких дополнительных гарантий. Соответственно он может или воспользоваться возмоностью для оптимизации, или нет, но испортить не может. То что throw() это __declspec(nothrow), а __delcpsec(nothrow) служит для оптимизации — это в MSDN сказано.
GN>Да, там сказано что это лучше в одном из случаев (/EHs). При этом там сказано вот что еще:
GN>
This attribute tells the compiler that the declared function and the functions it calls never throw an exception.
GN> то есть речь о функциях без определения. std::exception не тот случай, компилятор определение видит, чем ему помогает спецификатор?
Нет. Из выделенного не следует, что речь исключительно о функциях без определения.
AG>>У меня есть более правдоподобные версии.
AG>>1. Т.к. спецификация исключений не поддерживается, решили не требовать от разработчиков их писать в наследниках и не указывать их в стандартной библиотеке и её документации.
GN>Спецификации поддерживаются.
В "реальной жизни" описанная ситуация невозможна, спецификаторы исключений якобы не поддержаны,
AG>>2. Так сложилось исторически:
GN>Были другие breaking changes, пережили. Я скорее поверю, что /d1ESrt не документируют по историческим причинам. Даже боюсь представить что будет, если он станет широкоизвестен.
Но было и протягивание нестандартного поведения для совместимости. Хотя бы forScope или передачу временного объекта по неконстантной ссылке.
В данном случае throw() даст мало преимуществ, чтобы его стоило вводить.
AG>>Я поверю в наличие "поднятого" ообработчика только кода увижу ситуацию, когда он не выбрасывается.
GN>Она в самом первом моем посте.
Там он не выбрасывается но и не "поднимается", так что не проходит.
GN>Просто у тебя зуб на /d1ESrt
Здравствуйте, gear nuke, Вы писали:
GN>Спецификации поддерживаются. GN>Я скорее поверю, что /d1ESrt не документируют по историческим причинам. Даже боюсь представить что будет, если он станет широкоизвестен.
ESrt в х64 не поддерживаются, только в х86
Здравствуйте, byleas, Вы писали:
B>ESrt в х64 не поддерживаются, только в х86
Дык переусложнили EH и не справились наверное но это мелочи
Лучше скажи что-нибудь про std::exception это делалось на заре разбирательств с EH, когда я еще постоянно смотрел генерируемый код, поэтому я уверен что это оптимизация. Но почему-то особо дельше той ревизии не ушло...
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth