Pull to refresh
23
0
Виктор Лова @nsinreal

Пользователь

Send message

Верно только на половину.

Возьмем такой код:

using System; class A { static DateTime Do() { DateTime a; a = new DateTime(1); a = new DateTime(1).AddYears(1); return a; } }

Прогоним в IL через sharplab:

IL_0000: nop IL_0001: ldloca.s 0 IL_0003: ldc.i4.1 IL_0004: conv.i8 IL_0005: call instance void [System.Private.CoreLib]System.DateTime::.ctor(int64) IL_000a: ldc.i4.1 IL_000b: conv.i8 IL_000c: newobj instance void [System.Private.CoreLib]System.DateTime::.ctor(int64) IL_0011: stloc.1 IL_0012: ldloca.s 1 IL_0014: ldc.i4.1 IL_0015: call instance valuetype [System.Private.CoreLib]System.DateTime [System.Private.CoreLib]System.DateTime::AddYears(int32) IL_001a: stloc.0 IL_001b: ldloc.0 IL_001c: stloc.2 IL_001d: br.s IL_001f

Вуаля!

В первом случае у нас просто вызывается конструктор (call .DateTime::.ctor).

Во втором случае у нас происходит аллоцирование (newobj DateTime::.ctor).

Value types are not usually created using newobj. They are usually allocated either as arguments or local variables, using newarr (for zero-based, one-dimensional arrays), or as fields of objects. Once allocated, they are initialized using Initobj. However, the newobj instruction can be used to create a new instance of a value type on the stack, that can then be passed as an argument, stored in a local, and so on.

И в коде у @novar как раз такой случай, потому что у него паттерн t1 = new DateTime().Add{Smth}(1)

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

Я сначала кое-чего объясню.

--

Во-первых, SMTP хоть и про строки, но обратите внимание на декларацию метода:

int ProcessRead(byte[] buffer, int offset, int read, bool readLine)

Здесь что важно: buffer - byte[], не string. Т.е. строк таки нету.

Во-вторых, обратите внимание на вызов этого метода https://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpReplyReaderFactory.cs,279

int read = bufferedStream.Read(buffer, offset, count); int actual = ProcessRead(buffer, offset, read, false);

Из стрима мы вычитываем count байтов. Не непрерывной поток байтов, а конечный поток байтов. Если быть точнее — 256 байтов. Даже если отправляющая сторона отправила все разом, то вычитка происходит все равно по 256 байтов.

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

--

"их нет поверх чего гонять — строк нету". SMTP это как раз только строки и больше ничего там нет.

"В рамках SMTP происходит вычитка стрима маленькими блоками". Не согласен

Давайте вы сначала почитаете как работает код, а потом будет утверждать что-то.

  1. Если вас волнует GC именно из-за количества миллисекунд, то непонятно причем тут парсинг строки.

  2. Я практически уверен, что ребята имели в виду алгоритмическую сложность, а не общую скорость работы. Типа кейс "раз в 1мс" должен работать не сильно медленнее кейса "раз в 1 год". Потому что есть разные вариации решения этой задачи.

  3. Подозреваю следующее. Вы думаете, что раз нужно расписание "раз в 1мс", то это значит, что ваш код должен отрабатывать <1ms, чтобы успевать переходить от одного момента расписания к другому и выполнять задачу возложенную на этот момент. Если я прав, то вы наверняка напрасно тревожитесь.

Значит я зря по этому поводу вас оговорил. Простите.

  1. Прошу прощения за плохие формулировки. Под internals я подразумевал не "поля помеченные internal", а "кровь, кишки и внутренние органы класса". Тестировать такие вещи противопоказано, потому что при изменении реализации (без изменения внешнего API) ваш тест станет очень мешать. Скорее всего при переработке этот тест придется удалить. И те кейсы, которые проверялись чисто за счет этого теста — они проверены не будут.

  2. Даже если вы настаиваете на том, что это нужно тестировать, то вы действительно можете сделать отдельную структуру. Если вам кажется, что это не так, то вероятно вы не понимаете сути тестовых заданий. На них ожидается production-like-код, а не подвыподверты.

Ну, как именно это должно писаться на работе в код-ревью — это вопрос интересный.

Лично для себя я считаю, что если вы работаете в коллективе, то полезно исходить из следующих убеждений:

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

  • если коллеги уж чересчур бесят, то надо или заменять коллег одного за одним, или менять работу

Но меня тоже забавляет, что "не думать о качестве кода" — это норм, а "не думать о вежливости" — это не норм.

