• Сделаем Windows медленнее! Часть первая: файловый доступ

    • Перевод
    imageОС Windows долгое время попрекали за медлительность её файловых операций и медленное создание процессов. А почему бы не попробовать сделать их ещё более медленными? Эта статья покажет способы замедления файловых операций в Windows примерно в 10 раз от их нормальной скорости (или даже больше), причём способы эти практически не поддаются отслеживанию обычным пользователем.

    А ещё, конечно же, мы научимся подобные ситуации обнаруживать и исправлять. Весь текст написан на основе проблемы, с которой я столкнулся пару месяцев назад, так что всё, написанное ниже, полностью реально.
    Читать дальше →
    • +70
    • 32,7k
    • 9
  • Как передать полиморфный объект в алгоритм STL

    • Перевод
    Как мы можем прочесть в первой главе книги Effective C++, язык С++ является по сути своей объединением 4 разных частей:

    • Процедурная часть, доставшаяся в наследство от языка С
    • Объектно-ориентировання часть
    • STL, пытающийся следовать функциональной парадигме
    • Шаблоны

    Эти четыре, по сути, подъязыка составляют то, что мы называем единым языком С++. Поскольку все они объединены в одном языке, то это даёт им возможность взаимодействовать. Это взаимодействие порой порождает интересные ситуации. Сегодня мы рассмотрим одну из них — взаимодействие объектно-ориентированной модели и STL. Оно может принимать разнообразные формы и в данной статье мы рассмотрим передачу полиморфных функциональных объектов в алгоритмы STL. Эти два мира не всегда хорошо контачат, но мы можем построить между ними достаточно неплохой мостик.

    image
    Читать дальше →
    • +32
    • 6,5k
    • 6
  • Ускорение перечисления процессов и потоков в ОС Windows

      Иногда бывает нужно перечислить все процессы или потоки, которые в данный момент работают в ОС Windows. Это может понадобиться по разным причинам. Возможно, мы пишем системную утилиту вроде Process Hacker, а может быть мы хотим как-то реагировать на запуск/остановку новых процессов или потоков (писать лог, проверять их, внедрять в них свой код). Самым правильным способом это реализовать является, конечно же, написание драйвера. Там всё просто — используем PsSetCreateProcessNotifyRoutine и PsSetCreateThreadNotifyRoutine для установки колбек-функций, которые будут вызываться при запуске/остановке процессов и потоков. Работает очень быстро и не ест ресурсы. Именно так и делают все серьёзные инструменты. Но разрабатывать драйвера — не всегда подходящий способ. Их нужно уметь правильно писать, их с недавних пор обязательно нужно подписывать сертификатами (что не бесплатно) и регистрировать в Microsoft (что не быстро). И ещё их не удобно распространять — например, программы с ними нельзя выкладывать в Microsoft Store.

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

      Самая большая проблема здесь — это производительность. Связка CreateToolhelp32Snapshot() + Process32First() + Process32Next() работает ну очень медленно. Возможно, проблема лежит где-то в той же области, что и описанная вот в этой статье проблема с Heap32First() + Heap32Next(). Кратко — в силу исторических причин кое-где проход по линейному списку занимает квадратичное время.

      Можно ли как-то всё это ускорить? Можно. Но придётся сойти со светлого пути использования одних лишь публичных функций WinAPI.
      Читать дальше →
      • +30
      • 14,7k
      • 5
    • Почему функция Heap32Next() работает так медленно на Windows 7?

      • Перевод
      Если вы занимаетесь системным программированием под Windows, то могли бы заметить, что весьма полезные функции Heap32First/Heap32Next и другие из того же семейства стали работать существенно медленнее начиная с Windows 7. Что же с ними случилось?

      Давайте перенесёмся в далёкий 1992 год. Разрабатывается Windows 3.1. Одним из новых компонентов в ней стал ToolHelp. Он позволил немного покопаться во внутренностях ядра ОС. Для нас в нём наиболее интересны функции, позволяющие просматривать данные в куче (heap). Поскольку Windows 3.1 использовала кооперативную многозадачность, вызывая подобные функции можно было быть уверенным в том, что содержимое кучи не изменится между вызовами HeapFirst и HeapNext, ведь у ОС не было права прервать выполнение процесса и переключить контекс на выполнение другого. Вот были времена!
      Читать дальше →
    • Баг компилятора? Линкера? Нет, баг ядра Windows

      • Перевод
      imageГейзенбаг — это худшее, что может произойти. В описанном ниже исследовании, которое растянулось на 20 месяцев, мы уже дошли до того, что начали искать аппаратные проблемы, ошибки в компиляторах, линкерах и делать другие вещи, которые стоит делать в самую последнюю очередь. Обычно переводить стрелки подобным образом не нужно (баг скорее всего у вас в коде), но в данном случае нам наоборот — не хватило глобальности виденья проблемы. Да, мы действительно нашли баг в линкере, но кроме него мы ещё нашли и баг в ядре Windows.

      В сентябре 2016 года мы стали замечать случайно происходящие ошибки при сборке Хрома — 3 билда из 200 провалились из-за крэша процесса protoc.exe. Это один из бинарников, который при сборке Хрома сначала собирается сам, а затем запускается для генерации заголовочных файлов других компонентов. Но вместо этого он падал с ошибкой «access violation».
      Читать дальше →
    • Зомби, которые съедают вашу память

      • Перевод
      • Tutorial
      Что бы вы там себе не думали, а зомби существуют. И они действительно едят мозги. Не человеческие, правда, а компьютерные. Я говорю сейчас о зомби-процессах и потребляемых ими ресурсах. Это будет душераздирающая история о потерянных и снова найденных 32 ГБ оперативной памяти. Возможно, лишь некоторые из вас столкнутся с точно такой же проблемой, но если вдруг это произойдёт — у вас хотя бы будет шанс понять, что происходит.

      Начнём с того, что компьютеры под управлением ОС Windows склонны со временем терять память. Ну, по крайней мере, у меня, при моём способе ими пользоваться. После пары недель без перезагрузок (или, например, всего одного уикэнда за который я 300 раз пересобрал Хром) я стал замечать, что диспетчер задач начинает показывать мне очень маленькое количество свободной оперативной памяти, но в то же время в системе нет никаких процессов, которые эту самую память активно используют. В том примере выше (с 300 сборками Хрома) диспетчер задач сказал мне, что в системе занято 49.8 ГБ плюс ещё 4.4 ГБ памяти сжато — но при этом запущено всего несколько процессов, и все они в сумме даже и близко не используют столько памяти:

      image

      В моём компьютере 96 ГБ оперативной памяти (да, я счастливчик) и когда у меня нет вообще никаких запущенных процессов — я, знаете ли, хотел бы видеть ну хотя бы половину этой памяти свободной. Я правда рассчитываю на это. Но иногда этого достичь не удаётся и мне приходится перезагружать ОС. Ядро Windows написано качественно и надёжно (без шуток), так что память не должна бы пропадать бесследно. Но всё же она пропадает.
      Читать дальше →
    • Миром всё ещё управляет язык С

      • Перевод
      Многие из проектов на языке С, существующих сегодня, начинали разрабатываться ещё десятилетия назад. Операционная система UNIX стартовала 1969 году (и писалась на ассемблере), но уже в 1972 была переписана на С. Точнее, это язык С был создан для того, чтобы появилось что-то, на что было бы удобно переписать с ассемблера ядро UNIX и получить чуть более высокоуровневый код, менее зависимый от архитектуры и позволяющий выполнять больше полезной работы на каждую строчку написанного кода.

      Разработка базы данных Oracle началась в 1977 году (тоже на ассемблере) и тоже была переписана на С в 1983 году. К тому времени это был уже один из самых популярных языков в мире.

      В 1985 году вышла Windows 1.0. Хотя код операционной системы Windows не является открытым, общеизвестно, что ядро в основном написано на С с небольшими вставками ассемблера. Разработка Linux началась в 1991 году и началась сразу на С. В следующем году она была опубликована под лицензией GPL и использована как часть GNU Operating System, которая и сама начиналась как проект на С и Lisp, так что многие компоненты были написаны на С.

      Но проекты на С — это не только то, что стартовало десятилетия назад, когда выбор языков, скажем прямо, был достаточно ограничен. Много С-кода пишется и сейчас, на нём начинаются и новые проекты. Для этого есть причины.

      Как именно язык С управляет миром?

      Читать дальше →
    • Выравнивание инструкций кода

      • Перевод
      Насколько трудно может быть измерить производительность простой функции, вроде вот этой?

      // func.cpp
      void benchmark_func(int* a)
      {
      	for (int i = 0; i < 32; ++i)
      		a[i] += 1;
      }

      Ну, давайте просто завернём её в какой-нибудь микробенчмарк, вызовем её много-много раз (для усреднения результатов) и посмотрим, что получится, да? Ну ладно, мы можем ещё посмотреть на сгенерированные инструкции просто чтобы убедиться, что компилятор чего-то там не «наоптимизировал». Мы можем также провести несколько разных тестов, чтобы убедиться, что именно цикл является узким местом. Ну и всё. Мы понимаем, что мы измеряем, да?

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

      // func.cpp
      void foo(int* a)
      {
      	for (int i = 0; i < 32; ++i)
      		a[i] += 1;
      }
      
      void benchmark_func(int* a)
      {
      	for (int i = 0; i < 32; ++i)
      		a[i] += 1;
      }

      И вот однажды ваш менеджер приходит к вам и показывает претензию от пользователя вашей библиотеки, которая заключается в том, что она не работает настолько быстро, как вы обещали. Но постойте, мы ведь хорошо измерили производительность и обещали ровно то, что получили по результатам тестов. Что же пошло не так?
      Читать дальше →
    • Стратегия ветвления ThreeFlow

      • Перевод
      Из всех моих разговоров с коллегами о разных аспектах разработки программного обеспечения одна тема всплывает чаще других. Да что там «чаще» — она повторяется снова и снова, как заезженная пластинка — это беседы на тему того, чем плох GitFlow и почему его стоит избегать.

      Статья "Удачная модель ветвления для Git" описывающая метод, получивший в последствии название «GitFlow» стала де-факто стандартом того, как нужно начинать использовать Git в вашем проекте. Если поискать в Google что-то типа "git branching strategy" то вот как раз этот метод будет описан по первой ссылке (а скорее всего и по нескольким следующим).

      Лично я ненавижу GitFlow и за последние годы убедил много команд разработчиков перестать его использовать, чем, как мне кажется, сохранил им уйму времени и нервов. GitFlow заставляет команды организовывать управление изменениями кода хуже, чем оно может быть реализовано. Но поскольку это такой популярный метод (по крайней мере в результатах поисковика), то команды без достаточного опыта, которые ищут «что-то, хотя бы как-то работающее» находят именно его при быстром поиске, да ещё и видят слово «успешный» прямо в заголовке статьи с его описанием — ну и начинают бездумно использовать. Я хочу хотя бы немного изменить этот паттерн поведения, описав в этой статье более простую и не менее успешную стратегию использования веток Git, которую я внедрил во многих командах. Часто эти команды пробовали использовать GitFlow, но испытывали проблемы, которые, пропали с переходом на ThreeFlow.

      Я называю эту стратегию ThreeFlow потому, что в ней есть ровно три ветки. Не четыре. Не две. Три.
      Читать дальше →
    • Уделяйте внимание людям, а не технологиям

      • Перевод
      На моей первой работе в роли программиста у меня ушло три недели на то, чтобы полностью настроить рабочее окружение. Я был всего лишь вторым программистом в данной компании (а первый уволился — меня потому и взяли). Никакой документации, никакой передачи знаний. Всё пришлось изобретать самому.

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

      Сегодня есть люди, которые заявляют что-то вроде «мы используем Docker для настройки окружения, так что включить нового человека в проект занимает меньше часа».

      С одной стороны, я, конечно, рад за новых разработчиков, которым не нужно проходить через те боль и страдания, которые в своё время прошел я. Но с другой стороны «настроенное рабочее окружение» ещё совершенно не равно «разработчику, вовлечённому в проект». Чтобы работать продуктивно, человеку нужно нечто большее, чем просто компьютер, настроенный под разработку данного проекта.
      Читать дальше →
      • +16
      • 7,1k
      • 4