Сообщение Re[8]: Самый низкоуровневый язык, ага от 28.09.2023 22:27
Изменено 28.09.2023 22:30 vdimas
Re[8]: Самый низкоуровневый язык, ага
Здравствуйте, Евгений Музыченко, Вы писали:
V>>в гарвардской архитектуре инициализация "внутренней" памяти тоже нифига не автоматическая, требует некоторого кода.
ЕМ>Но не обязательно в CRT. Это может быть тот же загрузчик — хоть примитивный в МК, хоть навороченный в ОС.
Нет никакой ОС за ненадобностью.
После reset программа тупо исполняется с некоторого адреса.
V>>Допустим, не в БД, а в неоднородном NUMA-окружении, в неких суперкомпах.
V>>Под это есть свои Си-подобные диалекты, позволяющие декларативно описывать неоднородную память, дабы сохранять переносимость программ в исходниках.
ЕМ>Такое надо делать на плюсах, заворачивая в классы.
Повторю, для этого есть специальные языки для NUMA, которые выглядят как расширения к мейнстримовым.
Увы, весь современный мейнстрим создан для SMP-архитектур, и это одна из причин, почему в С++ не было библиотек для потоков — потому что SMP окончательно победила всех и вся только в нулевые, когда даже суперкомпы стали делать на обычных процах от персоналок.
V>>На асме, однако, приходилось писать критичные вещи, где надо расчитывать код с точностью до такта.
V>>Ну и еще для уменьшения вложенности вызова подпрограмм, т.к. на асме легко сделать в одну подпрограмму несколько точек входа
ЕМ>Все это успешно делается на "расширенных сях" с псевдопеременными, intrinsic-функциями, ассемблерными вставками и т.п.
Учитывая изкаробочную развитую модульность в Си/С++, ассемблерные вставки не всегда нужны.
Полноценный асм (макро-асм) на порядки удобнее убогих "вставок". ))
ЕМ>Если под архитектуру есть только "канонический" C, то вряд ли она сильно популярна.
Под любую архитектуру микриков, в первую очередь, есть асм.
Порой тулчейны не генерят непосредственно код из Си, а работают через промежуточный асм — оно зачастую и для отладки удобно.
V>>многие переменные могли покрывать адресное пространство друг друга в разные моменты работы программы), а компилятор справляется с этим плохо, т.к. не способен полностью трассировать программу.
ЕМ>Не понял, для чего здесь "трассировка". Эта задача решается сочетанием struct/union.
Зачем?
Вот у тебя есть локальные переменные.
В обычной архитектуре локальные переменные живут в стеке, а в микриках часто стек обслуживает только возвраты, а локальные данные живут во вручную организуемом стеке данных в ОЗУ.
Так вот, эта техника далека от эффективности.
В простейших случаях (и в отсутствии рекурсии) компиляторы способны статически распределить в ОЗУ локальные переменные с перекрытием.
Например, код верхнего уровня вызывает в разные моменты разные ф-ии, тогда компилятор статически распределит в ОЗУ адреса локальных переменных процедуры верхнего уровня и вызываемых по цепочке процедур. У вызываемых процедур адреса локальных переменных будут пересекаться, что не страшно, т.к. они никогда не используются одновременно.
Но когда идёт комбинаторика вызовов, и компилятор не способен оттрасировать статическое распределение локальных переменных, он начинает организовывать динамические фреймы "ручного стека", что тяжело и дорого. И тогда программист закатывает рукава и выполняет такое распределение сам, если знает, что некие ф-ии не вызывают друг друга ни в каком из сценариев.
В этом случае идёт отказ от локальных переменных в пользу перекрывающихся глобальных.
Да, можно через union, но с той оговоркой, что ты не объявляешь такую глобальную переменную, а высчитываешь ее адрес "в уме", как показывал выше, где адрес будет константой времени компиляции.
V>>в гарвардской архитектуре инициализация "внутренней" памяти тоже нифига не автоматическая, требует некоторого кода.
ЕМ>Но не обязательно в CRT. Это может быть тот же загрузчик — хоть примитивный в МК, хоть навороченный в ОС.
Нет никакой ОС за ненадобностью.
После reset программа тупо исполняется с некоторого адреса.
V>>Допустим, не в БД, а в неоднородном NUMA-окружении, в неких суперкомпах.
V>>Под это есть свои Си-подобные диалекты, позволяющие декларативно описывать неоднородную память, дабы сохранять переносимость программ в исходниках.
ЕМ>Такое надо делать на плюсах, заворачивая в классы.
Повторю, для этого есть специальные языки для NUMA, которые выглядят как расширения к мейнстримовым.
Увы, весь современный мейнстрим создан для SMP-архитектур, и это одна из причин, почему в С++ не было библиотек для потоков — потому что SMP окончательно победила всех и вся только в нулевые, когда даже суперкомпы стали делать на обычных процах от персоналок.
V>>На асме, однако, приходилось писать критичные вещи, где надо расчитывать код с точностью до такта.
V>>Ну и еще для уменьшения вложенности вызова подпрограмм, т.к. на асме легко сделать в одну подпрограмму несколько точек входа
ЕМ>Все это успешно делается на "расширенных сях" с псевдопеременными, intrinsic-функциями, ассемблерными вставками и т.п.
Учитывая изкаробочную развитую модульность в Си/С++, ассемблерные вставки не всегда нужны.
Полноценный асм (макро-асм) на порядки удобнее убогих "вставок". ))
ЕМ>Если под архитектуру есть только "канонический" C, то вряд ли она сильно популярна.
Под любую архитектуру микриков, в первую очередь, есть асм.
Порой тулчейны не генерят непосредственно код из Си, а работают через промежуточный асм — оно зачастую и для отладки удобно.
V>>многие переменные могли покрывать адресное пространство друг друга в разные моменты работы программы), а компилятор справляется с этим плохо, т.к. не способен полностью трассировать программу.
ЕМ>Не понял, для чего здесь "трассировка". Эта задача решается сочетанием struct/union.
Зачем?
Вот у тебя есть локальные переменные.
В обычной архитектуре локальные переменные живут в стеке, а в микриках часто стек обслуживает только возвраты, а локальные данные живут во вручную организуемом стеке данных в ОЗУ.
Так вот, эта техника далека от эффективности.
В простейших случаях (и в отсутствии рекурсии) компиляторы способны статически распределить в ОЗУ локальные переменные с перекрытием.
Например, код верхнего уровня вызывает в разные моменты разные ф-ии, тогда компилятор статически распределит в ОЗУ адреса локальных переменных процедуры верхнего уровня и вызываемых по цепочке процедур. У вызываемых процедур адреса локальных переменных будут пересекаться, что не страшно, т.к. они никогда не используются одновременно.
Но когда идёт комбинаторика вызовов, и компилятор не способен оттрасировать статическое распределение локальных переменных, он начинает организовывать динамические фреймы "ручного стека", что тяжело и дорого. И тогда программист закатывает рукава и выполняет такое распределение сам, если знает, что некие ф-ии не вызывают друг друга ни в каком из сценариев.
В этом случае идёт отказ от локальных переменных в пользу перекрывающихся глобальных.
Да, можно через union, но с той оговоркой, что ты не объявляешь такую глобальную переменную, а высчитываешь ее адрес "в уме", как показывал выше, где адрес будет константой времени компиляции.
Re[8]: Самый низкоуровневый язык, ага
Здравствуйте, Евгений Музыченко, Вы писали:
V>>в гарвардской архитектуре инициализация "внутренней" памяти тоже нифига не автоматическая, требует некоторого кода.
ЕМ>Но не обязательно в CRT. Это может быть тот же загрузчик — хоть примитивный в МК, хоть навороченный в ОС.
Нет никакой ОС за ненадобностью.
После reset программа тупо исполняется с некоторого адреса.
V>>Допустим, не в БД, а в неоднородном NUMA-окружении, в неких суперкомпах.
V>>Под это есть свои Си-подобные диалекты, позволяющие декларативно описывать неоднородную память, дабы сохранять переносимость программ в исходниках.
ЕМ>Такое надо делать на плюсах, заворачивая в классы.
Повторю, для этого есть специальные языки для NUMA, которые выглядят как расширения к мейнстримовым.
Увы, весь современный мейнстрим создан для SMP-архитектур, и это одна из причин, почему в С++ не было библиотек для потоков — потому что SMP окончательно победила всех и вся только в нулевые, когда даже суперкомпы стали делать на обычных процах от персоналок.
V>>На асме, однако, приходилось писать критичные вещи, где надо расчитывать код с точностью до такта.
V>>Ну и еще для уменьшения вложенности вызова подпрограмм, т.к. на асме легко сделать в одну подпрограмму несколько точек входа
ЕМ>Все это успешно делается на "расширенных сях" с псевдопеременными, intrinsic-функциями, ассемблерными вставками и т.п.
Учитывая изкаробочную развитую модульность в Си/С++, ассемблерные вставки не всегда нужны.
Полноценный асм (макро-асм) на порядки удобнее убогих "вставок". ))
ЕМ>Если под архитектуру есть только "канонический" C, то вряд ли она сильно популярна.
Под любую архитектуру микриков, в первую очередь, есть асм.
Порой тулчейны не генерят непосредственно код из Си, а работают через промежуточный асм — оно зачастую и для отладки удобно.
V>>многие переменные могли покрывать адресное пространство друг друга в разные моменты работы программы), а компилятор справляется с этим плохо, т.к. не способен полностью трассировать программу.
ЕМ>Не понял, для чего здесь "трассировка". Эта задача решается сочетанием struct/union.
Зачем?
Вот у тебя есть локальные переменные.
В обычной архитектуре локальные переменные живут в стеке, а в микриках часто стек обслуживает только возвраты, а локальные данные живут во вручную организуемом стеке данных в ОЗУ.
Так вот, эта техника далека от эффективности.
В простейших случаях (и в отсутствии рекурсии) компиляторы способны статически распределить в ОЗУ локальные переменные с перекрытием.
Например, код верхнего уровня вызывает в разные моменты разные ф-ии, тогда компилятор статически распределит в ОЗУ адреса локальных переменных процедуры верхнего уровня и вызываемых по цепочке процедур. У вызываемых процедур адреса локальных переменных будут пересекаться, что не страшно, т.к. они никогда не используются одновременно.
Но когда идёт комбинаторика вызовов, и компилятор не способен оттрасировать статическое распределение локальных переменных, он начинает организовывать динамические фреймы "ручного стека", что тяжело и дорого. И тогда программист закатывает рукава и выполняет такое распределение сам, если знает, что некие ф-ии не вызывают друг друга (даже сколь угодно косвенно) ни в каком из сценариев.
В этом случае идёт отказ от локальных переменных в пользу перекрывающихся глобальных.
Да, можно через union, но с той оговоркой, что ты не объявляешь такую глобальную переменную, а высчитываешь ее адрес "в уме", как показывал выше, где адрес будет константой времени компиляции.
V>>в гарвардской архитектуре инициализация "внутренней" памяти тоже нифига не автоматическая, требует некоторого кода.
ЕМ>Но не обязательно в CRT. Это может быть тот же загрузчик — хоть примитивный в МК, хоть навороченный в ОС.
Нет никакой ОС за ненадобностью.
После reset программа тупо исполняется с некоторого адреса.
V>>Допустим, не в БД, а в неоднородном NUMA-окружении, в неких суперкомпах.
V>>Под это есть свои Си-подобные диалекты, позволяющие декларативно описывать неоднородную память, дабы сохранять переносимость программ в исходниках.
ЕМ>Такое надо делать на плюсах, заворачивая в классы.
Повторю, для этого есть специальные языки для NUMA, которые выглядят как расширения к мейнстримовым.
Увы, весь современный мейнстрим создан для SMP-архитектур, и это одна из причин, почему в С++ не было библиотек для потоков — потому что SMP окончательно победила всех и вся только в нулевые, когда даже суперкомпы стали делать на обычных процах от персоналок.
V>>На асме, однако, приходилось писать критичные вещи, где надо расчитывать код с точностью до такта.
V>>Ну и еще для уменьшения вложенности вызова подпрограмм, т.к. на асме легко сделать в одну подпрограмму несколько точек входа
ЕМ>Все это успешно делается на "расширенных сях" с псевдопеременными, intrinsic-функциями, ассемблерными вставками и т.п.
Учитывая изкаробочную развитую модульность в Си/С++, ассемблерные вставки не всегда нужны.
Полноценный асм (макро-асм) на порядки удобнее убогих "вставок". ))
ЕМ>Если под архитектуру есть только "канонический" C, то вряд ли она сильно популярна.
Под любую архитектуру микриков, в первую очередь, есть асм.
Порой тулчейны не генерят непосредственно код из Си, а работают через промежуточный асм — оно зачастую и для отладки удобно.
V>>многие переменные могли покрывать адресное пространство друг друга в разные моменты работы программы), а компилятор справляется с этим плохо, т.к. не способен полностью трассировать программу.
ЕМ>Не понял, для чего здесь "трассировка". Эта задача решается сочетанием struct/union.
Зачем?
Вот у тебя есть локальные переменные.
В обычной архитектуре локальные переменные живут в стеке, а в микриках часто стек обслуживает только возвраты, а локальные данные живут во вручную организуемом стеке данных в ОЗУ.
Так вот, эта техника далека от эффективности.
В простейших случаях (и в отсутствии рекурсии) компиляторы способны статически распределить в ОЗУ локальные переменные с перекрытием.
Например, код верхнего уровня вызывает в разные моменты разные ф-ии, тогда компилятор статически распределит в ОЗУ адреса локальных переменных процедуры верхнего уровня и вызываемых по цепочке процедур. У вызываемых процедур адреса локальных переменных будут пересекаться, что не страшно, т.к. они никогда не используются одновременно.
Но когда идёт комбинаторика вызовов, и компилятор не способен оттрасировать статическое распределение локальных переменных, он начинает организовывать динамические фреймы "ручного стека", что тяжело и дорого. И тогда программист закатывает рукава и выполняет такое распределение сам, если знает, что некие ф-ии не вызывают друг друга (даже сколь угодно косвенно) ни в каком из сценариев.
В этом случае идёт отказ от локальных переменных в пользу перекрывающихся глобальных.
Да, можно через union, но с той оговоркой, что ты не объявляешь такую глобальную переменную, а высчитываешь ее адрес "в уме", как показывал выше, где адрес будет константой времени компиляции.