Я, кстати, думаю, что автор не получил фидбек как раз потому что детальный фидбек без мата было дать очень трудно.

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

Да, я писал, что у них своя специфика.

Да, sortedset может быть действительно тяжелее. Вам никто не мешает использовать BitArray или если его недостаточно — написать BitSet поверх BitArray.

Что быстрее, что тяжелее, что дороже — это надо бенчать.

Впрочем, конкретно здесь неважно, если у вас перфоманс просядет из-за SortedSet. Условие "Обращаю Ваше внимание, что класс должен быть эффективным и не использовать много памяти и ресурсов даже тогда, когда в расписании задано много значений. Например очень много значений с шагом в одну миллисекунду" — это условие о асимптотическом анализе (O(1) лучше O(N), O(logN) лучше O(N^2) и т.д.), а не о константом ускорении (1 байт лучше 10 байтов).

Используют методы в десять экранов. Не факт что это лучше, чем отступы в 10 табов

Зуб даю — лучше. Конечно, еще бы лучше, если бы методы были покороче, но щито поделать.

Кстати, в комментариях к этой статье я видел ссылку https://github.com/atifaziz/NCrontab/blob/9b68c8d1484ccd56a8f0bc1ce12e7270736f3493/NCrontab/CrontabSchedule.cs#L213 - это еще красивее.

Их код оптимизировался годами. От решения тестовой задачи требуется такое же качество?

По читабельности требуется качество даже лучше. По работоспособности, скорости, эффективности — не обязательно.

"в C# есть структуры для этого — BitArray, например". Такие объёмы массивов как в моём решении (около 1300 байт), это как раз граничная область где переход на биты не даёт значительного уменьшения потребляемой памяти. Такая оптимизация планировалась как потенциальная, и именно поэтому так устроены массивы.

Я про BitArray написал не для оптимизации. Вообще по барабану как он там устроен, абы работал.

Я про BitArray написал, потому что этот тип имеет правильную семантику для задачи.

"Но почему же byte, а не boolean?". Потому что надо экономить память (просьба заказчика), а boolean может занимать и 4 байта в зависимости от платформы. Всё таки разница 5000 байтов или 1300 - уже заметная.

По вашей же ссылке первый ответ звучит так:

Firstly, this is only the size for interop. It doesn't represent the size in managed code of the array. That's 1 byte per bool - at least on my machine. You can test it for yourself with this code:

Давайте я переведу на человеческий:

  • bool занимает 1 байт

  • но если вы укажете, что он должен занимать 4 байта, то он будет занимать 4 байта

  1. Во-первых, это не так

    1. Найдите класс Regex. См. https://referencesource.microsoft.com/#System/regex/system/text/regularexpressions/Regex.cs,bbe3b2eb80ae5526

    2. Кликните на его имя

    3. Узрите 109 instantiations + 243 references

  2. Во-вторых, это недостаточное обоснование.

    1. Реальная причина, почему "не используются" регулярки — это потому что они подходят для малого количества задач.

    2. В указанном вами примере (SMTP), применение регулярок не прокатит не потому что регулярки медленные, а потому что их нет поверх чего гонять — строк нету.

      1. В рамках SMTP происходит вычитка стрима маленькими блоками. Грубо говоря, сначала приходят 10 байт (условное число), потом еще 10 байт, потом еще 10 байт.

      2. Чтобы вкинуть регулярку ребятам понадобится на каждых 10 байтах делать следующее:

        1. Превращать эти байты в строку и соединять с предыдущими строками

        2. Запускать регулярку на детект данных

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

    3. Код внутри .NET должен быть определенно быстрее, чем код на самом .NET. Иначе получится очень странно.

  3. В-третьих, разбор SMTP-ответа чище и понятнее, несмотря на интересные конструкции в виде goto.

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

КГ/АМ

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

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

