From: |
Valentin Nechayev 2:463/68.300 |
05 Sep 2021 00:04 +0300 |
To: |
Nil A 2:5015/46 |
|
Subject: |
Golang
|
Hi,
>>>> Nil A wrote:
EM>>> Они собираются это так и оставить, или просто еще не доделали
EM>>> оптимизацию?
VN>> Скорее всего "получилось как всегда" - нет ресурсов улучшить
VN>> кодогенерацию, а опираться на чужие наработки (вроде LLVM) не
VN>> хотят принципиально.
NA> Golang бьёт себя в грудь тем, что компилятор занимается "escape
NA> analysis", т.е. если у вас переменная внутри функции ни как не
NA> передаётся наружу, то и разместить её можно на стеке, а не в куче. И,
NA> якобы, всякие Явы, которые тоже GC языки, они по любому чиху будут в
NA> куче память брать, а Golang просечёт варианты когда можно обойтись.
Escape analysis в JVM существует уже лет 15.
В дотнете - сравнимо.
Вообще сложно найти серьёзный JIT, где его нет.
О чём ещё они там себя бьют в грудь, и чем?
-netch-
... Это были глаза профессора Плейшнера.
From: |
Nil A 2:5015/46 |
04 Sep 2021 20:50 +0300 |
To: |
Valentin Nechayev 2:463/68.300 |
|
Subject: |
Golang
|
Hello, Valentin!
Saturday September 04 2021 14:32, from Valentin Nechayev -> Eugene Muzychenko:
EM>> Они собираются это так и оставить, или просто еще не доделали
EM>> оптимизацию?
VN> Скорее всего "получилось как всегда" - нет ресурсов улучшить
VN> кодогенерацию, а опираться на чужие наработки (вроде LLVM) не хотят
VN> принципиально.
Golang бьёт себя в грудь тем, что компилятор занимается "escape analysis", т.е.
если у вас переменная внутри функции ни как не передаётся наружу, то и
разместить её можно на стеке, а не в куче. И, якобы, всякие Явы, которые тоже GC
языки, они по любому чиху будут в куче память брать, а Golang просечёт варианты
когда можно обойтись.
Best Regards, Nil
From: |
Nil A 2:5015/46 |
04 Sep 2021 20:37 +0300 |
To: |
Eugene Muzychenko 2:5000/14 |
|
Subject: |
C++ ошибки из конструктора
|
Hello, Eugene!
Friday September 03 2021 10:35, from Eugene Muzychenko -> Valentin Nechayev:
VN>> За счёт пложения boilerplate типа if err != nil { return nil,
VN>> err; } устранены грабельки типа исключения в любом неожиданном
VN>> месте. (Есть статистика за пару лет назад, что 42% проектов C++
VN>> собираются с выключенными исключениями.
EM> Вот я тридцать лет писал на C/C++ с возвратом ошибок, и очень не хотел
EM> использовать исключений (главным образом потому, что ядре NT они не
EM> поддерживаются, да и не нужны они там).
Есключения в C++ - единственный способ сообщить об ошибке в конструкторе.
Пример из головы, есть класс чтения и парсинга чего-то из файла. Удобно прямо
сразу в конструкторе указать имя файла и чтобы он всё там прочитал и распарсил.
Понятно, что если ты C++ со стажем, ещё с древних C-времём идёшь, то будут
отдельные функции load(), parse(), и все они будут возвращать код ошибки.
Но если ты пописал немного уже на чём-то модном-молодёжном, например, том же
питоне, то уже и сам начинаешь думать, а что если прям в конструкторе всё
вызывать и кинуть исключение, если что-то пошло не так.
Ещё мой наблюдения. Современные учебники по C++17 учат молодёжь возвращать
значения в виде std::optional, типа ты получаешь результат, или что-то пошло не
так, и получается "пусто". Но как сообщить, что именно не так пошло? Вот тогда
учебники говорят использовать std::variant - будет тебе либо значение, либо
класс ошибки.
И, якобы, std::variant возвращать более кашерно, чем std::tuple (или std::pair)
в формате (result, err). Это, собственно, чем и занимается Golang всю дорогу.
Best Regards, Nil
From: |
Valentin Nechayev 2:463/68.300 |
04 Sep 2021 14:39 +0300 |
To: |
Eugene Muzychenko 2:5000/14 |
|
Subject: |
Golang
|
Hi,
>>>> Eugene Muzychenko wrote:
VN>> При этом нет огромного количества граблей C/C++ с undefined
VN>> behavior.
EM> Эти грабли были бы практически незаметны, если б компиляторы
EM> изначально выдавали предупреждения на любой возможный случай UB, с
Я наблюдал несколько дискуссий на эту тему, например, на хабре. Есть голоса в
пользу того, что это просто невозможно. Hапример, у вас в коде есть простое
"i++;" по отношению к некоторому i типа int, отследить судьбу которого во всех
деталях - компилятору не хватает ресурсов или данных. Этот инкремент может
вызвать переполнение. Предупреждать о нём или нет?
Следующая версия компилятора стала чуть больше уметь и прихватила больше
контекста, и решила, что переполнение недопустимо и поэтому сузила расчётные
границы значений для i - она имела на это право или нет?
Или, приходит указатель в функцию, валидность которого неизвестна. Указатель
разыменовывается. Если указатель был некорректен, это UdB, но может ли
компилятор тут об этом знать?
Я считаю (и давно говорю), что вместо этого надо регулировать свойства действий
контекстом или уточнением действия. Контексты давно можно задавать, поэтому,
например, в случае i++ в контексте "исключение по переполнению" создаёт явную
проверку и генерацию ошибки. А если программист уверен в качестве всего своего
анализа конкретного блочка кода - пусть помечает его усиленными разрешениями.
EM> возможностью явно и _удобно_ указать в коде, что это предусмотрено
EM> (хотя бы прагмой, но без выписывания "сохранить режим предупреждений",
EM> "запретить одно предупреждение", "восстановить режим"). Когда
Для контекста давно есть пометки формата [[слова]], типа
[[intarith(relaxed)]]
int foo(int x1, int x2) {
...
}
Прагмой можно помечать режим до конца текущего блока, в которой прагма.
Hо это не для предупреждений, а собственно для того, что компилятор себе
позволяет, как минимум в темах: переполнения, алиасинга, пустого указателя.
EM> предупреждения о возможных проблемах, известные пятьдесят лет назад,
EM> появляются лишь пять-десять лет назад - это позорище.
VN>> возможности компилятору сделать свои далеко идущие выводы ХЗ из
VN>> чего - отсутствуют.
EM> И в таких случаях тоже нужны предупреждения.
Задолбётесь читать, десяток на каждую строчку кода.
<...>
EM> Подумываю над тем, чтобы прибить все это нах, и заменить на исключения
EM> (хотя бы в неядерном коде).
Да, вот потому они и были созданы.
-netch-
... Программная система "Медуза". Переименования файлов нет.
... Удаления файлов нет. Заполнена NOPами.
From: |
Valentin Nechayev 2:463/68.300 |
04 Sep 2021 14:32 +0300 |
To: |
Eugene Muzychenko 2:5000/14 |
|
Subject: |
Golang
|
Hi,
>>>> Eugene Muzychenko wrote:
VN>> У него вообще очень интересный вид, характерный, ни с чем не
VN>> спутаешь.
EM> Ему уже стандартизовали ABI, или пока делают кто во что горазд?
Там, где cgo должен взаимодействовать с C, используется C ABI целевой
архитектуры. В остальных местах - свой. Hе уверен, переносим ли внутренний ABI
между версиями компилятора. Так что "кто во что горазд" пока ближе.
VN>> Заполнение остатка до 16-байтной границы командами int3 (GCC,
VN>> Clang предпочитают делать длинный NOP).
EM> Int 3 таки правильнее - на случай, если управление вдруг попадет
EM> туда.
Может быть - но я про то, что это нетипично.
VN>> Если заглянуть в более длинный код, он практически весь состоит
VN>> из чтения со стека, короткой операции и записи обратно на стек.
VN>> Раскладка по регистрам за пределами одного предложения языка не
VN>> используется.
EM> Они собираются это так и оставить, или просто еще не доделали
EM> оптимизацию?
Скорее всего "получилось как всегда" - нет ресурсов улучшить кодогенерацию, а
опираться на чужие наработки (вроде LLVM) не хотят принципиально.
Именно что принципом является полностью своя экосистема, то есть компилятор,
линкер и стандартная библиотека свои, из ОС используется только
стандартизованный интерфейс (import libraries для Windows, сисколлы и небольшое
количество врапперов libc для Unix), и всё на своём языке и своём ассемблере.
Поэтому или кто-то перепишет LLVM на Go для них, или так и останется ещё
долго:)
Hо, кажется, их это ещё долго не будет волновать.
EM> Hу вот это и можно сделать на простом шаблонном классе. "Рабочий"
EM> указатель делается членом класса, а указатель на free передается в
EM> шаблон в качестве "нетипового" параметра.
Это если требуется освобождение указателя. Hо такие вещи в большинстве случаев
и так покрываются unique_ptr'ом.
А если действие типа закрытия временного файла с его удалением, или кидание
команды rollback в соединение с базой, то тут такие методы начинают выглядеть
слишком костыльными.
BOOST_SCOPE_EXIT ценна тем, что оформляет их в явном виде.
-netch-
... Кто здесь?????
From: |
Eugene Muzychenko 2:5000/14 |
03 Sep 2021 11:35 +0300 |
To: |
Valentin Nechayev 2:463/68.300 |
|
Subject: |
Golang
|
Привет!
02 Sep 21 13:42, you wrote to me:
VN> У него вообще очень интересный вид, характерный, ни с чем не спутаешь.
Ему уже стандартизовали ABI, или пока делают кто во что горазд?
VN> 3. Заполнение остатка до 16-байтной границы командами int3 (GCC, Clang
VN> предпочитают делать длинный NOP).
Int 3 таки правильнее - на случай, если управление вдруг попадет туда.
VN> Если заглянуть в более длинный код, он практически весь состоит из
VN> чтения со стека, короткой операции и записи обратно на стек. Раскладка
VN> по регистрам за пределами одного предложения языка не используется.
Они собираются это так и оставить, или просто еще не доделали оптимизацию?
EM>> C++ ее можно извращенным способом подставить в универсальный шаблон в
EM>> виде параметра-указателя.
VN> И так можно, наверно (не пробовал).
Я тоже не пробовал многих неочевидных шаблонных конструкций - каждый раз
удивляюсь, когда обнаруживаю что-то новое. Hо, блин, вся эта шаблонная магия -
извращение еще то, и выглядит, за редким исключением, ужасающе.
VN> Hо чаще требуется какая-то возня с локальными переменными, или хотя бы
VN> с передачей параметров - как конкретный указатель во free().
Hу вот это и можно сделать на простом шаблонном классе. "Рабочий" указатель
делается членом класса, а указатель на free передается в шаблон в качестве
"нетипового" параметра.
Всего доброго!
Евгений Музыченко
eu-gene@muzy-chen-ko.net (все дефисы убрать)
From: |
Eugene Muzychenko 2:5000/14 |
03 Sep 2021 11:35 +0300 |
To: |
Valentin Nechayev 2:463/68.300 |
|
Subject: |
Golang
|
Привет!
03 Sep 21 08:50, you wrote to Nil A:
VN> При этом нет огромного количества граблей C/C++ с undefined behavior.
Эти грабли были бы практически незаметны, если б компиляторы изначально
выдавали предупреждения на любой возможный случай UB, с возможностью явно и
_удобно_ указать в коде, что это предусмотрено (хотя бы прагмой, но без
выписывания "сохранить режим предупреждений", "запретить одно предупреждение",
"восстановить режим"). Когда предупреждения о возможных проблемах, известные
пятьдесят лет назад, появляются лишь пять-десять лет назад - это позорище.
VN> возможности компилятору сделать свои далеко идущие выводы ХЗ из чего -
VN> отсутствуют.
И в таких случаях тоже нужны предупреждения.
VN> За счёт пложения boilerplate типа if err != nil { return nil, err; }
VN> устранены грабельки типа исключения в любом неожиданном месте. (Есть
VN> статистика за пару лет назад, что 42% проектов C++ собираются с
VN> выключенными исключениями.
Вот я тридцать лет писал на C/C++ с возвратом ошибок, и очень не хотел
использовать исключений (главным образом потому, что ядре NT они не
поддерживаются, да и не нужны они там). Когда функции большие, это достаточно
удобно. Hо, начав массово выносить даже мелкие, но логически завершенные куски
кода в отдельные функции, обнаружил, что эти конструкции с проверкой и возвратом
ошибок стали встречаться слишком часто, отвлекая изрядную часть внимания. А с
увеличением количества уровней, возврата одного кода ошибки стало сильно не
хватать (что при этом получается - хорошо видно на примере Windows Update, когда
пользователь видит лишь универсальный код, поднятый из недр этого ужаса, и
выяснить суть проблемы можно только по логам, да и то не всегда).
Сперва хотел сделать какой-нибудь расширенный объект ошибки, и возвращать его
вместо кода, но так получится еще более громоздко, а в генерируемый код тогда
вообще лучше не смотреть, чтоб не пугаться. Подумываю над тем, чтобы прибить все
это нах, и заменить на исключения (хотя бы в неядерном коде).
Всего доброго!
Евгений Музыченко
eu-gene@muzy-chen-ko.net (все дефисы убрать)
From: |
Valentin Nechayev 2:463/68.300 |
03 Sep 2021 08:50 +0300 |
To: |
Nil A 2:5015/46 |
|
Subject: |
Golang
|
Hi,
>>>> Nil A wrote:
NA> Да-да, вижу тренд. Если на высоконагруженный бакэнд искали си++
NA> программистов ещё лет десять назад, потом всё более на Джаве писали,
NA> видимо, на плюсах себе все ноги отстрелили, плюс Джависты подешевле
NA> плюсов идут. А сейчас стартапчеги, где надо на бэкенде что-то
NA> посчитать, или много тыс.коннектов поддержать, и где на NodeJs
NA> пришлось бы слишком много "инстансев" покупать, то вот сейчас
NA> модно-молодёжно на гоу пейсать.
Hу в общем Go действительно неплохо занял нишу:
1. Компилируемый - несмотря на тупость компилятора и потерю производительности
на этом, наверно, в 2 раза. Есть какие-то оптимизации (хоть и ограниченные).
2. При этом нет огромного количества граблей C/C++ с undefined behavior.
Почти всё, что можно, определено. Определено не лучшим образом (например,
арифметика вся с усечением, а чтобы поймать переполнение - надо постараться), но
возможности компилятору сделать свои далеко идущие выводы ХЗ из чего -
отсутствуют.
3. AMM+GC, но при этом не короткими лавинами, а непрерывный (на обеспечение
вариантов типа "не больше 100 мкс остановки на один шаг GC" тратится много
усилий.
Порушить память - надо очень постараться (через разные unsafe).
4. Язык достаточно простой - нет ужасов типа "19 видов инициализации". Свои
грабли типа "этот nil не nil" менее масштабны и условно привычны.
5. За счёт пложения boilerplate типа if err != nil { return nil, err; }
устранены грабельки типа исключения в любом неожиданном месте. (Есть статистика
за пару лет назад, что 42% проектов C++ собираются с выключенными исключениями.
Это показывает, что проблема есть.)
6. Параллельность на горутинах - хоть и специфический подход, но вполне
подъёмен.
Есть своя специфика типа отсутствия привычно-полноценного наследования в ООП.
Hо, например, я вот смотрю на наши текущие проекты - в одном такое наследование
чуть ли не основа дизайна, а в другом его вообще нет и не предполагается.
Второй медленно мигрирует C->C++, но там и Go вполне бы подошло.
NA> Радует, что в Фейсбуке на Расте стало много проектов. Так глядишь они
NA> язычёк продвинут в разряд Industry standard. Я бы на Расте пейсал, он
NA> ближе к железу, без сборки мусора. Hо я не пейсал на Расте, пока
NA> проекта не нашлось, но хотелось бы попробовать на молодёжном языке.
:))
-netch-
... Вмале и узрите мя и паки вмале и не узрите мя.
From: |
Valentin Nechayev 2:463/68.300 |
02 Sep 2021 13:42 +0300 |
To: |
Eugene Muzychenko 2:5000/14 |
|
Subject: |
Golang
|
Hi,
>>>> Eugene Muzychenko wrote:
NA>>> Вернуть можно сразу несколько переменных
VN>> А вот кодогенератор при этом гоняет всё через стек, и
VN>> возвращаемые данные тоже.
EM> Это ж не обязательно. В тех же C/C++ на интелах для возврата
Я говорил именно про тот код, что генерирует компилятор Go.
У него вообще очень интересный вид, характерный, ни с чем не спутаешь.
Вот из докера, намеренно короткое:
0000000000bb9460 :
bb9460: 48 8b 5c 24 08 mov rbx,QWORD PTR [rsp+0x8]
bb9465: 48 8b 44 24 10 mov rax,QWORD PTR [rsp+0x10]
bb946a: 48 8b 4c 24 18 mov rcx,QWORD PTR [rsp+0x18]
bb946f: f0 48 0f b1 0b lock cmpxchg QWORD PTR [rbx],rcx
bb9474: 0f 94 44 24 20 sete BYTE PTR [rsp+0x20]
bb9479: c3 ret
bb947a: cc int3
bb947b: cc int3
bb947c: cc int3
bb947d: cc int3
bb947e: cc int3
bb947f: cc int3
1. Аргументы в стеке - 3 штуки.
2. Результат - в стеке (вот по смещению 0x20).
3. Заполнение остатка до 16-байтной границы командами int3 (GCC, Clang
предпочитают делать длинный NOP).
4. Обрати внимание на имя ассемблерного символа - такое норма для него, с
/ для пакетизации. Бывает с точкой, причём в исходном ассемблере это вообще не
точка, а U+00B7.
Если заглянуть в более длинный код, он практически весь состоит из чтения со
стека, короткой операции и записи обратно на стек. Раскладка по регистрам за
пределами одного предложения языка не используется. В результате вызов функции
это сплошь копирование на стеке, в стиле:
bb89ed: 48 8d 05 5b 21 5f 01 lea rax,[rip+0x15f215b]
#21aab4f
bb89f4: 48 89 04 24 mov QWORD PTR [rsp],rax
bb89f8: 48 c7 44 24 08 1a 00 mov QWORD PTR [rsp+0x8],0x1a
bb89ff: 00 00
bb8a01: e8 2a fa 02 00 call be8430
bb8a06: 48 8b 44 24 78 mov rax,QWORD PTR [rsp+0x78]
bb8a0b: 48 89 04 24 mov QWORD PTR [rsp],rax
bb8a0f: 48 8b 44 24 40 mov rax,QWORD PTR [rsp+0x40]
bb8a14: 48 89 44 24 08 mov QWORD PTR [rsp+0x8],rax
bb8a19: e8 12 fa 02 00 call be8430
и оно всё в таком духе.
VN>> чтобы не писать RAII класс на каждый чих.
EM> Если для освобождения ресурса достаточно вызова функции, то в C++ ее
EM> можно извращенным способом подставить в универсальный шаблон в виде
EM> параметра-указателя. VC++ для такого даже делает статическую линковку,
EM> если функция указана явно (именем).
И так можно, наверно (не пробовал).
Hо чаще требуется какая-то возня с локальными переменными, или хотя бы с
передачей параметров - как конкретный указатель во free().
-netch-
... Встрял перст в ноздрь...
From: |
Nil A 2:5015/46 |
30 Aug 2021 23:53 +0300 |
To: |
Eugene Muzychenko 2:5000/14 |
|
Subject: |
C++ RAII
|
Hello, Eugene!
Monday August 30 2021 22:27, from Eugene Muzychenko -> Valentin Nechayev:
VN>> чтобы не писать RAII класс на каждый чих.
EM> Если для освобождения ресурса достаточно вызова функции, то в C++ ее
EM> можно извращенным способом подставить в универсальный шаблон в виде
EM> параметра-указателя. VC++ для такого даже делает статическую линковку,
EM> если функция указана явно (именем).
Можно через unique_ptr прокрутить, например, вот так вот зафигачить:
auto file_open = [](const char* filename, const char* mode) -> FILE*
{
return std::fopen(filename, mode);
};
auto file_deleter=[](FILE* file) {
std::puts("Close file\n");
std::fclose(file);
};
std::unique_ptr fp{file_open("test.txt", "r"),
file_deleter};
Best Regards, Nil