Горе от ума, или Почему отличники пишут непонятный код

    У большинства из нас были отличные оценки по математике в школе и в универе. Помните, как мы решали примеры? Скажем, нужно взять производную от функции:

    $f(x) = \frac{\ln{x}}{x^2}$


    Мы задумывались на несколько секунд и записывали готовый результат:

    $f'(x) = \frac{1-2ln{x}}{x^3}$


    Ученики послабее записывали решение по шагам и тратили существенно больше времени:

    $f'(x) = \left(\frac{ln{x}}{ x^2}\right)' = \frac{(ln{x})' \cdot x^2 - ln{x} \cdot (x^2)'}{(x^2)^2} =\\ \frac{\frac{1}{x} \cdot x^2 - ln{x} \cdot 2 x}{x^4} = \frac{x - ln{x} \cdot 2 x}{x^4} = \\ \frac{x \cdot (1 - ln{x} \cdot 2)}{x^4} = \frac{1 - 2 ln x}{x^3} $


    Нам, отличникам, всё это ни к чему. Зачем писать столько ненужных промежуточных действий, когда можно сразу готовый ответ? Мы же хотим поскорее разделаться с этим примером, чтобы перейти к следующему!

    Почему мы так можем, а другие — не могут?

    Математика и кратковременная память


    Нет, это очевидно, что мы можем выполнить все необходимые операции в уме, и нам нет необходимости записывать промежуточные результаты на бумаге. Но почему это возможно?

    В когнитивной психологии ту часть памяти, в которой непосредственно производятся вычисления «в уме», называют кратковременной и рабочей. Там всё сложно и неоднозначно, но как её ни назови, а объём этой памяти сильно ограничен. Разные исследователи называют «магические числа» семь и пять. Это средние значения. Объём «рабочей» памяти зависит от обстоятельств и коррелирует с интеллектуальными способностями.

    Получается, способность не записывать промежуточные результаты обусловлена возможностями нашей рабочей (или кратковременной) памяти. У нас, технарей, рабочая память для хранения «технических» элементов объёмнее, чем у гуманитариев. Чем больше памяти, тем больше промежуточных шагов мы можем накапливать в ней, не записывая.

    Программирование и кратковременная память


    А теперь попробуем представить, как работают наши сверхспособности, когда мы программируем.

    Для решения полученной задачи требуется написать определённое количество кода. Но мы торопимся, и мы способны удержать в голове массу деталей. Чувствуете, куда я клоню? Мы не записываем промежуточные шаги. Вместо этого мы пишем такие алгоритмы, которые получают исходные данные и сразу выдают готовый результат. Они длинные, эти алгоритмы, и они делают очень много работы. Столько, сколько мы смогли уместить в нашей собственной рабочей памяти, когда их писали.
    Гипотеза. Чем умнее программист (чем более объёмной рабочей памятью он располагает), тем более длинные методы и функции он пишет.

    Попробуем подсчитать, насколько большой объём рабочей памяти требуется при программировании. Поскольку мы не знаем, как именно устроено мышление, и какими именно «объектами» оно оперирует, то просто будем подсчитывать независимые объекты, которые встречаются в коде программы.

    В качестве подопытного я взял функцию на Ruby длиной 150 строк из проекта BigBlueButton. Сразу признаюсь, что этот код задел меня за живое. Пришлось потратить пару дней и почти полностью переписать несколько методов, чтобы внести косметические изменения в функциональность небольшой части проекта. Ни одну сколько-нибудь длинную последовательность строк не получилось использовать повторно.

    Этот код прекрасно иллюстрирует гипотезу. Он был написан так же, как мы привыкли решать примеры. Масса промежуточных шагов удерживалась непосредственно в голове, а «на бумагу» попало лишь окончательное решение. 150 строк, которые решают всю чёртову задачу одним махом. Этот код явно был написан очень талантливым парнем!

    Мы делаем это не злонамеренно. Основа работы программиста — удерживать в голове колоссальное количество объектов и связей между ними. Что плохого в том, что мы задействуем сразу побольше этих объектов в одном методе, чтобы как можно скорее разделаться с ними и перейти к следующему большому набору объектов? Последовательное решение задачи «понемногу», «шаг за шагом» — это ведь для троечников, правда?

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

    • 4 аргумента функции, которые упоминаются в общей сложности 15 раз;
    • 42 внутренних переменных, использованных ещё 131 раз;
    • 52 обращения к 24 различным элементам хэшей, переданных функции в качестве аргументов (чтобы работать с кодом, необходимо удерживать в голове внутреннее «устройство» всех этих хэшей);
    • 9 обращений к 8 различным внешним сущностям (константам и методам классов).

    Итого, считая по-минимуму, получились 4+42+24+8=78 независимых объектов. И это я ещё не считал операции, которые выполняются над объектами. А ведь операции тоже «занимают» какую-то долю рабочей памяти.

    78 объектов против «магических» семи — не многовато ли для одной функции?

    Конечно, тут можно бесконечно спорить о том, что раз код написан и работает, значит, 78 объектов — вовсе не проблема. Это ведь не самый длинный метод, так? Значит, объектов может быть ещё больше? К тому же, кто сказал, что все 78 необходимо удерживать строго одновременно?

    Проблема длинного метода не в том, что его невозможно понять. Проблема в том, что понять его сложно, потому что требуются усилия для того, чтобы «прогреть наш кеш». Нужны время и силы, чтобы загрузить в рабочую память весь набор объектов, составляющих изучаемый фрагмент кода, и удерживать их там продолжительное время. Чем больше объектов — тем больше подавай рабочей памяти, а она у всех разная, да и у одного и того же разработчика зависит от степени усталости и настроения…

    Метрики и кратковременная память


    Для оценки качества кода применяются различные метрики. Например, Морис Холстед исследовал численные метрики кода ещё в 70-х (!) годах. Вроде бы понятно, что измерять и оценивать качество кода — дело хорошее. И спору нет, что чем сложнее код, тем больше умственных усилий он требует. Но есть с метриками один вопрос. По выражению EvgeniyRyzhkov, «в метриках не придумано главного — что с ними делать».

    Программирование — сложный интеллектуальный процесс. На его протекание влияют множество факторов. Самый значимый фактор, на мой взгляд — это свойства и ограничения главного рабочего инструмента для создания кода — интеллекта программиста. Изучением «внутреннего устройства» интеллекта занимается когнитивная психология. Благодаря ей достоверно известно, что возможности интеллекта ограничены, и величина этих ограничений даже была измерена. А раз ограничены возможности инструмента, значит и «изделие» будет и должно иметь определённую специфику. «Будет» — потому что код является продуктом работы мозга-инструмента. «Должно» — потому что код является одновременно также и исходным сырьём.

    Код-сырьё должен отвечать определённым критериям, чтобы мозг-инструмент не сломался во время его обработки.

    Раз наука, которая изучает свойства интеллекта, называется когнитивной, то и метрику, которая соотносится с ограничениями этого интеллекта, логично тоже назвать когнитивной. Назову её, скажем, когнитивным весом. А то когнитивная сложность уже занята. Кстати, Холстед в своих «Началах науки о программах» описывает метрику, на которую когнитивный вес очень сильно похож. Только Холстед не апеллирует к понятию «когнитивный». (К слову, «Cognitive Psycology» R. Solso была впервые опубликована в 1979-м, а «Elements of Software Science» M. Halstead — в 1977-м).

    Так вот, отвечаю на вопрос про метрики.
    Практически полезные метрики качества кода следует строить так, чтобы они показывали, с каким кодом мозгу-инструменту будет работать легко, а на каком он «сломается». И не «интуитивно», а опираясь на данные, получаемые от когнитивной науки.

    Резюме


    Запись решения «шаг за шагом» требуется не только для того, чтобы троечник мог решить пример. Она нужна, чтобы на чтение и проверку решения требовалось настолько мало умственных усилий, чтобы делать это мог даже троечник. Отличник разберётся и в записи решения без промежуточных шагов, но если примеры сложные, а прочитать их нужно тысячи… В общем, вы поняли.

    В следующий раз расскажу о приёмах, которые я использую для уменьшения сложности своего кода.

    P.S. Один из моих шайтан-методов, до которого никак не доходят руки отрефакторить, имеет длину 120 строк. Когнитивный вес даже считать не хочется. Стыдно.



    UPD

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

    Школьные оценки для меня никогда не были показателем чего-то существенного. У меня самого по всем предметам, кроме математики, были крайне средние оценки. Своей дочери я всегда старался внушить мысль, что оценки — не главное в жизни. Важно, чтобы что-то было в голове: знания, понимание, интерес к чему-то. Хотя, она меня не слушала и закончила школу с золотой медалью.



    UPD2

    Нет, я имел в виду именно тех отличников, которые соображают и понимают, а не зубрят. В учебных предметах наши способности позволяют нам получать верные решения очень быстро, пропуская несущественные промежуточные шаги. Да и в реальных технических задачах высокая скорость решения, как правило, — положительный фактор.

    А в программировании промежуточные шаги внезапно оказываются самыми существенными. И мы начинаем искренне недоумевать по поводу наездов на наши прекрасные 300-строчные функции. Но мы ведь старались, мы делали лучшее, на что способны! Просто никто не объяснил, что в программировании нужно в точности наоборот: писать тупой код, а быстрые и умные решения всегда имеют вредные последствия.

    Мне в универе рассказывали только про структурное программирование Дейкстры, а про принцип единственной ответственности я узнал много позже. Что удивительного, что я рассуждал о длине метода, опираясь на чьи-то советы по поводу размера экрана, не понимая, какова истинная причина того, почему методы должны быть короткими.
    Поделиться публикацией
    Комментарии 542
      +13
      Этот код прекрасно иллюстрирует гипотезу. Он был написан так же, как мы привыкли решать примеры. Масса промежуточных шагов удерживалась непосредственно в голове, а «на бумагу» попало лишь окончательное решение. 150 строк, которые решают всю чёртову задачу одним махом. Этот код явно был написан очень талантливым парнем!

      Вы не думаете что могло быть так, что сначала было написано 1500 строк, а потом за несколько проходов оптимизировано или воообще переписано? Тогда не надо голове держать много. Наверное есть гении которые сразу пишут оптимизированный код, но я думаю что это больше связано с опытом а не с тем скольк можно в голове удержать.
        +4
        Согласен с каждым словом. Примерно так я и сам часто пишу, и собираюсь рассказать об этом в следующем посте. Более того, наши гуру-наставники советуют всегда рефакторить код после написания, потому что хороший код никогда не получается с первого раза. Так что сначала 1500 строк, а потом рефакторинг — это нормально и правильно.

        Способность писать сразу понятный код именно что связана с опытом, а не со способностью удерживать в памяти много объектов.

        Только вот чтобы сначала написать 1500 строк, нужно их всё таки все держать в памяти. А во вторых, рефакторинг 1500 строк до 10 раз по 150 — это не очень хороший рефакторинг. Надо тогда уж хотя бы до 100 раз по 15.
          –5
          На одном месте я видал ваши рефакторинги на 100 функций по 15 строк.
          –9

          Логичнее писать псевдокод, его оптимизировать, а потом уже реализовывать (и еще раз оптимизировать). Сам псевдокод можно оставить в комментарии к исходнику.

            +7
            1500 строк псевдокода — хотелось бы посмотреть :)
              –3
              Любую книгу по алгоритмам откройте.
              Хотя современным «программистам» зачем оно…
                +3

                … И вы там не найдете такого псевдокода:)
                Алгоритмы обычно более короткие.

                  –1
                  Распишите-ка алгоритм, например, работы банкомата с картой.
                    +2

                    А такого вы и не увидите в книгах по алгоритмам.
                    Вы можете увидеть псевдокод RSA, можете увидеть декартово дерево. Но до 1500 строк там очень далеко:)

              0
              Нельзя на псевдокоде писать алгоритмы. Одна из проблем псевдокода — отсутствие спецификации.
              Простой пример — двойное отрицание над int в С++.
              Двойное отрицание дает нам приведение к строгому bool.
              Но в псевдокоде что даст двойное отрицание? Это будет логическая операция или бинарная? Конкретно в случае двойного отрицания понятно, что оно бессмысленно в случае бинарной операции. Но в любом случае однозначности нет.
                +1

                Язык псевдокода может быть достаточным образом формализован и однозначен. Основное его отличие от языка программирования лишь в том, что он не предназначен для исполнения машиной, например, написан исходя из предположения бесконечной памяти и скорости.

                  –3
                  Большинство нынешних «разработчиков» это и есть машина для исполнения псевдокода. Причем, очень некачественная.

                  PS и да, если вы не можете написать свои сишные уродцы типа «двойного отрицания над int» русским языком, то ваш код сразу можно выкинуть в помойку. Только там место недокументированному коду. Такому коду нельзя доверять, пока его сам не проверишь целиком. А, значит, проще написать заново.
                    +1

                    Математически строгая верификация кода очень трудоемка, если вообще возможна в общем случае. Так или иначе, коду приходится доверять и неважно кто его писал.

                      –1

                      А потом у нас у всех хартблид или иные факапы сходного масштаба

                  +1
                  то, что тут написано, и есть псевдокод.
                  в чем проблема написать «строгий boolean»?
              +18

              А почему "троечник" не может решить пример в три действия?


              (lnx/x^2)' = (lnx)'/x^2 + lnx * (x^-2)' = 1/x / x^2 -2 * lnx / x^3=(1-2*lnx)/x^3

              И не связана ли возможность "отличника" быстро щелкать примеры с тем
              что он в десятки, а возможно и в сотни раз больше задачек решил, чем троечник (
              который например банально не делает домашнее задание) и тем самым просто натренировался как "собака Павлова"?

                0
                Ну, в три действия — это уже «хорошист»!

                Безусловно, бывают такие талантливые троечники, которым просто на математику наплевать, потому что у них есть куда более интересные для них занятия: литература, плавание и КВН, например. На таких ведь так мало…
                  +2

                  А почему мало? У меня один одноклассник троечник — руководитель в строительной компании, второй — бизнесмен в области холодильного оборудования (мерседес новенький купил только только закончив универ). У отличников, тоже все нормально, но троечник это не приговор. Отличники часто чсв свое применить в жизни не могут.

                    +3
                    Тут дело не в количестве действий. В решении apro не записано самое важное:
                    (lnx/x^2)' = (lnx * x^-2)'
                    Это и есть «отличие» — увидеть возможность упростить задачу ещё до применения стандартных правил. А способность в уме выполнять последовательность действий, которую записал «троечник» — тоже, конечно, «отличие», но уже другое.
                      –1
                      А смысл упрощать её до такого вида, если делением намного проще получается и быстрее?
                      Но здесь нет никакого «отличия» — как сказали выше, просто заученный алгоритм и всё
                        +5
                        Это примерно как 36*98 можно считать в лоб столбиком, а можно 36*(100-2)=3600-36*2=3600-72=3528. Мне быстрее вторым способом.
                          0
                          Вы не поняли меня. Безусловно, в вашем случае я считал бы точно так же.
                          Я же говорил точно про тот пример, считать производную как производную частного там намного проще и быстрее, так зачем использовать «другой» метод, если можно пойти по классике?
                        –1
                        Формулу (a/b)'=(a'b-ab')/b^2 должны знать и отличник, и троечник. Её использование и стандартно (т.е. более узнаваемо и понятно), и более элегантно. А чтобы её вспомнить, то можно «на полях» вывести её из (a/b)'=(a * b^-1)'=…
                          0
                          Нет, эта формула ужасна. Применять ее в ситуации деления на x^n — не лучшая идея, слишком много иксов вылазит которые потом сокращать придется.
                            0
                            Ну, не знаю… некоторые и из левой части уравнения в правую с трудом слагаемые переносят.
                              0
                              > Применять ее в ситуации деления на x^n — не лучшая идея, слишком много иксов вылазит которые потом сокращать придется.

                              Вылезет — сократим. Вы так говорите, будто вам бумаги жалко.
                        +2
                        Вы пытаетесь причесать всех под одну гребёнку, IMHO.
                        Два человека, решившие одинаковое количество одинаковых примеров, не будут одинаково выполнять одинаковые примеры
                          +4

                          Способность отличника быстро щёлкать примеры связана с другой работой мозга. У меня старший ребенок решает примеры и не устаёт. Я старшего спрашивал как ты это делаешь. А он, оказывается числа в голове видит, он работает с зрительной памятью, он их не запоминает а представляет зрительно. Он математику любит. А младший биология и химия, районную олимпиаду выиграл. В биологии как рыба в воде. А считать долго не может, быстро устаёт.

                            0
                            Я в детстве числа представлял в виде длинной ленты с метками. Прообразом была мерная лента для шитья, которая висела в гардеробе. Для сложения и вычитания оперировал оффсетами на этой визуальной ленте и довольно быстро. Еще пытался умножение делать. Дальше дело не пошло :-)
                              0
                              Такие же оффсеты, как и при сложении. Только мерная лента слегка другая, с логарифмической шкалой :)
                                0
                                Только вот какой в этом смысл? Как это первокласнику обьяснить?
                            +4
                            Я думаю что возможность быстро «щёлкать» в первую очередь связана с качеством выстроенной модели в голове. По моему мнению количество решённых примеров влияет только на закреплении этой модели.
                            Если модели в голове по какой то причине нет, то не важно сколько вы решили задач, это просто форма зубрёжки.
                            +11
                            Вы построили такую вот зависимость: отличник -> много запоминает -> пишет длинные функции.
                            И первое условие не всегда верное, а уж второе-то…
                              –1
                              успеваемость по таким предметам, как чтение и математика, напрямую связана с рабочей памятью

                              Эллоуэй Трейси, глава «Рабочая память и коэффициент интеллекта IQ» из книги «Включите свою рабочую память на полную мощь».

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

                              Рабочая память, Википедия.

                              Так что первое условие я не придумал. А второе условие — да, это гипотеза, о чём я так прямо и написал. И мне хотелось бы обсудить её. Может быть, следовало обратиться к сообществу психологов? Однако затронутая проблема касается именно программистов, поэтому я предпочёл хабр.

                              Я сделал определённые выводы, и мне интересно, что о них думают мои собраться по профессии. Прошу высказываться!
                                +11
                                Это теория.
                                Непомерно длинные функции что я видел были написаны или в режиме «на отвали», или фрилансерами(по сути тоже что и первое) или по старой памяти с более низкоуровневых языков. Программисты которые сильно умны, думают не только об уравнениях, но и о качестве кода и его последующей поддержке или они не очень-то и умны как по мне.
                                  +5

                                  Умные программисты знают, что:


                                  1. Есть менее умные, но при этом не менее полезные.
                                  2. Мозг не всегда одинаково хорошо думает.
                                    +5
                                    Таких стоит называть «мудрые программисты», чтобы отличать их от просто умных ))
                                    +1
                                    Что вы скажете про 684-строчную функцию в GCC, внутри которой одно условие if занимает 38 строк? GCC тоже писали «на отвали»?
                                      +1
                                      Не надо ничего вводить в абсолют — мы не на дваче.
                                        +4
                                        О! Знаменитый reload. Для него даже отдельная статья в Wiki есть.

                                        По-моему достаточно одной цитаты: Reload is the GCC equivalent of Satan, чтобы понять как к ней относятся разработчики…

                                        Не думаю, чтобы её писали «на отвали», скорее «так уж вышло»… но да — это не то, чем можно гордится.
                                          +1
                                          Я к этому и вёл: что длинные запутанные функции появляются как в случае «писали на отвали», так и в случае «так уж вышло». GCC и его reload — далеко не уникальный случай.
                                            0
                                            Или в случае «хотели оптимизировать». Дополнительный вызов — это хоть и небольшие, но накладные расходы. Если можно их избежать — стоит избежать. Во всяких парсерах и компиляторах как раз я очень понимаю их наличие: огромная портянка из if-ов. При этом:

                                            1) Можно настроить IDE на схлопывание всех этих if-ов и расставить границы блоков фолдинга, и всё сразу станет коротко и изящно (ну если не открывать код в обычном редакторе, разумеется).
                                            2) Если мы пишем реально что-то очень длинное, типа автомата — у нас неизбежно будет много if-ов, но мы не можем просто так взять, и использовать вызов других методов, если мы пишем в ООП, поскольку тогда мы потеряем доступ к переменным состояния, которые так мы можем сделать локальными, а в противном случае придётся хранить их или как поля объекта (что выглядит безобразно), или завести объект с набором всех этих данных и передавать его как аргумент другим методам, заменив if-ы на вызовы этих методов. Как по мне, затраты по памяти и циклам CPU на создание и передачу такой структуры не стоят того, чтобы сделать основной метод «в пределах разумного по длине».

                                            Тем более, такие методы очень редко правятся, а если и правятся, то людьми, которые очень в теме того, что там внутри происходит :)
                                              0
                                              Можно настроить IDE на схлопывание всех этих if-ов

                                              Для ясности: там не тело ифа на два экрана, а само его условие.
                                              Условия ифов ваша IDE тоже схлопывает?

                                              Тем более, такие методы очень редко правятся, а если и правятся, то людьми, которые очень в теме того, что там внутри происходит :)

                                              Ну естественно, что раз код с одного взгляда вызывает желание закрыть его и больше никогда не открывать, — то он будет правиться очень редко и только теми людьми, кто смогут в себе эту естественную реакцию подавить.
                                                0
                                                Условие на два экрана — это уже диагноз :D
                                                0
                                                Дополнительный вызов — это хоть и небольшие, но накладные расходы. Если можно их избежать — стоит избежать. Во всяких парсерах и компиляторах как раз я очень понимаю их наличие: огромная портянка из if-ов.

                                                А я не понимаю.

                                                Такой код нетестируем и неподдерживаем, а вот код на каких-нибудь там монадных комбинаторах — вполне себе. При этом компилятор вполне может все лишние вызовы функций заинлайнить: не зря на boost.spirit получаются одни из самых быстрых парсеров.

                                                И, кроме того, тестируемость и поддерживаемость кода означает, что его легче менять и, в том числе, легче оптимизировать высокоуровнево: почти всегда профит в производительности достигается тюнингом алгоритма, а не его реализации вроде уровня инлайнинга.

                                                Если мы пишем реально что-то очень длинное, типа автомата — у нас неизбежно будет много if-ов, но мы не можем просто так взять, и использовать вызов других методов, если мы пишем в ООП

                                                А просто не надо писать стейтмашины в ООП.
                                                  0
                                                  Такой код нетестируем и неподдерживаем

                                                  Весьма спорное утверждение. Очень даже поддерживаем. А тестирование — ну да, тут сложнее, надо сразу писать без ошибок, иначе потом дебажить не так просто. Хотя тоже возможно, устанавливая точки останова в нужные if-ы прямо в начало.

                                                  А просто не надо писать стейтмашины в ООП.

                                                  Почему не надо, и какие альтернативы, если нам надо парсинг делать? К тому же, стейтмашина под конкретный язык — будет быстрее регулярок, причём в разы, имхо.
                                                    0
                                                    Очень даже поддерживаем.

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

                                                    А тестирование — ну да, тут сложнее, надо сразу писать без ошибок, иначе потом дебажить не так просто.

                                                    И ещё сразу писать так, чтобы рефакторить и поддерживать не пришлось, чтоб прям сразу все будущие юзкейсы покрыть и всё такое.

                                                    Хотя тоже возможно, устанавливая точки останова в нужные if-ы прямо в начало.

                                                    Когда у вас там виноградные гроздья if'ов, то проблемы возникают уже с выделением нужного if'а.

                                                    Когда вы пишете на комбинаторах, вы можете протестировать каждый комбинатор отдельно и чуть ли не породить минимальный ломающий пример в автоматическом режиме каким-нибудь quickcheck.

                                                    Почему не надо, и какие альтернативы, если нам надо парсинг делать?

                                                    А у вас ООП фиксированный констрейнт или необходимость распарсить язык? Откажитесь от ООП [в этой задаче].

                                                    К тому же, стейтмашина под конкретный язык — будет быстрее регулярок, причём в разы, имхо.

                                                    Регулярка задаёт соответствующую стейтмашину, между ними (вернее, между фактор-множествами по отношению эквивалентности на них) есть биекция.

                                                    При должном упорстве можно минимальный DFA построить даже в компилтайме.
                                                      0
                                                      Если у вас грамматика языка зафиксирована один раз и навеки, то, пожалуй, да, потому что поддерживать и не надо.

                                                      А разве бывает иначе (если мы пишем парсер существующего, а не своего языка)? Язык конечно меняется и совершенствуется, но отличия в версиях обычно не столь грандиозны. Да и в случае чего можно будет просто заново всё переписать, имхо.

                                                      Регулярка задаёт соответствующую стейтмашину, между ними (вернее, между фактор-множествами по отношению эквивалентности на них) есть биекция.

                                                      Но регулярка как инструмент более универсальна, т.к. может тестировать что угодно, а не только конкретную грамматику. Разве код без регулярок не будет в конечном итоге работать быстрее?

                                                      А у вас ООП фиксированный констрейнт или необходимость распарсить язык? Откажитесь от ООП [в этой задаче].

                                                      У меня констрейнт — язык разработки. Вы таки сможете написать парсер на Java, не используя ООП?)

                                                      Когда у вас там виноградные гроздья if'ов, то проблемы возникают уже с выделением нужного if'а.

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

                                                      Когда вы пишете на комбинаторах, вы можете протестировать каждый комбинатор отдельно и чуть ли не породить минимальный ломающий пример в автоматическом режиме каким-нибудь quickcheck.

                                                      А не подскажете, что называется комбинатором? Я просто только недавно начал изучать эту тему :)
                                                        0
                                                        А разве бывает иначе (если мы пишем парсер существующего, а не своего языка)?

                                                        Зависит. Иногда можно сесть с бумажкой и карандашом и написать грамматику руками, а потом запрогать её в том или ином виде.

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

                                                        Но регулярка как инструмент более универсальна, т.к. может тестировать что угодно, а не только конкретную грамматику.

                                                        Конкретная регулярка точно так же тестирует конкретную грамматику. Конкретную регулярку вы можете скомпилировать в автомат (всякие там JIT в pcre и подобных или тот же уже обикавшийся в этом треде ragel).

                                                        У меня констрейнт — язык разработки. Вы таки сможете написать парсер на Java, не используя ООП?)

                                                        Я вообще не смогу написать на Java, потому что я её не знаю.

                                                        Но вообще печально, конечно, когда нельзя выбрать язык под задачу.

                                                        А не подскажете, что называется комбинатором? Я просто только недавно начал изучать эту тему :)

                                                        Ну, в программировании (в ФП, в частности) в первом приближении это относительно любая функция высшего порядка. Например, вы можете написать комбинатор many, который будет принимать парсер A и возвращать парсер, распознающий A*. Можете написать комбинатор chain, принимающий два парсера и возвращающий парсер, склеивающий их. Можете написать комбинатор many1, эквивалентный Kleene plus вместо star и работающий как many1 p = chain p (many p).

                                                        Вот, например, список таковых парсеров в одной из каноничных библиотек.

                                                        В каком-нибудь boost.spirit оно по духу близко, пусть и с неизбежной уродливостью синтаксиса.
                                                          0
                                                          А что насчёт производительности этого процесса (получения парсера из парсера)? Я правильно понимаю, что для нас это «чёрный ящик», и мы не профилируем никак этот код?

                                                          Потом, Вы сказали, что pcre может компилировать регулярки. Но pcre — это стандарт синтаксиса. А ещё есть реализации pcre в разных языках программирования. Вы правда уверены, что они всегда всё компилируют и кэшируют, а не считают всё в рантайме (и даже что можно косвенно на это поведение влиять)? Я вот нет :) Потому и предположил, что без регулярок будет работать быстрее (и контроля больше).
                                                            0
                                                            А что насчёт производительности этого процесса (получения парсера из парсера)? Я правильно понимаю, что для нас это «чёрный ящик», и мы не профилируем никак этот код?

                                                            Зависит. В конце концов, это всего лишь абстракция, и никто не мешает достаточно хорошей реализации быть быстрой и компилироваться примерно в тот же код, который вы бы написали руками после кучи оптимизаций. Опять же, boost.spirit, работающий по этому принципу, работает очень быстро (но компилируется очень медленно, это да).

                                                            Потом, Вы сказали, что pcre может компилировать регулярки. Но pcre — это стандарт синтаксиса.

                                                            Я про libpcre.
                                                              0
                                                              А что там с джавой?) Она точно использует эту либу под капотом?)
                                                                0
                                                                Это на джаву смотреть надо, я про неё саму довольно мало знаю, а уж про детали реализации — тем более.
                                                      0
                                                      Почему не надо, и какие альтернативы, если нам надо парсинг делать?
                                                      Самый обычный Ragel, если вам именно стейт-машина и Bison не подходит.

                                                      То, что у вас одна большая процедура в коде не означает, что исходник точно так же должен выглядеть…
                                                        0
                                                        У Ragel проблема в том, что оно обязательно генерирует DFA (по крайней мере, я за разумное время не нашёл, как этого избежать), что на некоторых регулярках приводит к экспоненциальному взрыву числа состояний.

                                                        Самописный NFA на хаскеле работал не сильно медленнее (там, где Ragel'у таки удавалось что-то выплюнуть, не сожрав пару сотен гигов памяти и не уйдя в своп).
                                                      –3
                                                      Декомпозиция никак не влияет на тестируемость, это распространенный миф. Тестами покрывается не код, а граф потока управления (чего многие почему-то не понимают). При разбиении одной большой ф-и на много мелких — граф потока не меняется. При замене ифа лямбдой/наследованием — граф потока не меняется.

                                                      Несложно переписать любой код на лямбды так, чтобы никаких ифов там вообще не было. Код станет более тестируемым, что ли? Нет. А именно это происходит в случае с комбинаторами.
                                                        +2
                                                        Граф потока от декомпозиции еще как меняется: в нем появляются новые точки входа и выхода.
                                                          +1
                                                          Нет, не появляются. Откуда бы им появиться? Просто современный подход к тестированию — не про инженерию, а про рисование красивых циферок и выкрикивание баззвордов. Это ведь смешно, когда фактически один и тот же набор тестов после автоматического рефакторинга (замена ветвлений лямбдами) вам поднимает «покрытие кода» (что это такое кстати, хоть кто-то хоть когда-то смог дать корректное определение для языка с поддержкой указателей/ооп/лямбд?) в сотню раз, при этом количество потенциальных ошибок, которые данный набор тестов способен найти, никак не меняется и логика приложения — тоже не меняется.
                                                            +1
                                                            Никто не говорит про автоматический рефакторинг с обмазыванием лямбдами вместо ветвлений, это не имеет смысла. Смысл имеет замена ветвлений функциями вроде choice, many, many1, optional и так далее, с чётко определённой семантикой и единожды проверенной реализацией.

                                                            Какая-то непонятная мне борьба с соломенными чучелами из автоматических рефакторингов и погони за метриками вроде покрытия кода (которое, вы абсолютно правы, смысла не имеет даже в языке не то что с ООП/лямбдами, а хотя бы с условным оператором, лучше хотя бы думать о покрытии бранчей, если так метрик хочется).
                                                              0
                                                              > Смысл имеет замена ветвлений функциями вроде choice, many, many1, optional и так далее, с чётко определённой семантикой и единожды проверенной реализацией.

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

                                                              > с чётко определённой семантикой и единожды проверенной реализацией.

                                                              У if семантика тоже четко определена и неединожды проверена.

                                                              > лучше хотя бы думать о покрытии бранчей, если так метрик хочется

                                                              Я покрытие бранчей и имел ввиду. Но проблема в том, что любой виртуальный вызов, передача лямбды или указателя на ф-ю, дает потенциально бесконечное количество бранчей, по-этому покрытие всегда 0%.
                                                                +1
                                                                Но это и есть обмазывание лямбдой вместо ветвления. Ветвление в данном случае никуда не уходит.

                                                                А функции — всего лишь сахар над goto, функции не нужны, все на goto!

                                                                В графе потока управления как была вилка, так и осталась вилка. И эту вилку надо тестировать.

                                                                Зачем вам каждый раз тестировать, что делает условный many1?

                                                                У if семантика тоже четко определена и неединожды проверена.

                                                                Только она чуть более базова и посему неизбежно чуть более обща.

                                                                Но проблема в том, что любой виртуальный вызов, передача лямбды или указателя на ф-ю, дает потенциально бесконечное количество бранчей, по-этому покрытие всегда 0%.

                                                                Вы имели в виду бесконечное количество последовательностей бранчей? Да, с этим трудно спорить.
                                                            +1
                                                            O(m+n) обычно таки лучше O(mn).
                                                              –1
                                                              Так всегда O(mn) тестов надо. А если провели декомпозицию — то еще больше (тоже mn, но скрытая константа выше)
                                                            0
                                                            Потому что вы можете протестировать каждый комбинатор в отдельности, опять же. Да и проще рассуждать о том, что от добавления нового комбинатора у вас не сломаются имеющиеся (при постепенной реализации грамматики, например). Да и зависимости-переходы между комбинаторами сразу видно, и легко отвечать на вопрос «где используется этот кусок грамматики комбинатор».

                                                            Просто на всякий случай, мы правда обсуждаем, что лучше — функция на 1000 строк с полсотней ифов (пусть и в известном смысле минимальная такая функция), или же набор из полсотни мелких и удобных комбинаторов (тоже, для честности сравнения, в известном смысле минимальный)?
                                                              0
                                                              Просто на всякий случай, мы правда обсуждаем, что лучше — функция на 1000 строк с полсотней ифов (пусть и в известном смысле минимальная такая функция), или же набор из полсотни мелких и удобных комбинаторов (тоже, для честности сравнения, в известном смысле минимальный)?
                                                              Да. Я, впрочем, не буду заявлять, что «граф потока не меняется» от декомпозиции, но был у нас случай, когда мы хотели всё сделать «серьёзно», «по-настоящему безопасно».

                                                              И оказалось, что надёжнее всего не доказывать какие-то свойства «полсотни мелких и удобных комбинаторов» — а просто взять сгенерированную ragel'ем функцию, рассмотреть свойства языка, который она принимает — и всё. Не требуется знать и проверять все сотни DFA, из которых «собрана» функция, не требуется доказывать что ragel их правильно комбинирует… просто берём граф и изучаем его свойства.

                                                              Впрочем стоит признать что такой «финт ушами» мне встретился в моей карьере один раз. И фишка была в том, что мы хотели уменьшить как можно сильнее обьём кода, которому нам приходится доверять. Но… так тоже бывает.

                                                              P.S. Только не надо говорить «вы же всё равно собрали свой автомат из комбинаторов». Да — так нам показалось удобнее. Но тестировали мы именно функцию на 1000 строк (на самом деле на 100'000 строк, но не принципиально) — если бы она была написана руками с точки зрения тестирования ничего бы не поменялось.
                                                                0
                                                                И оказалось, что надёжнее всего не доказывать какие-то свойства «полсотни мелких и удобных комбинаторов» — а просто взять сгенерированную ragel'ем функцию, рассмотреть свойства языка, который она принимает — и всё. Не требуется знать и проверять все сотни DFA, из которых «собрана» функция, не требуется доказывать что ragel их правильно комбинирует… просто берём граф и изучаем его свойства.

                                                                То есть, вы взяли сгенерированный ragel'ом код, в известном смысле отреверсили его и что-то про него доказали?

                                                                А зачем? Почему бы не изучить то, что вы скормили ragel'у в предположении о том, что ragel корректен?

                                                                Не требуется знать и проверять все сотни DFA, из которых «собрана» функция

                                                                Ну да, требуется всего лишь проверить их композицию, что, конечно, легче.

                                                                И фишка была в том, что мы хотели уменьшить как можно сильнее обьём кода, которому нам приходится доверять. Но… так тоже бывает.

                                                                А, вот зачем. Ну libc, компилятор и, чего мелочиться, микрокод вы, надеюсь, тоже верифицировали?

                                                                Ну и лично мне (особенно после попыток вручную ковырять выплеванное ragel'ом, причём для тестовых примеров на отдельные комбинаторы, которые я компилировал в ragel, был у меня такой опыт) кажется, что проще было бы верифицировать сам ragel.

                                                                Но тестировали мы именно функцию на 1000 строк (на самом деле на 100'000 строк, но не принципиально) — если бы она была написана руками с точки зрения тестирования ничего бы не поменялось.

                                                                Было бы интересно сравнить время разработки этой функции руками и через ragel.
                                                                  0
                                                                  То есть, вы взяли сгенерированный ragel'ом код, в известном смысле отреверсили его и что-то про него доказали?
                                                                  Угу.

                                                                  А зачем? Почему бы не изучить то, что вы скормили ragel'у в предположении о том, что ragel корректен?
                                                                  Потому что, во-первых, были сомнения в корректности рагеля. Неизвестно — корректен ли он, но точно нестабилен: одинаковые входы дают на машинах разных разработчиков разные (хотя и эквивалентные) выходы. Чтобы это побороть мы перешли от прямой генериации кода ragelем к ragel -x с последующей генерацией кода уже нашим скриптом). Что, кстати, облегчило и исследование тоже.

                                                                  Во-вторных — у нас было подробное и достаточно точное описание того, что мы хотели получить в терминах исходной задачи (правда при этом использовался objdump -d, скорость которого была так примерно порядка на три меньше, чем нам было нужно). А все промежуточные DFA пришлось бы исследовать руками.

                                                                  А, вот зачем. Ну libc, компилятор и, чего мелочиться, микрокод вы, надеюсь, тоже верифицировали?
                                                                  libc и компилятором занимаются другие люди. А ragel'ем никто не занимается. Микрокод вне Intel никто исследовать не может… хотя и очень хочется — там точно есть баги, неизвестно только, насколько страшные. У security team есть идеи по этому поводу — но они под NDA, потому про них пока рано говорить. Но мне нравится ход ваших мыслей.

                                                                  Было бы интересно сравнить время разработки этой функции руками и через ragel.
                                                                  Это тяжело будет сделать. Но сама эта функция заменила более старую подсистему, которая как раз была написана в «полноценном ООП-стиле». Ну там интерфейсы всякие и композиция в рантайме.

                                                                  В программе у нас два режима — более «точный» и менее «точный». Соотвественно в оригинальной программе был флаг, а в случае с DFA — было две независимых DFA. Только менее точная была в 100 раз меньше, чем старая подсистема (и работает в 20 раз быстрее), а более точная — примерно то же самое по обьёму (но в 10 раз быстрее).
                                                                    0
                                                                    Неизвестно — корректен ли он, но точно нестабилен: одинаковые входы дают на машинах разных разработчиков разные (хотя и эквивалентные) выходы.

                                                                    А это было для вас недостатком?

                                                                    Известно ведь, что, скажем, минимальный DFA уникален, но с точностью до изоморфизма (и если вы выходом считаете конкретные метки конкретных состояний, то они будут различаться). Неминимальных вообще много, и ЕМНИП ragel не всегда старается получить именно минимальный.

                                                                    Во-вторных — у нас было подробное и достаточно точное описание того, что мы хотели получить в терминах исходной задачи (правда при этом использовался objdump -d, скорость которого была так примерно порядка на три меньше, чем нам было нужно). А все промежуточные DFA пришлось бы исследовать руками.

                                                                    Это (и связь с objdump) я вот не понял. Видимо, это уж слишком завязано на вашу конкретную задачу.

                                                                    libc и компилятором занимаются другие люди.

                                                                    И они правда занимаются верификацией какого-нибудь современного gcc или clang? Или вы используете что-нибудь вроде gcc 3.3 или 4.2 из условной МСВС?

                                                                    А ragel'ем никто не занимается.

                                                                    Ну, опять же, у меня есть впечатление, что проще было бы расковырять сам ragel. Или написать кодогенератор на хаскеле или идрисе, в конце концов.

                                                                    Но это, конечно, зависит от того, как часто вам нужно такие финты проворачивать. Вы вон сами пишете, что оно вам встречалось раз в жизни.

                                                                    Но сама эта функция заменила более старую подсистему, которая как раз была написана в «полноценном ООП-стиле». Ну там интерфейсы всякие и композиция в рантайме.

                                                                    Я плохо себе представляю, кто и зачем в здравом уме будет писать парсеры в ООП-стиле, с интерфейсами и композицией в рантайме.
                                                                      0
                                                                      Неизвестно — корректен ли он, но точно нестабилен: одинаковые входы дают на машинах разных разработчиков разные (хотя и эквивалентные) выходы.
                                                                      А это было для вас недостатком?
                                                                      Это риторический вопрос? Вам слова «reproducible builds» о чём-нибудь говорят?

                                                                      Это (и связь с objdump) я вот не понял. Видимо, это уж слишком завязано на вашу конкретную задачу.
                                                                      Угу.

                                                                      И они правда занимаются верификацией какого-нибудь современного gcc или clang?
                                                                      До формальной верификации — им ещё как пешком до луны. Пока речь идёт о самых базовых вещах. В частности нужно, чтобы builds были reproducible, а то непонятно — что с чем сравнивать. Но да, в идеале дойти до формальной верификации — было бы неплохо.

                                                                      Я плохо себе представляю, кто и зачем в здравом уме будет писать парсеры в ООП-стиле, с интерфейсами и композицией в рантайме.
                                                                      Можете попробовать догадаться. Hint: это связано с предыдущим абзацем, который вы не поняли. Люди писавшие первую версию, не осознавали, что они, на самом-то деле, пишут парсер некоего языка.
                                                                        0
                                                                        Это риторический вопрос? Вам слова «reproducible builds» о чём-нибудь говорят?

                                                                        Это с одной и той же версией ragel? Тогда да, тогда это не очень хорошо (а на самом деле очень нехорошо).
                                                                          0
                                                                          Это с одной и той же версией ragel? Тогда да, тогда это не очень хорошо (а на самом деле очень нехорошо).
                                                                          Не только «та же самая версия». Тот же самый бинарник.

                                                                          Причём на простых DFA вроде такого не случалось. А когда обнаружили — было поздно уже всё переделивать.

                                                                          Он там когда машину строит — один из вариантов выносит в switch в default, а остальное — уже обрабатывает. При этом выносит так, чтобы минимизировать размер кода, но если вариантов выноса два или больше — может на разных машинах разные интервалы выносить.
                                                                            0
                                                                            При этом выносит так, чтобы минимизировать размер кода, но если вариантов выноса два или больше — может на разных машинах разные интервалы выносить.

                                                                            Я всё равно плохо представляю, как это может быть недетерминировано в данном случае. Ну да ладно.
                                                                              0
                                                                              Представьте себе, что у вас есть вариант перейти для 0..127 в одно состояние, а для 128..255 в другое.

                                                                              Вы можете написать switch и 128 меток от 0 до 127 (а 128...255 пойдут в default) или 128 меток от 128 до 255 (а 0..127 пойдут в default).

                                                                              Вот на одних машинах ragel выбирает первую альтернативу, а других — вторую.

                                                                              P.S. На самом деле ragel умнее и если у вас есть только два таких диапазона — будет использовать if. Проблемы начинаются когда у вас есть несколько диапазонов одинакового размера и несколько отдельных вариантов.
                                                                                0
                                                                                Вы имеете в виду 127 условий? Просто если это тупо одна величина — я не очень понимаю, зачем 128 меток городить, когда простого сравнения достаточно…
                                                                    0
                                                                    Не требуется знать и проверять все сотни DFA, из которых «собрана» функция.
                                                                    Ну да, требуется всего лишь проверить их композицию, что, конечно, легче.
                                                                    Таки да, легче. Тут проблема в том, что ragel не совсем на DFA оперирует. В частности если у вас есть машина принимающая a и печатающая Yes, this is a! и другая машина, принимающая ab и перечающая Yes, this is ab!, то их композиция напечатает обе строчки получив на вход ab.

                                                                    Это, зачастую, упрощает его использование на практике, но сильно усложняет теоретическую оценку правильности результата. Если же мы рассматриваем только конечный автомат «в сборе» — то это проблемой для нас не является…
                                                                      0
                                                                      Это была одна из причин, почему я в итоге отказался от ragel и заменил его своей реализацией NFA simulation — там какая-то наркомания с приоритетами, жадностью и смежными вещами, некоторые крайние случаи которой мне победить не удалось.

                                                                      Но, в любом случае, если у вас есть относительно формальное описание того, что значит «композиция» в терминологии ragel'а, то про это уже можно доказывать всякие интересные вещи.
                                                                        0
                                                                        Но, в любом случае, если у вас есть относительно формальное описание того, что значит «композиция» в терминологии ragel'а, то про это уже можно доказывать всякие интересные вещи.
                                                                        Можно. Но проще оказало не разрабатывать специализированную теорию, а просто посмотреть на результирующий DFA.
                                                      0
                                                      У меня такие функции (может, чуть короче) тоже имеются… Конечно, гордиться может и нечем. Но когда код лично твой, и ты понимаешь структуру — ты в нём не запутаешься. Вот если открыть его через год-два-три — уже может быть не очень приятно...)
                                                    +1
                                                    успеваемость по таким предметам, как чтение и математика, напрямую связана с рабочей памятью

                                                    Есть исследования утверждающие что "общий" интеллект нельзя свести к одному фактору:


                                                    http://www.cell.com/neuron/abstract/S0896-6273(12)00584-3


                                                    и полагаю что такие обширные области как математика или прикладное программирование требуют всех возможностей интеллекта для разных классов задач
                                                    и свести все кто одному фактору типа "быстрая память" будет ошибочным упрощением.

                                                      0

                                                      Причем тут "рабочая память", когда ПО пишут команды разработчиков. Код должен быть понятным и храниться в памяти компьютера, а не в голове конкретного разработчика. Тем-более код по статистике чаще читается, а не пишется. И читается он не только автором, но и его коллегами и т.д.
                                                      Длина метода в 100+ строк — это уже не есть хорошо. По этому поводу Макконелл еще давно писал. А его книга "Совершенный код" — это классика, к которой все прислушиваются. У него по поводу размера метода отдельный раздел есть, где он пишет, что метод в идеале должен полностью помещаться на экране монитора (50-80 строк).
                                                      Плюс к этому, рекомендуется заменять комментарии, методами с подходящим названием.
                                                      А держать методы в уме (что вылетит из бошки очень быстро) — это не правильно, как по мне.

                                                        0
                                                        Никакое название методов не поможет потом понять, для чего та или иная штука вообще была сделана. Ну вот метод что-то проверяет и называется проверкаЧегоНибудь, а дальше что? Зачем эта проверка была сделана? С какой целью? Что произойдет, если ее отключить? Почему проверка работала раньше, а сейчас не работает?
                                                          0
                                                          Если писать не на С, а на С++ с использованием хороших практик ООП, то такой проблемы не будет.
                                                            0
                                                            И каким образом практика ООП поможет пониманию неизвестной предметной области?
                                                            0

                                                            Смотрите названия методов, которые эту проверку вызывают :)

                                                              0
                                                              Может не помочь понять, почему она в конкретном случае возвращает именно то значение, которое возвращает (например, если вроде бы поймали баг, но не уверены, баг это, или так и было задумано). Всё равно придётся вниз по иерархии спускаться.
                                                      0

                                                      Имхо, память и ум вещи совсем разные. Можно много чего запомнить и держать в голове—это память, но что делать с тем, что у тебя запомнилось? нужно что то еще, например ум, что бы это все вытащить из памяти и скомпоновать все это

                                                      +1
                                                      В программировании нет сложных задач. Есть очень-очень-очень много средне-лёгких. Просто их очень много.
                                                        +14

                                                        Просто нужно придумать как из сложной задачи сделать кучу средне-лёгких. Это возможно самый сложный этап.

                                                          0
                                                          -
                                                            0
                                                            В масштабах человечества — наверное, нет. А в масштабах интеллекта конкретного программиста — задачи вполне могут быть на верхнем пределе способностей. Или выше, в патологических случаях ;)
                                                            +1
                                                            Ну прорешайте десяток-другой задач из project euler, что ли.
                                                            0
                                                            Вцелом — не очень согласен.
                                                            Если под отличником(-цей) понимать не зубрилу и выскочку (катати как и подлец только мужского рода слова), а способного мальчика или девочку, то точно не согласен что так будет брать производную. Будет расписывать по шагам а не заучивать формулу. У нас в университете даже это просто запрещалось делать. Поэтому решение примера чуть сложднее шекольного например по теме интегралы были на 4-х страницах с двух сторон в каждой клеточке.

                                                            Что касается магического числа семь. По этому поводу мне очень хорошо запомнилась байка одного из преподавателей прмышленного дизафна в политехе, где училась моя сестра. Вобщем-то тема была как раз о памяти. По поводу ворон например оказалось что сичтают до трех. Проверяли таким способом. Клали сыр в закрытое помещение куда входили по очереди и потом выходили 1, 2, 3 и 4 человека. Так вот вороны дожидались пока не выйдет 1, 2, 3 человека а на 4-м начали ошибаться.

                                                            По поводу человека проводили опыты с пастухами. Забирали у них из стада численностью 1,2,3 и т.п. голов одну корову и просили сказать все ли коровы на месте не пересчитывая их. Пастухи действтельно начинали ошибаться при стаде из 8 коров.

                                                            Потом сделали тот же оптыс с оленеводами. Дошли ждос стада в 1000 оленей и оленевод всегда точно отвечал типа Сохатого не хватает, однако.

                                                            Тут важен сам подход

                                                            А вот тот код написал троеччник или отличник это еще нужно доказать. С кодом функции в пару тысяч строк например я еще могу разобраться. Но если троечник начинает делать все «по науке» и разбивать 50 строк на модули, но не в стиле «раздляй и властвуй», а в стиле: разложи листики в конвертики, потом конвертики сложи в коробочки, коробчки положи в ящички, потом подойди к ящичкам и найди где лежит заначка. То это гораздо более утомительно. Прошлая Ваша публикация на данную тему кажется мне более инересной.
                                                              +2
                                                              Когда пример лёгкий, как тот, что я привёл в пример в начале поста — такой вполне можно решить в уме и записать сразу готовый ответ. Лично я постоянно так и делал, несмотря на требования расписывать решение по шагам. Ну или хотя бы пропускал половину шагов, потому что влом записывать.

                                                              А когда решение на четырёх страницах — его ни один отличник в уме не решит. Более того, каждый отличник (и я в том числе) будет сразу записывать именно по шагам, чтобы потом можно было проверить правильность решения.

                                                              Опыт с оленеводом немного к другой теме относится. Пастух — он чужих коров пасёт, и у него они действительно сидят в памяти, близкой к кратковременной. А оленевод своё стадо знает с пелёнок, и кратковременная память тут меньшую роль играет — он здесь на долговременную опирается.

                                                              Про код могу так ответить. «Отличник» и «троечник» здесь — условные категории. Надеюсь, их не будут воспринимать слишком буквально. Надо же было их как-то кратко назвать. Не писать же каждый раз «лица с хорошо развитым подвижным интеллектом» и «лица с не так хорошо развитым подвижным интеллектом».

                                                              Я просто хотел двинуть идею, что если мы видим длинный код — то написавший его человек обладает хорошо развитой кратковременной памятью. А значит, согласно исследованиям «некоторых учёных», и хорошо развитым подвижным интеллектом. И, скорее всего, — был отличником по математике.

                                                              А вот обратное — не всегда верно. Некоторые «отличники» (читай: «талантливые парни с хорошо развитой кратковременной памятью и подвижным интеллектом») — да, всё ещё продолжают писать длинные методы. А некоторые — уже набили достаточно шишек, и перешли к коротким.
                                                                +3

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

                                                                  0
                                                                  Да я понимаю о чем Вы хотели сказать. В первой Вашей статье была действительно интересная идея о том что у хорошего программиста-хорошиста код всега очень лаконичный и понятный. В большинстве же своем программисты как и нормальные люди троешники (написал бы троечники но похоже на кучеров троек получается что-то). Мне например сложнее всего вникать в суть не с длинной плохо написанной функици, а предположем 3 функций которые были плохо разбиты на 3 функции. Если Вы дадите троечнику задание разбей свой код на функции он это сделает на ту же самую тройку и будет уже тройка в квадрате.
                                                                +2
                                                                Некоторые утверждения статьи странные. Константы не нужно держать в голове, что они значат видно прямо из их названия в момент чтения. Насчёт хэшей — точно не уверен, как там в питоне, но в js есть только один вид хэша — получить объект/значение по ключу (и это логично). Причём что это за объект/значение следует прямо из названия хэша. Нужно помнить только сам объект, но и тут его название говорит само за себя: если оно во множественном числе, это массив, если это объект вида user или userObj, то это struct или экземпляр класса, если userId или userCount, то число, если isUser, то boolean и т. д.

                                                                А функции да, по возможности нужно разбивать на более мелкие — проанализировать 2 независимые функции по 10 строк намного проще, чем одну из 20 строк. Ну и конечно именование функций, параметров, переменных и т. д. очень важно (а особенно важен API).
                                                                  +12
                                                                  Программирование это навык на границе науки и искусства. Задача программиста: декомпозиция задачи на блоки, каждый из которых должно как можно эффективнее использовать повторно. Наука здесь в декомпозиции и решении задачи. Искусство в эмпирическом предсказании возможности повторного использования каждого отдельного блока решения. Этот тезис был известен даже в СССР. С тех пор не изменился.

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

                                                                  Беда в том, что из-за низкого порога входа такие вот программисты начинают проектировать свои собственные системы. Которые чуть более, чем полностью состоят из таких вот богоподобных функций с кучей предусловий и неясной механикой работы. В таких системах сложные задачи вполне решаются за 150 строк, если вы достаточно «вкурили» странную логику автора. Только вот это будет навык использования этой конкретной системы, а не навык программирования или решения задач.

                                                                  Если люди считают, что программирование это решение задач с помощью кода — они «кодеры», программисты низшего уровня. То есть, попросту, или хреновые, или начинающие специалисты. Даже отличники при переходе к реальной работе будут начинающими специалистами. Даже отличники будут совершать типичные ошибки новичков. По опыту могу сказать, что отличники чаще не готовы признать себя начинающими. А значит, склонны дольше оставаться таковыми. Отсюда возникает иллюзия ошибок, характерных именно для отличников. На деле же — обычная ошибка новичка.

                                                                  Кроме ошибки новичка здесь, несомненно, вина школьного образования в целом. Ибо это образование представляет программирование именно как написание кода. А на деле, написание кода — наименьшая и наиболее простая из задач программиста, вышедшего за пределы начального уровня. Если признать, что отличник это такой ученик, который наилучшим образом встроился в образовательную программу, очевидно, он будет тащить за собой больше всего ошибок этой самой программы.

                                                                  Мой коллега после прочтения статьи сразу же предположил, что из гениев, способных перепрыгивать огромные этапы решения, выйдут совсем хреновые программисты. Опровергну. Главная черта гения: умение предсказывать результат своих действий далеко вперёд. Если этот гений ещё и программист, он не будет подкладывать себе же грабли. Он скорее сразу разобьёт код на блоки, которые будут удобны для повторного использования ему самому.

                                                                  Из всего вышеописанного следует, что оценки когнитивной психологии в общем смысле здесь применять неверно. Они, несомненно, важны в деле программирования. Но важны в другом месте. Человеку, который может удержать в голове больше объектов, проще построить грамотную архитектуру. Проще разбить задачу на подзадачи наиболее оптимальным образом. В общем смысле, эффективная архитектура не должна оперировать больше, чем пятью объектами на каждом уровне. Это исходит именно из оценки когнитивной психологии. Достичь этого можно кучей методов композиции и уменьшения связности. Чем больше объектов в человек может удержать в голове в один момент времени, тем лучше он выделит наиболее сильные связи и устранит слабые. Самый важный навык — умение быстро переключаться между уровнями архитектуры: между переменными в функции, системными объектами и промежуточными уровнями абстракции. Но это уже тема для статьи, а комментарий как-то подзатянулся.
                                                                    0
                                                                    Вспомнился такой небольшой постскрипттум. Мой средний балл по математике в районе 3.7 по школе и вузу (матфаку, почему именно матфаку с такой оценкой — отдельная история). Я точно так же, как описано в статье для отличников, прыгал через этапы решения. Просто по рассеянности внимания где-то постоянно терял важные символы. Что характерно, забывал записать, а не применить. Чем больше меня в этом упрекали — тем хуже становилась оценка по математике. Так что, я бы поостерёгся с термином «отличник» в таком контексте.
                                                                      0
                                                                      Гений — это далеко после «отличника». Кто же их знает, этих гениев? Может, не все умеют предсказывать результаты своих действий далеко вперёд? Я не гений, подтвердить или опровергнуть не могу.

                                                                      А про когнитивные метрики — так я же как раз об этом. Как сообщить кодеру, что пора завязывать кодить и настало время подумать об архитектуре? Вот тебе метрика: как только вылезет выше семи — начинай грамотно проектировать. И кстати, такая когнитивная метрика сама по себе работать не будет, а только в комплексе со всеми прочими метриками. Потому что про связность и зацепление тоже надо помнить, и прочее, и прочее.
                                                                        +2
                                                                        Про кодера скорее идти стоит от обратного. Плох тот солдат, что не мечтает стать генералом. Вот когда в этих попытках стать генералом появляются хорошие метрики — тогда и время повысить его наконец. И мы приходим к обычной для творческой среды проблеме наставничества: а откуда простой кодер возьмёт задачи, в которых сможет показать навыки выше уровня кодера?

                                                                        Тут возникает другой аспект: какому-нибудь «сеньору», как правило, очевидно, где решение хорошее, а где — плохое, без всяких метрик когнитивной психологии. Эти самые метрики нужны для людей, которые в самой профессии не смыслят. То есть, для внешних управленцев. И мы внезапно оказываемся ну очень далеко от исходной темы.
                                                                      +2
                                                                      Да это всем известный факт.
                                                                      Кстати, кто-нибудь смотрел исходники, которые выложил ВК? Там ведь сплошь гении и наверняка есть кучи гениальности по всем проектам.
                                                                      github.com/vk-com/kphp-kdb/blob/master/queue/queue-engine.c#L1486
                                                                        +2
                                                                        ёлки, ну и вырвиглаз.
                                                                        а дело не только в длинне или непонятности синтаксиса но и в общих правилах структурирования кода
                                                                          –2
                                                                          Ой, тоже мне нашли беду. Это ж парсер параметров. Сравните с GNU LD'шным.

                                                                          Простая функция, пусть и довольно длинная.
                                                                            +2
                                                                            Наименование переменных :(

                                                                            Первая же строчка:
                                                                            int r, c;

                                                                            Что такое r и что такое c? Это первая строчка функции! Хотя бы коммент оставили.

                                                                            Хотя фиг знает, может это общепринятное соглашение, и эти r и c используются 100 раз в коде, но что-то я сомневаюсь, что они писали 100 похожих парсеров.

                                                                            И почему функция парсера конфига ничего не возвращает? Причём с первой же строчки начинает менять глобальные переменные. Глобальные надо менять, когда реально состояние приложения изменилось. А тут всего лишь парсер конфига.
                                                                              0
                                                                              Что такое r и что такое c? Это первая строчка функции! Хотя бы коммент оставили.
                                                                              Зачем? Посмотрите где они инициализируются — и всё станет ясно.

                                                                              C99 ещё не везде, так что да, приходится описывать переменные в начале функции, а не там, где они нужны…

                                                                              Хотя фиг знает, может это общепринятное соглашение, и эти r и c используются 100 раз в коде, но что-то я сомневаюсь, что они писали 100 похожих парсеров.
                                                                              Ноборот — они там 5 строчках используются. И в них легко понять — что это за переменные и что они делают.

                                                                              Вы пытаетесь читать программу на C89 опираясь на подходы какого-то другого языка (C++? Python?) — отсюда сложности.

                                                                              Глобальные надо менять, когда реально состояние приложения изменилось.
                                                                              Опять-таки — это подход современных языков и современных приложений. Которые, типа, могут прочитать конфиг и поменять его «на лету» (ага, три раза). Программки на C обычно читают конфиг один раз и если он неправильный — выдают ошибку. Зачем тут лишние сущности устраивать?
                                                                                +1
                                                                                Понял, да, я применял подходы других языков. Просто я обычно даю переменным имена, которые явно показывают, для чего они предназначены. Сокращать до одной буквы очень редко имеет смысл.
                                                                                  +2
                                                                                  Просто я обычно даю переменным имена, которые явно показывают, для чего они предназначены. Сокращать до одной буквы очень редко имеет смысл.
                                                                                  Старая привычка от которой просто сложно отказаться. Как с курением.

                                                                                  Старые мониторы (80x24), нет IntelliSense… а переменные нужно обьявлять в начале функции в C89 (до первого оператора). Когда-то это имело смысл. А если ты пишешь 10+ лет в таком стиле, то уже как-то и не видишь ничего страшного в одной букве. c(haracter), s(tring), p(ointer) — наиболее частые сокращения. «r»… думаю r(ead result) имелся в виду.

                                                                                  То есть да — не могу сказать что это прям код — верх изящества, но он вполне в духе «old school» и не сказать чтобы уж прям ужас-ужас…

                                                                                  P.S. Вообще, конечно же, лучше описывать переменные где используешь… C99 и C++ не зря придумали (и давно), но грехи (Windows и MSVC) не пускают…
                                                                                    +2
                                                                                    c — character, s — string, f — function, p — pointer — это всё нормально. Но там попался какой-то «r».

                                                                                    Но ещё в своём языке я привык объявлять переменные по мере использования. Просто const c — такого не бывает, а вот const c = s[i] — может быть, т. е. несмотря на короткое имя, назначение очень понятно.

                                                                                    Но таких коротких переменных можно пересчитать по пальцем, даже a — array — не всегда хороший вариант, потому что если это не совсем временная переменная, лучше назвать users, к примеру. А там все переменные так названы =)
                                                                                      +2
                                                                                      Один из признаков хорошего программиста, на мой взгляд — умение переучиваться и обновлять свои привычки в соответствии с изменяющимися условиями.

                                                                                      Это было бы «ну так себе нормальненько», если бы этому коду было лет 30, а не 9.
                                                                                    0
                                                                                    C99 ещё не везде, так что да, приходится описывать переменные в начале функции, а не там, где они нужны…

                                                                                    Конкретно в этом коде переменные в середине функции объявляются. (int was_created = -1;) — мне это сразу резануло глаза. Не знаю что в этой "фиче" хорошего, тоже писал так 10 лет назад по глупости, потом перестал.

                                                                                      +1
                                                                                      Не знаю что в этой «фиче» хорошего, тоже писал так 10 лет назад по глупости, потом перестал.
                                                                                      Хорошего то, что легко можно увидеть где и как эта переменная используется.

                                                                                      А то, что тут у нас смесь C89 и C99 стилей — это как раз плохо. Надо бы выбрать что-нибудь одно, по хорошему-то…
                                                                                      +1
                                                                                      C99 ещё не везде, так что да, приходится описывать переменные в начале функции, а не там, где они нужны…

                                                                                      Только мне кажется, что проект, который «consisting of efficient PHP-to-C++ translator called «KPHP» or «KittenPHP», and several auxiliary high-performance «engines» (specialized non-relational databases), needed for its deployment», может позволит себе опираться хоть на С++11, хоть на хаскель.

                                                                                      Опять-таки — это подход современных языков и современных приложений. Которые, типа, могут прочитать конфиг и поменять его «на лету» (ага, три раза). Программки на C обычно читают конфиг один раз и если он неправильный — выдают ошибку. Зачем тут лишние сущности устраивать?

                                                                                      Потому что о программах без глобального состояния проще, как это говорится, reason about. Можно посмотреть на входы и выходы каждой функции и сделать выводы, с чем она работает и от чего она зависит, а также что она меняет и на что может повлиять.

                                                                                      Чтение много раз — дело десятое.
                                                                                        0
                                                                                        Потому что о программах без глобального состояния проще

                                                                                        Не всегда. А в случае высоконагруженного специализированного процесса — почти всегда наоборот. А если точнее, то "глобальное состояние" там — это состояние всего сервера (железки) с операционной системой. А любой процесс — уже локален, считайте что это экземпляр объекта "сервер" со своими полями. Вас же не смущает, когда методы класса меняют состояние класса, а не только свои локальные переменные? А оверхед "модного ооп-кода" убрали — он тут ни к чему, экземпляр этого объекта строго один на процесс, а может быть и на серверную железку, и поддержка нескольких экземпляров одновременно только создаёт лишнюю нагрузку (например обратиться к this->a всегда сложнее чем к глобальной переменной a).

                                                                                          0
                                                                                          А в случае высоконагруженного специализированного процесса — почти всегда наоборот. А если точнее, то «глобальное состояние» там — это состояние всего сервера (железки) с операционной системой.

                                                                                          Почему специализированный у нас процесс, а состояние — вся железка?

                                                                                          Вас же не смущает, когда методы класса меняют состояние класса, а не только свои локальные переменные?

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

                                                                                          И классы с большим количеством состояния меня тоже смущают.

                                                                                          например обратиться к this->a всегда сложнее чем к глобальной переменной a

                                                                                          Если объект на стеке, то нет. Если объект на хипе и короткоживущий, то по сравнению с лишним дёрганием хип менеджера это ничто. Если объект на хипе и долгоживущий и в горячем коде, то бранч индирекшон предиктор быстро обучится.
                                                                                            0
                                                                                            Почему специализированный у нас процесс, а состояние — вся железка?

                                                                                            Потому что там ещё было слово "высоконагруженный". В таких случаях на каждого функционального демона выделяется своя железка, а то и несколько железок (как в вк). И цель оптимизаций — выжать максимум из железки для конкретного одного демона, и полностью плевать на сценарии, где на том же компе будет запущено что-то ещё.


                                                                                            то бранч индирекшон предиктор быстро обучится.

                                                                                            Вам всё равно придётся держать лишний занятый регистр под этот this и тратить несколько лишних байт кода чтобы в него загрузить нужное значение. Может быть это и не особо большой оверхед (хотя насчёт занятого регистра мне всё-таки кажется что заметный) но польза то от этого вообще нулевая в данном случае.

                                                                                              0
                                                                                              Потому что там ещё было слово «высоконагруженный». В таких случаях на каждого функционального демона выделяется своя железка, а то и несколько железок (как в вк).

                                                                                              И выделяется одна железка под высоконагруженный парсер конфигов?

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

                                                                                              Вы правда уверены, что парсинг конфигов — горячая часть этого сервиса?

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

                                                                                              И да, индирекшон всё равно есть.
                                                                                                0
                                                                                                И да, индирекшон всё равно есть.
                                                                                                Где? %rip это ж не регистр, это просто запись такая, адрес в декодере вычисляется…
                                                                                                  0
                                                                                                  В смысле, просто запись такая? Его ж линкер (или ОС при загрузке, если это шаредлиба и PIC, но это не шаредлиба) подправляет.
                                                                                                    0
                                                                                                    Нет. Его линкер/загрузчик не подправляет, учите матчасть.

                                                                                                    В 32-битном режиме адрес задаётся прямо в команде. А в 64 битах так сделать нельзя: требовалось бы 64-битное смещение, что расточительно.

                                                                                                    Потому кроме режима абсолютной адресации, который позволяет добраться только до первых 4GB (на самом деле первых 2GB и последних 2GB, но не суть), в x86-64 есть ещё и «адрес относительно текущей инструкции». И в 64-битном режиме именно он и используется обычно. Вот именно его мы видим по вашей ссылке.

                                                                                                    Дополнительного времени при работе процессора он не требует и пайплайн не сбивает: адрес инструкции декодеру известен, сумматор — это меньше тысячи транзисторов, так что μop получает уже сразу адрес из декодера.

                                                                                                    А при обращении к «обычному» регистру ничего подобного сделать нельзя. Там — нужно «вынуть» адрес из регистра, возможны разного рода задержки и прочее.
                                                                                                      0
                                                                                                      Потому кроме режима абсолютной адресации, который позволяет добраться только до первых 4GB (на самом деле первых 2GB и последних 2GB, но не суть), есть ещё и «адрес относительно текущей инструкции». И в 64-битном режиме именно он и используется обычно. Вот именно его мы видим по вашей ссылке.

                                                                                                      А адрес глобальной переменной относительно текущей инструкции на каком этапе становится известен?

                                                                                                      А при обращении к «обычному» регистру ничего подобного сделать нельзя. Там — нужно «вынуть» адрес из регистра, воможны разного рода задержки и прочее.

                                                                                                      Какого рода задержки? В горячем коде оно и так всё будет хорошо.
                                                                                                        0
                                                                                                        А адрес глобальной переменной относительно текущей инструкции на каком этапе становится известен?
                                                                                                        Зависит от настроек компиляции. Обычно на этапе статической линковки, но бывают и исключения. Смотрим сюда. Первое окошечко — главная программа, там индирекции нет. Второе окошечко — разделяемая библиотека, скомпилированная нормальным программистом, там тоже индирекции нет. Третье окошечко — варинт «на отвали», там индирекция есть.

                                                                                                        Какого рода задержки?
                                                                                                        Хотя бы уже тот факт, что вы задействуете больше исполняемых устройств может аукнуться.
                                                                                                        В горячем коде оно и так всё будет хорошо.
                                                                                                        Нет. В зависимости от режима адресации могут получиться задержки на такт из-за необходимости пересылки адреса из блока вычисляющего адрес в ALU. Для «горячего» кода — это прилично.

                                                                                                        P.S. Впрочем обычно потеря 10-15% скорости не так критична, как потеря безопасности. Потому глобальные переменные сейчас часто не используют даже для очень «горячего кода». Но таки задержки эти есть — даже на самых современных процессорах.
                                                                                                          0
                                                                                                          Обычно на этапе статической линковки, но бывают и исключения.

                                                                                                          Нет. Его линкер/загрузчик не подправляет, учите матчасть.

                                                                                                          Ну ладно :)

                                                                                                          Это уже лучше на личной машине попробовать, из godbolt'а мне не удалось выковырять нужный выход, но соберите на x86_64 бинарь из двух файлов (с пустой doFoo() рядом, например) и сравните его дизасм с дизасмом объектного файла из одного только main. Во втором случае там будет что-то вроде 0(%rip), который именно что заполняется линкером.

                                                                                                          А загрузчик в ОС — как раз эти самые ваши исключения, по крайней мере, для не-PIC-кода (я в исходном комментарии описался на эту тему, да).

                                                                                                          Ну и опять же,
                                                                                                          Первое окошечко — главная программа, там индирекции нет.

                                                                                                          С точностью до определений, считать foo(%rip) за индирекцию или нет.

                                                                                                          Хотя бы уже тот факт, что вы задействуете больше исполняемых устройств может аукнуться.

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

                                                                                                          В зависимости от режима адресации могут получиться задержки на такт из-за необходимости пересылки адреса из блока вычисляющего адрес в ALU. Для «горячего» кода — это прилично.

                                                                                                          Если класс на стеке, то там скорее всего оно всё относительно (%rsp), и опыт показывает, что можно забить.

                                                                                                          Впрочем обычно потеря 10-15% скорости не так критична, как потеря безопасности. Потому глобальные переменные сейчас часто не используют даже для очень «горячего кода». Но таки задержки эти есть — даже на самых современных процессорах.

                                                                                                          Самое главное-то в контексте дискуссии я изначально забыл, сбился с этим человеческим reason about code. Компилятору-то тоже проще оказывается о коде рассуждать: посчитайте количество сторов в m_x и в a здесь. То есть, не, понятно, если static добавить, то всё становится очень сильно лучше, но в исходном-то коде никакими статиками и не пахнет!

                                                                                                          Но как-то мы уже далеко не в ту степь зашли, ИМХО, и всерьёз обсуждать качество кода для парсинга опций в контексте высоконагруженных серверов — это та ещё импликация, но выводить её некорректность лень и непродуктивно.
                                                                                                            0
                                                                                                            Это уже лучше на личной машине попробовать, из godbolt'а мне не удалось выковырять нужный выход, но соберите на x86_64 бинарь из двух файлов (с пустой doFoo() рядом, например) и сравните его дизасм с дизасмом объектного файла из одного только main. Во втором случае там будет что-то вроде 0(%rip), который именно что заполняется линкером.
                                                                                                            О боги. Вам про разницу между статическим и динамическим линкером никто не рассказывал? И про то, что relocationы бывают динамическими и статическими? О чём мы тогда, вообще, тут говорим?

                                                                                                            Конечно в обьектном файле там будет 0(%rip)! А вот уже в исполняемом файле или в динамической библиотеке — там будет конкретное число.

                                                                                                            Вы, я извиняюсь, обьектный файл собрались запускать или как?

                                                                                                            А загрузчик в ОС — как раз эти самые ваши исключения, по крайней мере, для не-PIC-кода (я в исходном комментарии описался на эту тему, да).
                                                                                                            Ну нельзя так. Просто нельзя. Если вы понятия не имеете о том, как работают обьектные файлы, динамические библиотеки и загрузчик OS — то не очень понятно, о чём дальше говорить. Почитайте хотя бы Drepperа, что ли…

                                                                                                            С точностью до определений, считать foo(%rip) за индирекцию или нет.
                                                                                                            Давайте не считать ангелов на кончике иглы, а? Мы вроде об эффективности говорим. То, что существует в промежуточном представлении, но не в итоговом бинарнике на эффективность ну никак не может влиять. А вопросы терминологии — не так важны и интересны.

                                                                                                            Если количество считанных из конфига байт — такой же горячий код, то ну я вообще не знаю, что тут ещё можно сказать.
                                                                                                            Какие-нибудь флаги вполне могут проверяться внутри «горячего» кода, а заводить часть конфига в виде глобальных переменных, а часть — в виде локальный или какой-нибудь структуры — это уже перебор, согласитесь.

                                                                                                            Если класс на стеке, то там скорее всего оно всё относительно (%rsp), и опыт показывает, что можно забить.
                                                                                                            На стеке у вас скорее всего будет какой-нибудь другой класс. Вряд лы вы будете передавать класс с настройками программы по значению (ну то есть можно, конечно — но это ещё большее извращение, чем глобальные переменные).

                                                                                                            Но как-то мы уже далеко не в ту степь зашли, ИМХО, и всерьёз обсуждать качество кода для парсинга опций в контексте высоконагруженных серверов — это та ещё импликация, но выводить её некорректность лень и непродуктивно.
                                                                                                            Это вы не в ту степь зашли, судя по вашим примерам.

                                                                                                            Вы уже не просто «за деревьями не видите леса», а обсуждаете какие-то трещинки на поверхности коры, не обращая внимание на то, является ли этот кусок коры ещё частью дерева или нет.

                                                                                                            Давайте я попробую вам лес показать? Это же несложно!

                                                                                                            1. Время разбора конфи гурационного файла — мало кого интересует. За исключением очень редких и патологических случаев — это очень быстро даже если конфигурационные файлы большие и сложные.
                                                                                                            2. Время обращения к данным (флагам и разным настройкам), напротив, может быть очень критически важным и может случаться даже в самых внутренних циклах (например какой-нибудь флаг может влиять на количество вызовов VRECPS в вашем алгоритме).
                                                                                                            3. Если ваша программа не требует хитрых «атомарных» изменений конфига «на лету», то результат разбора конфига разумно поместить в набор глобальных переменных
                                                                                                            4. А вот уже временные переменные, предназначенные только для разбора конфига — лучше бы в глобальные переменные не класть. Тут я согласен. Ибо на скорость не влияют, а место занимают.

                                                                                                            В это смысле у GNU LD парсер почище будет. Я больше про него думал, чем про пример от VK. Пример от VK, конечно, доставляет. Код в стиле C89, но при этом с кучей C99 фич — это нечто. Кто и зачем такое пишет — мне непонятно.

                                                                                                            Зачем rpc_default_ct глобал — я могу понять. И даже то, что парсер конфигов всё прямо туда пишет — не страшно. Но вот зачем делать глобальной переменную config_buff — загадка, этого я ничем обьяснить не могу…
                                                                                                              0
                                                                                                              О боги. Вам про разницу между статическим и динамическим линкером никто не рассказывал? И про то, что relocationы бывают динамическими и статическими? О чём мы тогда, вообще, тут говорим?

                                                                                                              Не знаю, о чём вы говорите, я-то изначально про ровно это и сказал (хотя сейчас перечитал, да, кривовато выглядит, сорри), это вы зачем-то сказали, что так не бывает.

                                                                                                              Если вы понятия не имеете о том, как работают обьектные файлы, динамические библиотеки и загрузчик OS — то не очень понятно, о чём дальше говорить.

                                                                                                              И GOT и прочие механизмы для релокейшнов как обеспечивают это самое ваше конкретное число, ради интереса? Или я опять не в ту степь зашёл, и это всё не то?

                                                                                                              То, что существует в промежуточном представлении, но не в итоговом бинарнике на эффективность ну никак не может влиять.

                                                                                                              Как-то я упустил пропажу таких выражений из итогового бинарника. Если с малым влиянием ввиду лёгкости работы для декодера (но это нужно бенчмаркать) я ещё готов согласиться, то это уже как-то перебор.

                                                                                                              Какие-нибудь флаги вполне могут проверяться внутри «горячего» кода, а заводить часть конфига в виде глобальных переменных, а часть — в виде локальный или какой-нибудь структуры — это уже перебор, согласитесь.

                                                                                                              Эти флаги можно отдельно внутрь горячего кода и передать (и я бы сказал, что и нужно).

                                                                                                              Вы уже не просто «за деревьями не видите леса», а обсуждаете какие-то трещинки на поверхности коры, не обращая внимание на то, является ли этот кусок коры ещё частью дерева или нет.

                                                                                                              Я бы сказал, что такое обсуждение началось, когда речь зашла о пользе глобальных переменных для состояния конфига для производительности кода, так что я лишь поддерживаю общий накал страстей :)

                                                                                                              Давайте я попробую вам лес показать? Это же несложно!

                                                                                                              1 — да.
                                                                                                              2 — да, конечно.
                                                                                                              3 — нет. Это не дело кода, парсящего конфиги. Это вообще не его забота (как он и не должен парсить хостнеймы или чего-то ещё). Если вдруг профайлер показывает, что вот он, ваш звёздный час, тот самый случай раз в десятилетие, когда чтение локальной переменной или параметра ну хотя бы статистически значимо влияет на производительность горячего кода, и с глобальной переменной быстрее — ну передайте этот несчастный параметр в обёртку, которая выставит какую угодно глобальную переменную, засунутую в анонимный неймспейс, помеченную как статик, или ещё что. И рядом в комментарии напишите только, зачем это сделано, какая методика оценки, и как оно оценивалось.

                                                                                                              Зачем rpc_default_ct глобал — я могу понять.

                                                                                                              А я не могу. Я вообще много чего не могу понять, но это уже придирками выглядит на общем фоне.
                                                                                                              Почему 63 байта на длину хоста магическим числом? Где ещё 63, 62 и 64 вылезает из проверок?
                                                                                                              Почему после некоторых syntax есть return?
                                                                                                              Что за танцы с s и c на строках 1520-1521 и потом на 1534-1535? За что так ненавидеть читающего код?
                                                                                                              А тестами это вообще покрыто? А как автор покрывать будет? Куда послал, я не расслышал?

                                                                                                              Но я не отличник, у меня тройка по истории была. И из-за уравнений в частных производных чуть из физтеха не выперли.
                                                                                                                –1
                                                                                                                И GOT и прочие механизмы для релокейшнов как обеспечивают это самое ваше конкретное число, ради интереса? Или я опять не в ту степь зашёл, и это всё не то?
                                                                                                                Нет — это уже почти то. GOT «включается в работу» когда ваш символ экспотрируется из библиотеки. Вот эта ссылочка — она ровно про это. Третий вариант.

                                                                                                                И там хорошо видно — у вас либо GOT, две инструкции и %rbx, либо одна инструкция, прямой доступ и %rip. То есть да — если у вас переменнай экспортируется из библиотеки, то доступ к ней будет через индирекцию — но и обычный регистр, не %rip

                                                                                                                Как-то я упустил пропажу таких выражений из итогового бинарника. Если с малым влиянием ввиду лёгкости работы для декодера (но это нужно бенчмаркать) я ещё готов согласиться, то это уже как-то перебор.
                                                                                                                Из бинарника пропадают релокейшены. Правда-правда. Ну проверьте:
                                                                                                                $ cat test.c
                                                                                                                int i;
                                                                                                                int main() {
                                                                                                                  return i;
                                                                                                                }
                                                                                                                $ gcc -O3 -c test.c -o test.o
                                                                                                                $ objdump -dr test.o
                                                                                                                
                                                                                                                test.o:     file format elf64-x86-64
                                                                                                                
                                                                                                                
                                                                                                                Disassembly of section .text.startup:
                                                                                                                
                                                                                                                0000000000000000 <main>:
                                                                                                                   0:	8b 05 00 00 00 00    	mov    0x0(%rip),%eax        # 6 <main+0x6>
                                                                                                                			2: R_X86_64_PC32	i-0x4
                                                                                                                   6:	c3                   	retq   
                                                                                                                $ gcc -O3 test.o -o test
                                                                                                                $ objdump -dR test 
                                                                                                                
                                                                                                                test:     file format elf64-x86-64
                                                                                                                
                                                                                                                
                                                                                                                Disassembly of section .init:
                                                                                                                
                                                                                                                ...
                                                                                                                Disassembly of section .text:
                                                                                                                
                                                                                                                0000000000000530 <main>:
                                                                                                                 530:   8b 05 f6 0a 20 00       mov    0x200af6(%rip),%eax        # 20102c <i>
                                                                                                                 536:   c3                      retq   
                                                                                                                 537:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
                                                                                                                 53e:   00 00 
                                                                                                                ...
                                                                                                                


                                                                                                                Как видите всё было сделано на этапе сборки бинарника, ничего для динамического линкера не осталось…

                                                                                                                Эти флаги можно отдельно внутрь горячего кода и передать (и я бы сказал, что и нужно).
                                                                                                                Ну это вопрос очень и очень философский. Глобальный флаг — быстрее. Передача в горячий код — медленнее, но более гибко.

                                                                                                                3 — нет. Это не дело кода, парсящего конфиги.
                                                                                                                А почему, собственно?

                                                                                                                Я, как бы человек простой. Если в меня начинают бросаться всякими вумными словами типа «неSOLIDно», «нарушение инкапсуляции» и прочее, то я обычно останавливаю этот поток сознания и прошу снизойти до «простых смертных». И описать пример цепочки начиная с бизнес-требований.

                                                                                                                Ну то есть «если мы сделаем по-другому и владелец нашего бизнеса потребует X, для чего нам потребуется Y и мы можем это сделать без переписывания половины программы только если у нас будет выполняться условие Z»… ну и кончаться это должно заключением «вот поэтому -это не дело кода, парсящего конфиги». Иначе — это архитектурная астронавтика какая-то.

                                                                                                                И рядом в комментарии напишите только, зачем это сделано, какая методика оценки, и как оно оценивалось.
                                                                                                                Извините — но нет. Вы предлагаете заменить простой, понятный, быстрый код на нечто более сложное, менее удобное в использовании и менее понятное. Так что это вам нужно обьяснять — какую проблему вы, тем самым, решаете и чего хотите добиться… после чего уже можно оценить вероятность того, что такая проблема кому-то в работе реально встретится…

                                                                                                                Почему 63 байта на длину хоста магическим числом?
                                                                                                                RFC1025: labels must be 63 characters or less.

                                                                                                                Где ещё 63, 62 и 64 вылезает из проверок?
                                                                                                                Не понял вопроса.

                                                                                                                Что за танцы с s и c на строках 1520-1521 и потом на 1534-1535? За что так ненавидеть читающего код?
                                                                                                                Кто вам сказал, что читающего код здесь кто-то ненавидит? gethostbyname требует там '\0', а мы там имеем что-то другое… что такого странного в том, чтобы записать туда требуемый '\0', а потом вернуть всё «как было»?

                                                                                                                А тестами это вообще покрыто? А как автор покрывать будет? Куда послал, я не расслышал?
                                                                                                                А для вас тесты — как религия? Или? Они, вообще-то, средство, не цель.

                                                                                                                То есть да, можно сделать этого код более модульным, более удобным для рефакторинга и прочее… Но это точно окупится? Человек, могущий менять конфиги у вас на сервере — и так, в общем-то, имеет над ним более-менее полный контроль… так зачем всё это дело обильно покрывать тестами и, скажем, фаззить? С чем боремся-то?
                                                                                                                  0
                                                                                                                  И там хорошо видно — у вас либо GOT, две инструкции и %rbx, либо одна инструкция, прямой доступ и %rip. То есть да — если у вас переменнай экспортируется из библиотеки, то доступ к ней будет через индирекцию — но и обычный регистр, не %rip

                                                                                                                  И кто занимается подстановкой правильных значений в GOT? Кто занимается подстановкой правильных значений перед %rip? К чему тогда было ваше исходное, что линкер или загрузчик этим не занимаются?

                                                                                                                  Из бинарника пропадают релокейшены. Правда-правда.

                                                                                                                  Я говорил о foo(%rip).

                                                                                                                  Ну это вопрос очень и очень философский. Глобальный флаг — быстрее. Передача в горячий код — медленнее, но более гибко.

                                                                                                                  Ну хоть парсер опций-то у вас отделён от вычисляющего кода, и они не перемежаются, аки вермишель? Соответственно, какая-то высокоуровневая обёртка над горячим кодом может решить, передать самому коду это как параметр, или выставить одному ему известную глобальную переменную, или ещё что сделать?

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

                                                                                                                  Если в меня начинают бросаться всякими вумными словами типа «неSOLIDно», «нарушение инкапсуляции» и прочее, то я обычно останавливаю этот поток сознания и прошу снизойти до «простых смертных». И описать пример цепочки начиная с бизнес-требований.

                                                                                                                  SOLID я сам не знаю и не понимаю, но вот представлений о том, что чем тоньше интерфейсы между системами, тем проще их анализировать, валидировать и вообще читать, на самом деле достаточно. И я бы не хотел работать в компании, где написание поддерживаемого кода не входит в список требований или даже этаких околокультурных ценностей.

                                                                                                                  Иначе — это архитектурная астронавтика какая-то.

                                                                                                                  Архитектурная астронавтика начинается сильно позже, когда начинают писать абстрактные фабрики декораторов парсеров конфигов. Изоляция систем и сужение интерфейсов между ними — естественное требование.

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

                                                                                                                  Чем в коде больше глобального неохраняемого состояния (и вообще чем больше состояния, но то такое), тем он менее простой и понятный.

                                                                                                                  Кто вам сказал, что читающего код здесь кто-то ненавидит? gethostbyname требует там '\0', а мы там имеем что-то другое… что такого странного в том, чтобы записать туда требуемый '\0', а потом вернуть всё «как было»?

                                                                                                                  Прекрасный пример, кстати. Ну, то есть, мне это тоже стало понятно, но не через 5 и даже не через 10 секунд чтения кода. А всё потому, что чтение хостнейма и его валидация размазана что по kbd_gethostbyname (почему бы ей не передавать рейндж указатель на начало и конец строки, чёрт возьми?) и по вызывающей функции.

                                                                                                                  Кстати, в kbd_gethostbyname есть обработка случая, когда длина строки больше или равна 128. Так что искомая проверка на 63 символа — это баг? Или тут должна быть не kbd_gethostbyname? Или тут может быть не только хостнейм? Или что вообще почему тут так?

                                                                                                                  А для вас тесты — как религия? Или? Они, вообще-то, средство, не цель.

                                                                                                                  Если бы я писал парсер так, мне бы в голову не пришло его тестировать, тайпчекер всё оттестирует. Но если у меня тут сырая работа и беготня по массиву байт с затиранием их нулями по необходимости — это лучше таки обложить тестами. Как средство помощи самому себе в разработке.

                                                                                                                  Человек, могущий менять конфиги у вас на сервере — и так, в общем-то, имеет над ним более-менее полный контроль… так зачем всё это дело обильно покрывать тестами и, скажем, фаззить? С чем боремся-то?

                                                                                                                  Потому что кривая работа с памятью — это не только всегда свежие 0-day, но и ваш личный геморрой по отладке багов, проявляющихся через 10 вызовов функций, 10 секунд или 10 часов в совсем другом модуле. Спасибо, я этого в своё время наелся достаточно.
                                                                                                                    0
                                                                                                                    И кто занимается подстановкой правильных значений в GOT?
                                                                                                                    Динамический загрузчик во время запуска программы.

                                                                                                                    Кто занимается подстановкой правильных значений перед %rip?
                                                                                                                    Статический линкер во время сборки программы. В готовой программе, в бинарнике, там уже ничего не меняется никогда.

                                                                                                                    Из бинарника пропадают релокейшены. Правда-правда.
                                                                                                                    Я говорил о foo(%rip).
                                                                                                                    В бинарнике — это не релокейшен. Когда линкер из нескольких обьектных файлов делает бинарник там записывается фиксированное число, которое больше уже никогда не меняется. Что там было на этапе сборки программы — никого не волнует, мы же всё это обсуждаем в контексте влияния на скорость.

                                                                                                                    Ну хоть парсер опций-то у вас отделён от вычисляющего кода, и они не перемежаются, аки вермишель?
                                                                                                                    Отделён, конечно.

                                                                                                                    Соответственно, какая-то высокоуровневая обёртка над горячим кодом может решить, передать самому коду это как параметр, или выставить одному ему известную глобальную переменную, или ещё что сделать?
                                                                                                                    А это зачем? Пользователь программы может задать флаг, если хочет, а обёртке это зачем?

                                                                                                                    И по моему опыту работы с другими людьми, те, кто любит обмазывать код глобальными переменными и всякими однобуквенными обозначениями, до оптимизации алгоритма просто не доходят, потому что очень часто тонут в неподдерживаемом коде.
                                                                                                                    Не знаю, не знаю. У нас этим целые команды занимаются. Хотя однобуквенные обозначения у нас не в чести…

                                                                                                                    Изоляция систем и сужение интерфейсов между ними — естественное требование.
                                                                                                                    И вот тут, как раз, глобальные флаги — отличная вещь. Ибо когда «высокоуровневая обёртка над горячим кодом может [что-то там] решить» — это как раз и нарушает принцип изоляции.

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

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

                                                                                                                    Подход «флаги глобальны, читать их может кто угодно, но менять может только пользователь программы» — работает вполне надёжно. 3325 флагов у программы копирования файлов — никого особо не напрягают, так как все их мало кому приходится «дёргать» (думаю что практически — никому).

                                                                                                                    А всё потому, что чтение хостнейма и его валидация размазана что по kbd_gethostbyname (почему бы ей не передавать рейндж указатель на начало и конец строки, чёрт возьми?) и по вызывающей функции.
                                                                                                                    В С функции получают строку закрытую нулём. gethostbyname — не исключение. Можно было бы создать язык, где они принимали бы рейнджи… но это уже будет не C.

                                                                                                                    Кстати, в kbd_gethostbyname есть обработка случая, когда длина строки больше или равна 128. Так что искомая проверка на 63 символа — это баг? Или тут должна быть не kbd_gethostbyname? Или тут может быть не только хостнейм? Или что вообще почему тут так?
                                                                                                                    Можете посмотреть на код, однако. Или нет? kbd_gethostbyname запросы кеширует, но только для коротких (не более 128 символов) имён. Для длинных — пропускает как есть. Это механизм вообще не имеет никакого отношения к ограничению на 64 символа, которые приходят прямо из DNS'а. В главной программе допускаются только весьма ограниченное множество «правильных» имён хостов… хотя непонятно — зачем их на этом уровне-то проверять, когда gethostbyname(3) это сама должна сделать.

                                                                                                                    Потому что кривая работа с памятью — это не только всегда свежие 0-day, но и ваш личный геморрой по отладке багов, проявляющихся через 10 вызовов функций, 10 секунд или 10 часов в совсем другом модуле. Спасибо, я этого в своё время наелся достаточно.
                                                                                                                    Для этого Asan есть.

                                                                                                                    P.S. Вот, кстати, kdb_gethostbyname огорчила больше. В частности тем, что граница в 128 символов для кешированного имени выглядит какой-то абсолютно произвольной… и встречается в коде далеко не в одном месте. Тут уже и константу бы не грех сделать…
                                                                                                                      –1
                                                                                                                      Динамический загрузчик во время запуска программы.

                                                                                                                      Статический линкер во время сборки программы. В готовой программе, в бинарнике, там уже ничего не меняется никогда.

                                                                                                                      И чем это отличается от того, что я изначально сказал? Ну, кроме вышеупомянутой неаккуратности, конечно, но придираться к ней — перебор, ИМХО.


                                                                                                                      В бинарнике — это не релокейшен.

                                                                                                                      У нас снова терминологический спор :(


                                                                                                                      А это зачем? Пользователь программы может задать флаг, если хочет, а обёртке это зачем?

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


                                                                                                                      Не знаю, не знаю. У нас этим целые команды занимаются.

                                                                                                                      Чем, глобальными переменными? Спасибо, что сказали, буду иметь в виду.


                                                                                                                      Ибо когда «высокоуровневая обёртка над горячим кодом может [что-то там] решить» — это как раз и нарушает принцип изоляции.

                                                                                                                      Вы либо не так поняли моё высказывание про обёртки (надеюсь, что пояснение выше в таком случае немножко разъяснило), либо я не знаю.


                                                                                                                      Подход «флаги глобальны, читать их может кто угодно, но менять может только пользователь программы» — работает вполне надёжно.

                                                                                                                      Конечно. У вас ведь компилятор гарантирует выполнение этого условия?


                                                                                                                      В С функции получают строку закрытую нулём. gethostbyname — не исключение. Можно было бы создать язык, где они принимали бы рейнджи… но это уже будет не C.

                                                                                                                      А, да, простите, я слишком в 2023, когда вся стандартная библиотека С++ переписана со string_view.


                                                                                                                      Можете посмотреть на код, однако.

                                                                                                                      Могу, но это совершенно не имеет смысла, потому что его писал вышеупомянутый отличник (а скорее — олимпиадник под веще дедлайнами), и для его анализа надо зарываться глубоко внутрь кроличьей норы, что, безусловно, нужно для анализа кода, но совсем не нужно, чтобы понять, что КГ/АМ.


                                                                                                                      Когда я вижу функцию с именем getHash, то как-то естественно ожидать, что она вернёт хеш, а не пойдёт в хешмапу, поищет там значение по данному ключу и вставит новое, если его нет. Кроме того, getHash выглядит как реюзабельная обобщённая функция (ну там ей вроде первым параметром указатель на конкретную мапу передают), а на самом деле она заточена под хосты/айпи.


                                                                                                                      И почему проверка на 128 символов ровно в ней? Да такая жёсткая, прям с ассертом?


                                                                                                                      И почему вообще эта проверка нужна, если, как вы написали выше, больше 64 символов оно быть не может?


                                                                                                                      И нет, не кеширует: последним параметром ip ей передаётся 0, а в этом случае вставки значения не происходит. Ну как там, код лёгкий, очевидный, ясно, что происходит?


                                                                                                                      Для этого Asan есть.

                                                                                                                      Успехов с асаном, когда RES за полсотни гигов, или когда лажа проявляется только под определёнными паттернами нагрузки, которые под асаном не воспроизводятся ввиду замедления.


                                                                                                                      Вот, кстати, kdb_gethostbyname огорчила больше.

                                                                                                                      Меня вообще весь этот код огорчает.

                                                                                                                        0
                                                                                                                        Тут не просто разница в терминологии. Тут разница во времени загрузки программы в память и во времени исполнения инструкций.

                                                                                                                          0
                                                                                                                          Да. Изначально речь вообще шла об индирекшнах вроде foo(%rip) и их влиянии.
                                                                                          0
                                                                                          Потому что о программах без глобального состояния проще

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

                                                                                            –1
                                                                                            Вы слышали что-нибудь о вещах вроде MonadWriter?
                                                                                              0
                                                                                              А какое отношение MonadWriter имеет к системам логирования? Существует ли у MonadWriter файл конфигурации, по сложности сравнимый с конфигами семейства log4xxx и их наследников (logback, NLog)?

                                                                                              (Это был риторический вопрос, можете не рассказывать мне что такое MonadWriter)
                                                                                                0
                                                                                                Это дело десятое. Речь-то о подходе и о возможности избежать глобального состояния даже в этом случае.
                                                                                                  0
                                                                                                  Чем больше конфиг — тем дороже повторное чтение его каждый раз.
                                                                                                    0
                                                                                                    Зачем его читать много раз?

                                                                                                    В конце концов, говоря на С++-ном, голая глобальная переменная и private static-поле — это ж две сильно разные вещи по возможным последствиям.
                                                                                                      0
                                                                                                      А что делать на Си, где нет приватных полей?
                                                                                                        +1
                                                                                                        Задуматься о том, оправдан ли выбор С в данном случае.
                                                                                                        0

                                                                                                        Глобальная static-переменная мало чем отличается, у неё точно так же ограничена область видимости окружающим её кодом (для static-поля окружающий код это класс, для глобальной static-переменой — файл исходника с ней, а есть ещё локальные static-переменные с областью видимости — функией, где она объявлена).

                                                                                                          0
                                                                                                          Только оно там без `static`, я специально смотрел.
                                                                                                  0

                                                                                                  Это относится к функциональным языкам — у них есть своя специфика, которая и позволяет обходить глобальные состояния… за счет глобальных монад. Да что уж, предлагаю пойти дальше — класс, функция — это тоже глобальный объект, в своем роде. Только вот их глобальность скрыта ЯП и не доступна, обычно, программисту. Хотя умельцы патчат классы в рантайме прямо в виртуалке при загрузке этих классов — AspectJ как пример.


                                                                                                  И да, как решение для ликвидации глобального состояния — ФОП отличное решение.
                                                                                                  А в остальных случаях как? Или идеализм и перфекционизм вездесущи и все строем должны идти учить хаскель?

                                                                                                    0
                                                                                                    Это относится к функциональным языкам — у них есть своя специфика, которая и позволяет обходить глобальные состояния… за счет глобальных монад.

                                                                                                    Я не очень понимаю, что такое глобальная монада.

                                                                                                    Или идеализм и перфекционизм вездесущи и все строем должны идти учить хаскель?

                                                                                                    Его стоит выучить хотя бы потому, что он очень сильно расширяет сознание набор доступных техник и паттернов даже в императивных языках.

                                                                                                    Ну и в конце концов, мы же тут об отличниках говорим?
                                                                                              +4
                                                                                              «Зачем? Посмотрите где...»
                                                                                              Ну давайте посмотрим на «с».
                                                                                              c = *s;

                                                                                              Ок, пошли смотреть на «s»
                                                                                              char *ptr, *s;

                                                                                              Хорошо. Ищем инициализацию «s».
                                                                                              ptr = s = cfg_cur

                                                                                              Ура! Всё сразу понятно стало, сэкономленное на оформлении кода время окупилось, разработчику — премию (сарказм!).
                                                                                              Вот так и ходишь и смотришь большую часть времени, вместо того, чтобы просто читать понятный код.
                                                                                              Такое оформление, по которому нужно «Посмотрите где...» — это то же самое, что и «идите на...» по отношению к тому, кто в дальнейшем будет работать с кодом.
                                                                                              Пойдешь вот так, как Вы предлагаете, смотреть, что означает переменная "_suprknadel", а через полчаса обнаруживаешь 15 открытых модулей и еще больше вопросов.
                                                                                              Поубивал бы.
                                                                                              Извините за эмоции, но я только что вынырнул из модуля в 17000 строк, с которым работаю :(
                                                                                              +2
                                                                                              А мне больше понравилось, как функция парсинга конфига заодно резолвит какие-то хостнеймы парой десятков строк ниже.
                                                                                              –1
                                                                                              Только у GNU код на два порядка лучше структурирован.
                                                                                            0
                                                                                            В когнитивной психологии ту часть памяти, в которой непосредственно производятся вычисления «в уме», называют кратковременной и рабочей. Там всё сложно и неоднозначно, но как её ни назови, а объём этой памяти сильно ограничен. Разные исследователи называют «магические числа» семь и пять. Это средние значения. Объём «рабочей» памяти зависит от обстоятельств и коррелирует с интеллектуальными способностями.

                                                                                            — Петька, приборы?
                                                                                            — 42.
                                                                                            — Что «42»?!
                                                                                            — А что «приборы»?

                                                                                            В каких единицах числа приведены? В битах? Байтах? Вариках?
                                                                                              +3
                                                                                              Тут всё интересно. Пять и семь — это количество неких «объектов». Если заставить человека запоминать случайную последовательность букв, то такими объектами будут буквы, и человек запомнит в среднем примерно семь букв. А если слова — то примерно семь слов. А букв в этих словах будет существенно больше.
                                                                                                0
                                                                                                Однажды мне потребовалось запомнить последовательность из 3 иероглифов, чтобы через время их воспроизвести. Две минуты старался, но не осилил. При этом иероглифы были не сложными, до 5 штрихов каждый. Оказалось, проще было сфотографировать на телефон.

                                                                                                Это я к тому, что число объяснять надо. Вы говорите, что рассматривается случайная последовательность букв, но в тексте этого нет. Да и код отнюдь не случаен. Да даже для случайной последовательности следует указать размер алфавита «объектов» (7 букв запомнить — не 7 слов, что в свою очередь не 7 иероглифов).

                                                                                                P.S. я знаю про один эксперимент
                                                                                                На линейной шкале (оттенки серого, размеров и т.п.) выбирается в определённом смысле равноудалённо n точек и затем предлагается случайная последовательность этих точек человеку, чтобы он идентифицировал каждую точку. Шкалу он видит только единожды, перед началом классификации, число n знает. Оказало, наиболее комфортно человек классифицирует при n<9, причём эта цифра почти не зависит от природы линейной шкалы, лишь бы границы ощущались как диаметрально противоположные (белый-черный, точка-метр).
                                                                                                  0
                                                                                                  А Вы знали значение этих иероглифов? Если бы знали значение и знали наизусть их начертание — запомнили бы без труда. А так, задача запомнить произвольный набор из 10+ штрихов — непосильная для человека западной культуры. Это куда сложнее, чем запомнить 10+ произвольных букв.

                                                                                                  А вот семь букв или семь слов (знакомых) для нас — это как раз всё равно. Потому что те и другие мы обрабатываем как целостные объекты, гештальты.
                                                                                                    0
                                                                                                    А Вы знали значение этих иероглифов?
                                                                                                    Разве это существенно? Скажем, иероглифы хираганы я могу запомнить вряд до 15, если при восстановлении будет под рукой таблица, и до 7-10, если не будет. Китайские иероглифы даже в количестве 5 штук я не восстановлю, даже если под рукой будет соответствующая таблица. Значение ни тех, ни иных я не знаю. Просто алфавит меньше.
                                                                                                    А вот семь букв или семь слов (знакомых) для нас — это как раз всё равно. Потому что те и другие мы обрабатываем как целостные объекты, гештальты.
                                                                                                    Опять же. Что проще, запомнить «оывхзку» («о! ы-ы-ы-ы… вот хз… ку» — уже запомнил) и «овации пароходом гештальту пенсне шизофазия велюром седло»? Букв-то всего 33 и многовероятно появление слогов и других знакомых групп. А слов больше и далеко не каждое вызывает устойчивый запоминаемый образ.

                                                                                                    Или же под объектом в тексте понимается именно образ? Но тогда как сделать однозначное сопоставления объекта и формальных языковых конструкций (букв, слов, словосочетаний)? У каждого же по-разному.
                                                                                                      +1
                                                                                                      Опять же. Что проще, запомнить «оывхзку» («о! ы-ы-ы-ы… вот хз… ку» — уже запомнил) и «овации пароходом гештальту пенсне шизофазия велюром седло»?
                                                                                                      Во втором случае очень много редких слов, а часть — явно распадается на два (шизофазия) или вообще не воспринимается как слово (гештальту). Если бы там не было таких ужасов — разницы бы не было.

                                                                                                      А слов больше и далеко не каждое вызывает устойчивый запоминаемый образ.
                                                                                                      Это да, некоторые слова будут занимать не один «обьект» в голове, а два или три.

                                                                                                      Или же под объектом в тексте понимается именно образ? Но тогда как сделать однозначное сопоставления объекта и формальных языковых конструкций (букв, слов, словосочетаний)? У каждого же по-разному.
                                                                                                      Именно поэтому мы не можем сказать точно сколько обьектов нужно держать в голове, чтобы осознать как работает функция. Можем только оценить.

                                                                                                      Но можно заметить, что 5-7 обьектов — это столько, сколько мы можем «захватить» не напрягаясь. Если «загружать» в сознание обьекты постепенно и если это — не случайные, никак не связанные друг с другом обьекты, то можно уместить гораздо больше.

                                                                                                      При написании программ очень мало функций укалыдваются в это ограничение — потому и требуется над кодом посидеть и подумать, чтобы понять. Даже если код хорошо написан.
                                                                                                        +1
                                                                                                        иероглифы хираганы
                                                                                                        В хирагане нет иероглифов. Это слоговая азбука. Т. е. в японском используют сразу хирагану, катаканы и иероглифы одновременно (например, начало слова может писаться иероглифами, а конец хираганой).

                                                                                                        Знаки хираганы называются знаками хираганы (или знаками каны, если более обобщённо).
                                                                                                          +1

                                                                                                          При этом эти знаки каны очевидно являются упрощениями все тех же китайских иероглифов

                                                                                                            0
                                                                                                            Это слоговая азбука.

                                                                                                            Это не характеристика типа знаков системы. Слоговую азбуку можно кодировать иероглифами, буквами, рисунками, канцелярскими скрепками — чем угодно.
                                                                                                            0
                                                                                                            Разве это существенно?

                                                                                                            Да. Значение и происхождение иероглифов помогает их запоминать куда быстрее. Это не моё мнение, это практика моих знакомых японистов из МГУ.
                                                                                                          +1

                                                                                                          Проще запомнить много знакомого, чем мало незнакомого. Например, те же иерогрифы. Нам они кажутся темным лесом, и даже один нам трудно запомнить, зато читая многие слова, напримр: элктчтво, мы его прочтем, даже если нет некоторых букв, просто по памяти. А память наша держит сотни и тысячи русских слов.

                                                                                                      +1

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

                                                                                                        0
                                                                                                        Как я написал выше, программирование как разработка программных систем это не только science, но ещё и art. Причём art как раз того порядка, про который мы пока только догадки строим: предсказания на основе непонятных внутренних эвристик. По сути, это как NP-задача, мы можем построить полиномиальные метрики к ней, но придумать полиномиальное решение, подходящее для всех, не можем, и даже не знаем, есть ли оно.
                                                                                                          0
                                                                                                          даже не знаем, есть ли оно.

                                                                                                          Зато работа есть ;)
                                                                                                          0
                                                                                                          Computing science должна быть доказательной и повторимой, как любая наука.

                                                                                                          Вы, видимо, не знакомы с принятой ныне структурой научного знания.
                                                                                                            0
                                                                                                            Ну это одно из определений, в общем-то.
                                                                                                              –2
                                                                                                              Определение того, что именуется ядром науки, а не наукой в целом, если следовать именно принятому в философии делению. Бернал, Прайс, Ильин — почитайте))
                                                                                                          +3
                                                                                                          Должна быть какая-то оптимальная компактность программного кода. Слишком сжатое изложение мысли трудно для восприятия из-за перегруженности смыслом, а слишком пространное — из-за большого объема. Как в задачке, с которой начиналась статья: подробное решение сложнее осмыслить и проверить, чем краткое. Но этот оптимум зависит от квалификации.
                                                                                                            +1
                                                                                                            С гипотезой не согласен. С началом изучения действительно, бывало, писал длинные и запутанные портянки. На данный момент главная тенденция — удобочитаемость кода. Функции/процедуры/ и т.п стараюсь вместить на один экран, если допустимо. По длине строк — так же. Даже если объект более 1 раза не используется — ничего страшного. Главное — чтобы эти блоки были логически и мнемонически ясны тебе через месяц/год/и т.п. А если твой код смогут понять без труда другие программисты — то это она самая и есть — гениальность :))). И никакая оперативная мозговая память тут ни при чем.
                                                                                                            А отличники пишут запутанный код 1) из-за спешки 2) Из-за неуважения к себе и другим
                                                                                                              +2
                                                                                                              Это потому, что Вы наступили на достаточное количество граблей и перешли на следующий уровень. Опытный программист отличается от неопытного тем, что пишет понятный код.

                                                                                                              Некоторые из отличников пишут длинный запутанный код потому, что могут это делать, интеллект позволяет. А опыт ещё не подсказывает, что так делать нельзя.
                                                                                                              +5
                                                                                                              Полагаю, что исходная посылка статьи растёт из определённого, достаточно распространённого метода преподавания. И не является, в общем случае, универсальной истиной.
                                                                                                              Нам, отличникам, всё это ни к чему. Зачем писать столько ненужных промежуточных действий, когда можно сразу готовый ответ?

                                                                                                              Звучит примерно как: «Тестировать свой код — это для троечников. Зачем писать столько ненужного лишнего когда, если программа и так будет работать как-нибудь?» Ну… Чем не точка зрения, конечно.
                                                                                                              Я тоже училась на все пятёрки и помню, какой это был понт, когда ты промежуточных вычислений не записываешь. Зачастую это приводило к ошибкам, которые трудно было отследить и исправить (что осложняло работу учителя в разы, как я поняла в дальнейшем, когда сама стала преподавать), но мы велись, и это всё счастье до сих пор живо в наших школах. Но мало ли на что школьники ещё ведутся: что курить — это круто, что если все пойдут с крыши прыгать…
                                                                                                              Называть ли это всё «горем от ума»? Я бы, скорее, говорила о попытке самоутвердиться в своей социальной группе. У разных групп — свои критерии «крутизны», одними и теми же методами стать крутым вообще везде сложно (и это очевидно). А оценки — это просто оценки, они учителе-зависимые. Когда ты ещё ребёнок и не понимаешь, что оценивание твоей контрольной работы на 5 или 4 не имеет ничего общего с оценкой тебя, как человека, ты начинаешь за этой оценкой гоняться. А учителя — разные. Кто-то за многостраничную работу сразу ставит 5, не читая (ибо лень), а кто-то гарантированно снижает на балл-другой (потому что читать было лень, но всё равно пришлось).
                                                                                                              Если ребёнку на уроках информатики начать снижать оценки за нечитабельный код, то через пару месяцев у «завзятого отличника» код станет самым читабельным в классе. Не думаю, что он от этого как-то резко поглупеет.
                                                                                                              Школа она в любом случае и хорошо, что была, и хорошо, что была окончена. Если её выкинуть из головы, хуже не станет.
                                                                                                                0
                                                                                                                Вот бы и правда кто-то стал снижать оценки за нечитабельный код… Мечта…
                                                                                                                0
                                                                                                                хотелось бы познакомиться алгоритмом вычисления метрики когнитивный вес, очень очень, или вес это количество объектов?
                                                                                                                  0
                                                                                                                  Нет пока ещё такого алгоритма. Есть предположение, что, возможно, такая метрика может оказаться полезной. Что в неё следует включать — не до конца ясно. Объекты, которыми оперирует интеллект программиста при написании участка кода. Ясно, что какими объектами он оперирует, те и пишет в код. Но какие именно объекты? Переменные, вызовы методов, операции, константы, элементы структур — что? Какие именно из этих объектов занимают место в рабочей памяти программиста, а какие — нет? Проблема очерчена — есть, над чем поработать.

                                                                                                                  Почитайте «Программный код и его метрики», и особенно метрики Холстеда. Там много пищи для размышления.
                                                                                                                    0
                                                                                                                    имхо берёшь AST кода, задаёшь каждому узлу ( не только листу, но и ветке включая внутренние узлы) тэг классификации по смыслу, то есть «в этом цикле считается количество домов в городе» а не «в этом цикле переменная и увеличивается от 0 до 1000», определяешь метрики= расстояние между смыслами.
                                                                                                                    А всякие количественные метрики это ерунда — ту же самую программу можно переписать в другом виде чтоб весь тот расчёт был другой, то есть он не репрезентативен.
                                                                                                                      0
                                                                                                                      Нет пока ещё такого алгоритма

                                                                                                                      Думаю, и не будет. По крайней мере — одного для всех.


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


                                                                                                                      Метрики будут действовать в границах, которые вы обязаны будете выбрать и наложить — выбором языка, парадигмы программирования, платформы, страны проживания, и т.п.


                                                                                                                      Подавляющее большинство практических задач натуральным образом являются граничными (краевыми) задачами, и чем длиннее граница очерченной области, тем больше гитик.

                                                                                                                      0

                                                                                                                      Есть Цикломатическая сложность некоторые linter'ы ее умеют считать и выдавать предупреждение, например rust-clippy

                                                                                                                      –1
                                                                                                                      Хорошая тема, очень правильная и актуальная.

                                                                                                                      До сих пор помню один из самых обидных фейлов в жизни.
                                                                                                                      На одной из пар (насколько помню, по диффурам) была задача, что-то там про пулю, которая врезается в крутящийся барабан. Решил её раньше всех – сразу было ясно, что всё решается методами простейшей физики и геометрии, тупо школьный курс же.
                                                                                                                      Но получил «трояк». Ибо препод решил, что «не по уставу». Даже несмотря на то, что ответ полностью сошёлся с ответами горе-отличников, корпевших над «уставным» решением чуть ли не вдвое дольше…

                                                                                                                      Мораль сей басни такова – хорош не тот, кто просто зубрит, а тот, кто понимает предмет в целом и суть конкретного вопроса в частности. И на оценки тут уже плевать – они важны только лишь в узких рамках ВУЗа, а дальше жизнь сама расставит всё и всех по своим местам… ;)

                                                                                                                      P.S. Хорошо, что хоть по информатике нам достался хоть и «советски»-отсталый, но крайне здравый преподаватель – «неуставные» решения не рубил и не критиковал, а сам приходил от них в восторг. До сих пор вспоминаю его с искренней теплотой… ^_^
                                                                                                                        +1

                                                                                                                        Хорош тот, кто в состоянии в задании преподавателя увидеть задачу для себя. В данном случае она должна была быть "изучение конкретных подходов к решению дифференциальных уравнений", а не в "раньше всех сказать ответ". Ты тройка, видимо, была именно по дефурам.

                                                                                                                          0
                                                                                                                          увидеть задачу для себя

                                                                                                                          Вот именно об этом я и говорил.
                                                                                                                          Образ мышления отличника – решить всё «правильно, как учили». Образ мышления программиста – решить всё максимально рационально, пусть даже это будет «не канонично». Иначе говоря, «труъ-программистское решение» – найти путь к тому же (!) результату, но меньшими усилиями и меньшей тратой ресурсов. Оптимизация же, и всё такое (о чём, кстати, вообще и статья)…

                                                                                                                          Конечно, идеально, когда оба варианта совпадают. Но если вдруг не совпадают – то, всё же, второй вариант видится как-то намного более предпочтительным.
                                                                                                                            +1

                                                                                                                            Ты решение из другой сферы взял, и при этом не достиг цели.
                                                                                                                            Вот если бы ты в контексте дифуров придумал свое решение, пусть даже менее элегантное — это было бы круто.


                                                                                                                            Ваш вариант — это в соревновании бега в мешках пробить в нем дно и бежать.
                                                                                                                            Решение не подпадает под заданную цель. Считайте это как ТЗ.


                                                                                                                            В целом же, в абстрактном случае, получи вы такую же задачу с просьбой решить — ваше решение было бы как раз самым правильным, потому что дифуры — это микроскопом гвозди забивать, когда есть более простое и доступное решение.


                                                                                                                            Где же в вашем первоначальном комментарии "правильно, как учили"?