Теперь о том, почему код — говно.

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

    1. Отказ от регулярок полностью необоснован и высосан из пальца.

    2. Не нравится регулярка — у вас есть возможность заюзать чей-то чужой парсер (ANTLR, например), который произведет ясный код.

    3. Не нравится чей-то чужой парсер — напишите свой, но нормально. Нормально — это несколькоуровневый парсер Token -> AST. У вас же получился scanerless parser, а это криповая поебень. Но даже scanerless parser может быть написан красивее.

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

    1. Например, вы можете заместить, что паттерн типа var dotPosition2 = (dotPosition1 < 0) ? -1 : scheduleString.IndexOf ('.', dotPosition1 + 1) повторяется четыре раза, но не читабельноть этого дела не была оптимизирована

    2. Вот читаю я строку internal readonly byte[] _years = new byte[101]; // год (2000-2100).

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

        1. Но почему же byte, а не boolean?

        2. Почему это не сделать как отдельную явную структуру? Кроме того, в C# есть структуры для этого — BitArray, например. Но можно сделать свой IndexedBitArray. Так, чтобы он создавался через new IndexedBitArray(start: 2000, end: 2100). Т.е. чтобы была возможность указывать, откуда и до куда, раз уж вам не выгодно хранить range 0-2100.

        3. Нейминг "_years", srsly? Это должно быть что-то типа "_scheduledYears" или типа того в такой реализации.

      2. И таких строк много, и они связанны (years, months, days, etc). Почему бы не ввести новую структуру Schedule, в которую запихнуть все это говно?

    3. У вас есть строка кода с 12-ю табами. Для C# норма 3-4. В сложных случаях 5-6 допустимо со скрипом и смазкой. А у вас 12 — это просто полный пиздец. Для сырого джуна еще допустимо. Но для позиции strong jun+ я не хочу никак взаимодействовать с таким программистомом и его кодом.

      1. Вы также можете посмотреть на реализацию подобного метода у популярных ребят. Например, насколько я понимаю, Quartz.CronExpression.GetTimeAfter https://github.com/quartznet/quartznet/blob/f376d69537724d784d4aced87346dbfbfcd3e017/src/Quartz/CronExpression.cs#L1625https://github.com/quartznet/quartznet/blob/f376d69537724d784d4aced87346dbfbfcd3e017/src/Quartz/CronExpression.cs#L1625. Там правда, есть свои особенности. Код не очень, но уже ощутимо лучше по сравнению с вашим вариантом.

      2. Вообще, когда вы пишите сложный нетривиальный алгоритм ориентированный на быстродействие — у вас никакими трюками не получится сделать его понятным, кроме как огромного количества комментариев. По-сути, вам нужно написать техдоку, что-то типа такого: https://www.wikiwand.com/en/Timsort.

    4. Тест ScheduleTests.Construction — плох, поскольку лезет в internals и проверяет детали реализации. А еще и выглядит бредово

    5. Benchmark ничего не сравнивает и написан просто шоб было.

    6. И конечно, другие люди в комментариях уже тоже хорошо потыкали.

Дальше, хочу отдельно прокомментировать статью автора

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

непонятно, поскольку неизвестны условия эксплуатации

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

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

Карго-культ. Регулярки неэффективны в ряде случаев, но не всегда.

и частого выделения объектов в «куче» (heap) чтобы не нагружать сборщик мусора

Хуита. В большинстве случаев вы не будете упираться в сборщик мусора. Особенно в C#.

Алсо, по объявленному опросу тоже интересно получается. Версии "я — хуевый программист" там нету. Как мне голосовать-то? Но это еще один микропоказатель, почему бы с вами не хотелось работать.

Да по барабану, нравится ли вам DDD в понимании Эванса или нет. Вы или называете вещи своими именами или просто примазываетесь к термину. И то, что разработчики из iSpring возможно неправильно применяют этот термин — вам оправдания не делает.

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

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

Вопрос не в том, правильный подход или нет. Вопрос в терминах. Вы сможете без гугла ответить, в чем идеология DDD? Вы понимаете, что если {Something} Driven Development использует арифметические операторы, то это не делает арифметические операторы единственным определяющим аттрибутом {Something} Driven Development?
Ору. Вы вообще понимаете, что DDD — термин, введенный Эвансом?
Любое другое понимание этого термина — это лишь попытка примазаться к известному термину.

Зачем вы используете термины, которых не понимаете?
Не выпал. Что-то новое, что помогает совладать со сложностью — это тоже что-то новое.
В конечном счёте, все тьюринг — полные языки суть одно и то же

Разрешите докопаться. Вы вот когда эту фразу написали, вы её осознанно/обдуманно написали или просто повторяете чей-то аргумент?

Вы на brainfuck или malbolge сможете написать обычное todo-приложение или вы плохой разработчик, который не способен совладать с тьюринг-полным языком?

А саппортить его и добавлять новые фишки?
Т.е. на горизонте в 20 лет обратная совместимость есть, а на горизонте в 5 лет — её нет?
Концепция портов и адаптеров появились давно. И у неё есть свои ограничения.

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

И это не специфично под JS. Разница только в том, что за вас сделали возможность запускать этот код на разных платформах, в том числе и новых.

