> но что-то никто не обратил внимание на такую мысль: vim — это слепой десятипальцевый метод редактирования текста.
Это важно только для тех, кому важен собственно слепой десятипальцевый. А таких, по крайней мере у нас, не очень много. И для программирования это не обязательно. Не хочу поднимать отдельный флейм, но один знакомый успешный и даже знаменитый программист набирает двумя пальцами и предпочитает mcedit :)
Для единообразия работы в гуе берутся таки гуевые средства (тот же gvim).
А Ctrl+C, Ctrl+V это конфликтные шорткаты — в отличие от Ctrl+Ins, Shift+Ins.
> Пирамидальная (heap) сортировка в худшем случае O(n*log(n)), да еще и стабильна.
Если «стабильна» это сохранение порядка равных элементов, то этого свойства как раз у пирамидальной никогда не было.
> быстрая сортировка (quick sort), модификацией которой является представленный алгоритм, имеет временную сложность O(n^2) в худшем случае
Только если её неправильно реализовать. Даже случайный выбор разделяющего элемента асимптотически снижает вероятность такого до нуля. С помощью медианы медиан получается гарантированное O(n log n), причём всё равно дешевле в большинстве окружений, чем пирамидальная.
Если авторы не знают этой возможности гарантировать время для быстрой сортировки, то что они вообще знают, простите?
> Может показаться, что глобальная очередь имеет преимущество перед локальной, но регулярная проверка глобальной очереди критична для избежания M использования только горутин из локальной очереди.
Непонятно, к чему «но», если вторая часть не противоречит первой. Надо было вводным сказать что-то вроде «Возможно, вас удивляет, что...»? Тут странность в исходном тексте.
> Постоянное упреждение (preemption)
Перевод как «упреждение» неадекватный, в данном контексте для preemption принятое и понятное слово — «вытеснение».
> В любой момент M горутин должны быть распределены между N потоками ОС
Конфликтует с последующим использованием M как системного треда (которых N, как раз по этой цитате). Лучше было бы заменить буквы, их, к счастью, достаточно :)
По содержанию:
Вообще не видно связи между выбором между work-sharing и work-stealing, и оптимизацией шедулера, точно так же как причин их жёстко противопоставлять друг другу.
Work-stealing это метод, неизбежный всегда, потому что ситуация, когда стоят ждущие процессора задачи и процессор освобождается (а другие работают и не должны отвлекаться) — типична и происходит в реальной загруженной машине много раз в секунду. «Мы сделали work stealing» звучит, поэтому, примерно как «мы наконец-то включили мозг при написании шедулера». Это, конечно, хорошо, но вряд ли заслуживает особой публикации.
Избавление от центрального мьютекса и lock-free — уже ближе к интересному (но тут популярное описание от самого Вьюкова было бы ценнее, эта тема — его давний конёк).
А вот много других интересных особенностей тут явно упущено. Например, метод и качество определения факта, что M в блокирующем системном вызове или аналогичной библиотечной функции, и реакции на неё. Согласование этого с FFI. Есть ли деление на блокирующие и неблокирующие FFI вызовы, какая граница между ними? Планируют ли они просить scheduler activations, как поддержку от ядра ОС? Ещё, есть проблема (тут, например, она под номером 58) горутин, которые не отдают управление. Элементарно забить такими все GOMAXPROCS, или намеренно, или случайно при больших вычислениях. Собираются ли решать её?
> Например, менеджер памяти реализован так, что хранит информацию об объектах разного размера в разных таблицах и использовать разные пулы памяти.
Да, это аргумент (хотя такой реализации для C/C++ ни разу вживую не видел).
Тогда это надо переделать на malloc/free. Использование же через общий sockaddr* остаётся основным методом.
> Так что анализатор ведёт себя правильно и ничего в нём исправлять не надо.
Но про контроль обнуления структур рекомендую таки подумать.
> Это не тот argv, который приходит в main. Это нечто самобытное:
Я и не говорил, что это входной argv данного процесса. Это, скорее всего, выходной, для передачи в какой-то из exec*(). И он обязан завершаться NULLʼом, иначе дочерний процесс не запустится правильно.
> Я неприятно удивлю, но компиляторы до сих пор проглатывают такой код.
> Создаются структуры типа sockaddr_un и sockaddr_in. При этом хранятся и уничтожаются они как структуры типа sockaddr.
> Надо понимать, что рассмотренный код очень опасен и достаточно, чтобы в одном из классов появился конструктор/деструктор или был добавлен член сложного типа (например std::string), как всё сразу сломается окончательно.
Появление конструктора в sockaddr_* это из области невероятной фантастики — это гарантированно плоские структуры данных.
Код может быть тут сколько угодно формально некорректным, исходя из формальности типа «если new, то вероятен конструктор», но сама эта формальность неуместна.
Более того, если бы это случилось, то удаление объекта через указатель на базовый класс с виртуальным деструктором — нормальная ситуация: если B и C — потомки A, а A имеет виртуальный деструктор, то код вида
A* a1 = new B();
A* a2 = new C();
...
delete a1;
delete a2;
законен.
Так что тут ваша проверка некорректна, рекомендую исправить. Для семейства sockaddr лучше вообще добавить исключение (работа через sockaddr* — общее правило BSD sockets API), а в общем случае ловить удаление через базовый класс без виртуального конструктора.
А вот что я бы рекомендовал добавить — именно для sockaddr_* (всех адресных семейств) — это или использование calloc(), или заполнение структуры нулевыми байтами перед использованием. Отсутствие такого заполнения может приводить к странным эффектам при переносе между ОС: например, для BSD семейства (включая MacOS) sin_len (отсутствующее в Linux) должно быть или 0, или актуального размера.
Также это касается ряда других интерфейсных структур — с ходу вспоминается sigaction. Можно вообще поставить общим правилом для ядерных структур.
> Для таких случаев есть стандартный макрос M_PI, который вдобавок раскрывается в более точное значение.
M_PI не определён ни в стандарте C, ни в базовом Posix; есть в Posix расширении XSI и в Microsoft SDK. Утверждение, что он стандартен, распространено, но неверно. Эта диагностика также требует исправления.
> snprintf(buf + strlen(buf), sizeof(buf),
Против такого лучший идиоматический приём выглядит примерно так:
// удобно, потому что blimit уже сдвигаться не будет
const char *blimit = buf + sizeof buf;
// а вот этот едет каждый раз
const char *bpos = buf;
...
r = snprintf(bpos, blimit - bpos, порция1);
// привет Шлемиэлю, но его подвиг мы не повторяем
// также компилятор может соптимизировать blimit-bpos в одно вычисление
bpos += min(r, blimit - bpos);
r = snprintf(bpos, blimit - bpos, порция2);
bpos += min(r, blimit - bpos);
...
Хотя для короткого буфера и двух записей хватит и такого strlen.
> Здесь забыты фигурные скобки.
Вообще я бы рекомендовал настаивать на включении тел if, else, while в фигурные скобки в принудительном порядке везде и всегда. Новые языки (Swift, Go) это уже делают в основах синтаксиса.
> argv2[i] = NULL;
> Не будут обнулены указатели.
> NULL будет записан за границу массива.
Обнуление не обязательно. NULL, скорее всего, не будет записан за границу, потому что массив argv в Unix-стиле передачи аргументов другим процессам всегда заканчивается ещё одним NULL, не указанным в arg_count (argc — для main).
Тут жалоба, я уверен, ложна, хотя определить это по самому коду может быть слишком сложно для анализатора.
> The 'strncat' function call could lead to the 'dd_info->object_uri' buffer overflow.
И опять strlen (см. выше), но strncat это вообще диверсия почти всегда.
В случае Unix мира лучше рекомендовать переходить на strlcat, а в общем случае — на strncat_s.
> Предупреждение PVS-Studio: V591 Non-void function should return a value.
Нормальный компилятор это вообще должен считать за ошибку. Этот код, подозреваю, вообще не компилируют.
Вы не о том. Я всё время говорю о преимуществе прямой адресации fp регистров, а не через стек, а вы — о проблемах двух чипов и общения с памятью.
FPU и так читает из памяти и команды, и данные, и пишет в основном в память (не в CPU). Ну читали бы в fp2 напрямую, а не на вершину стека — то же самое, но проще.
А вот лучше ли давать сопроцессору общаться с памятью напрямую или через CPU — вопрос уже другой. В MIPS, ARM и т.п. общаются только через CPU. Но я не знаю их мотивации в этом случае, да и делалось это всё на 10+ лет позже.
> В итоге, т.к. два чипа висели на одном потоке команд нужно было как-то данные сохранять в сопроцессоре для обработке (там же ещё операции долгие и основной поток команд мог выполнятся параллельно на основном процессоре).
Передача в фиксированный регистр и обратно всё равно быстрее, и это делалось хотя бы для управляющего регистра.
Всё равно непонятно…
Ну есть задача, надо её реализовать, выхлоп очевиден. Считают, что по силам написать FS. Почему нет? Может, и доведут до ума.
Я не понял, почему aufs так пострадала — но VFS в Linux это вообще очень странный предмет. ;(
Для пол-терабайта в первую неделю, мне кажется, можно и потребовать дополнительные деньги. Это уже чисто коммерческий вопрос — сколько взять, чтобы отсечь халявщиков системы «выкачал и отключился», но не ограничить перспективных.
Та же Воля даёт «гиперскорость» без ограничения вообще и без учёта в обычный трафик на 1 сутки за ~0.2 USD.
Пример:
День 1: только подключились, запаса 0. В полночь добавили 33GB, потратили за сутки 2, остаток 31.
День 2: В полночь добавили 33GB, потратили за сутки 10, остаток 54.
День 3: В полночь добавили 33GB, потратили за сутки 60, остаток 27.
…
День N+1: Было остатка 900GB, в полночь добавили 33GB, потратили за сутки 933GB, остаток 0.
… и так далее…
Но, если остаток на момент добавления превышает 1000GB — он урезается вниз до 1000GB. То есть неограниченное накопление невозможно, выше месячного лимита не растёт.
Ну а если потрачено до нуля — там уже меры как в исходной статье — ограничили до 64Kbit/s или похожего.
Подобная ситуация была несколько лет назад с «Воля-кабель» — ввели ограничения на месячный трафик под предлогом «гарантии честного использования». В основном прошло тихо, но часть клиентов ушла — например, мой шеф сурово на них обиделся и перешёл к конкуренту.
В предложенном (представим себе, что эти ограничения таки осмысленны и будут введены) самое неверное это дробление по месяцу. По-нормальному должна быть система token bucket с пределом пусть тот же 1TB и начислением порции не реже раза в сутки. 33GB в сутки, 1.375GB в час — насколько часто смогут, настолько пусть и будет. В таком случае превышение затормозит использование на короткое время, беспроблемное для большинства; и это не помешает «рывком» взять те же 1TB, если надо, и есть неиспользованное.
Один огромный недостаток — объяснять эту схему не-ITшнику сложно ;( так что вводить её только по желанию клиента. (Ой, сочувствую тому биллингу.)
Control & status передаются в FPU и обратно без всякого стека. Значит, могли, если хотели.
Тут всё-таки идеологические соображения. Но непонятно, какие именно.
Да, но при жёсткой дисциплине на их использование. Например, сложение-вычитание с разным m недопустимо; при умножении и делении надо уточнять m результата; присвоение с другим m тоже недопустимо без явной конверсии с указанием правила округления… В сумме таких правил накапливается столько, что часто легче явно вести в целых, а масштаб (то самое m) подразумевать контекстом.
Это важно только для тех, кому важен собственно слепой десятипальцевый. А таких, по крайней мере у нас, не очень много. И для программирования это не обязательно. Не хочу поднимать отдельный флейм, но один знакомый успешный и даже знаменитый программист набирает двумя пальцами и предпочитает mcedit :)
А Ctrl+C, Ctrl+V это конфликтные шорткаты — в отличие от Ctrl+Ins, Shift+Ins.
А где пруфы на Вашу позицию?
> Сложность процессоров увеличилась в 1000 раз, количество ошибок увеличилось в 1000 раз?
Это вопрос кому?
Если «стабильна» это сохранение порядка равных элементов, то этого свойства как раз у пирамидальной никогда не было.
> быстрая сортировка (quick sort), модификацией которой является представленный алгоритм, имеет временную сложность O(n^2) в худшем случае
Только если её неправильно реализовать. Даже случайный выбор разделяющего элемента асимптотически снижает вероятность такого до нуля. С помощью медианы медиан получается гарантированное O(n log n), причём всё равно дешевле в большинстве окружений, чем пирамидальная.
Если авторы не знают этой возможности гарантировать время для быстрой сортировки, то что они вообще знают, простите?
> Может показаться, что глобальная очередь имеет преимущество перед локальной, но регулярная проверка глобальной очереди критична для избежания M использования только горутин из локальной очереди.
Непонятно, к чему «но», если вторая часть не противоречит первой. Надо было вводным сказать что-то вроде «Возможно, вас удивляет, что...»? Тут странность в исходном тексте.
> Постоянное упреждение (preemption)
Перевод как «упреждение» неадекватный, в данном контексте для preemption принятое и понятное слово — «вытеснение».
> В любой момент M горутин должны быть распределены между N потоками ОС
Конфликтует с последующим использованием M как системного треда (которых N, как раз по этой цитате). Лучше было бы заменить буквы, их, к счастью, достаточно :)
По содержанию:
Вообще не видно связи между выбором между work-sharing и work-stealing, и оптимизацией шедулера, точно так же как причин их жёстко противопоставлять друг другу.
Work-stealing это метод, неизбежный всегда, потому что ситуация, когда стоят ждущие процессора задачи и процессор освобождается (а другие работают и не должны отвлекаться) — типична и происходит в реальной загруженной машине много раз в секунду. «Мы сделали work stealing» звучит, поэтому, примерно как «мы наконец-то включили мозг при написании шедулера». Это, конечно, хорошо, но вряд ли заслуживает особой публикации.
Избавление от центрального мьютекса и lock-free — уже ближе к интересному (но тут популярное описание от самого Вьюкова было бы ценнее, эта тема — его давний конёк).
А вот много других интересных особенностей тут явно упущено. Например, метод и качество определения факта, что M в блокирующем системном вызове или аналогичной библиотечной функции, и реакции на неё. Согласование этого с FFI. Есть ли деление на блокирующие и неблокирующие FFI вызовы, какая граница между ними? Планируют ли они просить scheduler activations, как поддержку от ядра ОС? Ещё, есть проблема (тут, например, она под номером 58) горутин, которые не отдают управление. Элементарно забить такими все GOMAXPROCS, или намеренно, или случайно при больших вычислениях. Собираются ли решать её?
Да, это аргумент (хотя такой реализации для C/C++ ни разу вживую не видел).
Тогда это надо переделать на malloc/free. Использование же через общий sockaddr* остаётся основным методом.
> Так что анализатор ведёт себя правильно и ничего в нём исправлять не надо.
Но про контроль обнуления структур рекомендую таки подумать.
> Это не тот argv, который приходит в main. Это нечто самобытное:
Я и не говорил, что это входной argv данного процесса. Это, скорее всего, выходной, для передачи в какой-то из exec*(). И он обязан завершаться NULLʼом, иначе дочерний процесс не запустится правильно.
> Я неприятно удивлю, но компиляторы до сих пор проглатывают такой код.
MSVC, да?
> Надо понимать, что рассмотренный код очень опасен и достаточно, чтобы в одном из классов появился конструктор/деструктор или был добавлен член сложного типа (например std::string), как всё сразу сломается окончательно.
Появление конструктора в sockaddr_* это из области невероятной фантастики — это гарантированно плоские структуры данных.
Код может быть тут сколько угодно формально некорректным, исходя из формальности типа «если new, то вероятен конструктор», но сама эта формальность неуместна.
Более того, если бы это случилось, то удаление объекта через указатель на базовый класс с виртуальным деструктором — нормальная ситуация: если B и C — потомки A, а A имеет виртуальный деструктор, то код вида
законен.
Так что тут ваша проверка некорректна, рекомендую исправить. Для семейства sockaddr лучше вообще добавить исключение (работа через sockaddr* — общее правило BSD sockets API), а в общем случае ловить удаление через базовый класс без виртуального конструктора.
А вот что я бы рекомендовал добавить — именно для sockaddr_* (всех адресных семейств) — это или использование calloc(), или заполнение структуры нулевыми байтами перед использованием. Отсутствие такого заполнения может приводить к странным эффектам при переносе между ОС: например, для BSD семейства (включая MacOS) sin_len (отсутствующее в Linux) должно быть или 0, или актуального размера.
Также это касается ряда других интерфейсных структур — с ходу вспоминается sigaction. Можно вообще поставить общим правилом для ядерных структур.
> Для таких случаев есть стандартный макрос M_PI, который вдобавок раскрывается в более точное значение.
M_PI не определён ни в стандарте C, ни в базовом Posix; есть в Posix расширении XSI и в Microsoft SDK. Утверждение, что он стандартен, распространено, но неверно. Эта диагностика также требует исправления.
> snprintf(buf + strlen(buf), sizeof(buf),
Против такого лучший идиоматический приём выглядит примерно так:
Хотя для короткого буфера и двух записей хватит и такого strlen.
> Здесь забыты фигурные скобки.
Вообще я бы рекомендовал настаивать на включении тел if, else, while в фигурные скобки в принудительном порядке везде и всегда. Новые языки (Swift, Go) это уже делают в основах синтаксиса.
> argv2[i] = NULL;
> Не будут обнулены указатели.
> NULL будет записан за границу массива.
Обнуление не обязательно. NULL, скорее всего, не будет записан за границу, потому что массив argv в Unix-стиле передачи аргументов другим процессам всегда заканчивается ещё одним NULL, не указанным в arg_count (argc — для main).
Тут жалоба, я уверен, ложна, хотя определить это по самому коду может быть слишком сложно для анализатора.
> The 'strncat' function call could lead to the 'dd_info->object_uri' buffer overflow.
И опять strlen (см. выше), но strncat это вообще диверсия почти всегда.
В случае Unix мира лучше рекомендовать переходить на strlcat, а в общем случае — на strncat_s.
> Предупреждение PVS-Studio: V591 Non-void function should return a value.
Нормальный компилятор это вообще должен считать за ошибку. Этот код, подозреваю, вообще не компилируют.
Вы не о том. Я всё время говорю о преимуществе прямой адресации fp регистров, а не через стек, а вы — о проблемах двух чипов и общения с памятью.
FPU и так читает из памяти и команды, и данные, и пишет в основном в память (не в CPU). Ну читали бы в fp2 напрямую, а не на вершину стека — то же самое, но проще.
А вот лучше ли давать сопроцессору общаться с памятью напрямую или через CPU — вопрос уже другой. В MIPS, ARM и т.п. общаются только через CPU. Но я не знаю их мотивации в этом случае, да и делалось это всё на 10+ лет позже.
Передача в фиксированный регистр и обратно всё равно быстрее, и это делалось хотя бы для управляющего регистра.
Всё равно непонятно…
Ну есть задача, надо её реализовать, выхлоп очевиден. Считают, что по силам написать FS. Почему нет? Может, и доведут до ума.
Я не понял, почему aufs так пострадала — но VFS в Linux это вообще очень странный предмет. ;(
Та же Воля даёт «гиперскорость» без ограничения вообще и без учёта в обычный трафик на 1 сутки за ~0.2 USD.
День 1: только подключились, запаса 0. В полночь добавили 33GB, потратили за сутки 2, остаток 31.
День 2: В полночь добавили 33GB, потратили за сутки 10, остаток 54.
День 3: В полночь добавили 33GB, потратили за сутки 60, остаток 27.
…
День N+1: Было остатка 900GB, в полночь добавили 33GB, потратили за сутки 933GB, остаток 0.
… и так далее…
Но, если остаток на момент добавления превышает 1000GB — он урезается вниз до 1000GB. То есть неограниченное накопление невозможно, выше месячного лимита не растёт.
Ну а если потрачено до нуля — там уже меры как в исходной статье — ограничили до 64Kbit/s или похожего.
Так вариант, что я описываю, позволяет разово взять аж до месячного лимита.
В предложенном (представим себе, что эти ограничения таки осмысленны и будут введены) самое неверное это дробление по месяцу. По-нормальному должна быть система token bucket с пределом пусть тот же 1TB и начислением порции не реже раза в сутки. 33GB в сутки, 1.375GB в час — насколько часто смогут, настолько пусть и будет. В таком случае превышение затормозит использование на короткое время, беспроблемное для большинства; и это не помешает «рывком» взять те же 1TB, если надо, и есть неиспользованное.
Один огромный недостаток — объяснять эту схему не-ITшнику сложно ;( так что вводить её только по желанию клиента. (Ой, сочувствую тому биллингу.)
Тут всё-таки идеологические соображения. Но непонятно, какие именно.