Единственное, что специфично для JS — это возможность использовать фишки из новой версии рядом (прямо в одной функции) со старой версией кода, постепенная миграция руками. Хотя зачем это, если практически любое изменение в языке можно поправить автоматической миграцией кода?
ПСКОВ исполняется строго в веб-браузере без взаимодействия с сервером.
<sarcasm>Очень крутой пример, который убедительно поддерживает вашу точку зрения</sarcasm>

Для освобождения ПСКОВА из клетки пришлось создать ЛФСД, крошечный скрипт на Python, который необходимо запускать руками на время работы ПСКОВА. ЛФСД даёт возможность ПСКОВУ работать с локальной файловой системой пользователя. Python выбран ввиду его наличия по умолчанию на Linux и macOS, а также возможности поставить на Windows (даже Windows 2000).
<sarcasm>А это вообще топчик. Конечно, если переложить какую-то часть работы на другой язык, то можно убедительно аргументировать, что на JS можно писать долговечные нетривиальные приложения</sarcasm>

А я сейчас пойду проверю как дела у DOSBox.
Поведение copyFile чётко описано в документации и гарантировано предсказуемо (если знать как оно работает)? Если да, то основная притензия пока только к назанию :)
На самом деле, не к названию, конечно, а к смешиванию функционала
Довольно спокойная реакция. Ну типа, ну странная особенность, ну с кем не бывает, ну можно было лучше. Это объясняет, почему вы спокойно относитесь к особенностям js.

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

Мимо :)
Без RTFM вы не узнаете про то, как const работает в объектах (свойства можно менять) и что конструкция { … code … } это блок со своей областью видимости и эти особенности могут сбить вас с толку. Соответственно я подозреваю, что тут опять вы получите «контринтуитивное» поведение (особенно const в обектах вам «понравится»).
Я не думаю, что у вас могут возникнуть какие-то сложности с пониманием, если вы таки доберётесь до MDN но без чтения документации результат «интуитивного» применения вас может удивить.
Впрочем, я не верю, что вы вообще принципиально не читаете доки :)
Поясню, почему я считаю, что это снобская реакция. Приходит человек, говорит, что у JS есть недостаток. Вы защищаете эту особенность языка и предполагаете, что этот человек просто не понимает базовых вещей. При этом тратите довольно много текста на это. Мол, не, язык нормальный, это ты дурак, а я тебе покажу, где у тебя еще могут быть проблемы. Выглядит так, хотя вполне вероятно, что вы не пытались это делать осознанно.

Я знаю человека, который начинал програмировать на JS в средней школе и когда год назад добрался до C# (в колледже) тоже удивлялся разным «моментам» :)
Это не ваш случай, просто илюстрация на тему «где больше дурацкого поведения». Многое зависит от «привычного» конкретному человеку языка. ИМХО :)
Слишком мало деталей. Ну, он удивился или возмутился? Что удивило, что возмутило? Был ли опыт с другими языками (предполагаю что нет, но все же)? Попрактиковавшись и пописав промышленный код, он утвердился в своем мнение или изменил его?

И вы бы хотели, что-бы при использовании команды «langVersion:es7» (по аналогии с «use strict») скрипт падал с ошибкой, при обнаружении var. Или нет?
Да. Хотя детали реализации не важны. Просто мусор не должен быть в новой версии языка.

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

Тем более, что тут скорее вопросы нужно к разработчикам TS адресовать, а не к JS. Как по мне, лучше пусть компилятор TS падает сразу, чем JS потом.
TS — это JS с типами и ничего больше. Это довольно здравая позиция, она обеспечивает прозрачное взаимодействие с браузером, без сюрпризов. Претензии можно только к JS выставлять или невозможности завести другой язык в браузере напрямую.
Это грустно, что есть языки, у которых ничего не работает через год-два. Но есть языки, у которых работает, хотя и поддерживать это уже нельзя.

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

Но как вы можете говорить про «достигать 20 лет»? Если:
— node.js выпущен только в 2009, т.е. даже при желании код на ноде не может быть взрослее 11-летки
— какой либо нетривиальный JS-код 20 лет назад было нереально трудно написать?
— в браузере постоянно происходят штуки, ломающие существующее поведение (CORS, force HTTPS, изменения в Cookie Security)
— некоторое время назад большинство достаточно полезного кода могло быть написано только с применением возможностей, которые были реализованы, но потом были удалены
— большинство полезных библиотек часто депрекейтят АПИ. Да, конечно, можно жить на старой версии, но разрабатывать дальше не получится хорошо.

Information

Rating
Does not participate
Location
Харьков, Харьковская обл., Украина
Registered
Activity