Вот эта строчка кода замедляет работу Табуна в 10 раз

+109
в блоге IT Pony!
{date_format date=$oCommentDate hours_back="12" minutes_back="60" now="60" day="day H:i" format="j F Y, H:i"}

160 комментариев

А если быть точнее, то функция smarty_function_date_format. И не только Табун, но и любой сайт, написанный на движке ЛайвСтрит. (в постах, где много комментариев)
Sasha-Flyer Изменён автором
+3
Правильно, функции надо самому писать, а не юзать готовые, мало ли что там разрабы навертели)
Randy1974
+1
Правильно, функции надо самому писать

Ещё чуть чуть «самописных функций» и браузер сколлапсирует

а если серьёзно то не дело, когда ключевые для юзеров вещи типа ответов реализуются «на коленке»
oxide
+2
А я ведь предупреждал! Говорил им изгонять Жабу, пусть вся логика будет на стороне сервера. Но меня не слушали. Муа-ха-ха.
MTH_Root
+3
Вообще-то пусть лучше браузер у одного человека тормозит, чем сервер табуна у всех сразу, кмк.
makise_homura Изменён автором
+3
Тормозить — работа сервера. Пользователь заметит малейшие тормоза интерфейса на загруженной странице, но не заметит увеличение загрузки страницы на ~100ms (а если задержка выше, то пора менять сервер).
MTH_Root
+2
Ну, этот конкретный случай — это как раз тормоза при загрузке страницы.
Ну и заметь, что это 100 мс — это тормоза для всех условно 100 пользователей, которые в данный момент получают данные с сервера, и при отсутствии оффлоада и балансировки последний пользователь будет ждать данных 100*100 мс = аж 10 секунд. А по 100 мс у каждого из клиентов вообще незаметно.
makise_homura
+1
последний пользователь будет ждать данных 100*100 мс = аж 10 секунд

Я не это имел ввиду.
Один раз рендерим все что нужно (допустим эту страницу с комментариями и без них), кешируем, пользователям выдаем одну версию из кеша в зависимости от настроек.
Сервер рендерит, условно, за 100ms, потом только затраты на передачу по сети.

В противном случае каждому будет выдана версия с комментариями, и каждый пользователь будет затрачивать свои вычислительные ресурсы на их удаление. Или наоборот, комментарии автоподгрузкой через JS будут бомбить сервер. Что весьма расточительно, если посчитать затраты мощностей сервера и клиентов.
MTH_Root
0
Только вот проблема в том, что пользователи запрашивают разные страницы. В итоге кэширование оказывается бесполезным.
И это хорошо, если рендер 100 мс; потому что, например, курисаба тред с 500+ комментами (где 500 — это бамплимит) рендерила до того, как мы её оптимизировали, секунд 5 (там проблема была в том, что в каждом комменте нужно было отображать номера ответов на него, а это не O(1) запросов к базе, а O(n). К счастью, мы переделали до O(1), сделав отдельную кросс-таблицу ответов. А теперь представь хотя бы десяток пользователей, тыкающихся в такие треды (норма для крупных борд с чат-тредами, перекатывающимися по бамплимиту)…
Короче, тогда к нам и пришло осознание, что красивее выглядел бы лёгкий рендерер на стороне клиента, которому просто прилетает пак данных прямиком из базы, а он на них уже строит страницу. Да, после выполнения запроса это, допустим, не 200 мс, а 1000, зато ответ на запрос прилетает мгновенно — он быстро выполняется (тупо парочка SELECT-ов на сервере) и быстро передаётся (ибо это минимизированный JSON, а не непомерно раздутый HTML). В итоге получается даже быстрее — если, конечно, на той стороне анон не скроллит тред с кнопочного телефона). Не говоря уж о куда более быстром обновлении треда (по AJAX прилетят результаты SELECT с timestamp > последнего обновления треда/последнего полученного коммента, а не весь тред целиком заново) и прочих действиях типа ответа на коммент.
makise_homura
+1
Ну вот толстые клиенты с js по сути как раз и есть реализация распределенных вычислений здорового человека. И это еще без учета того, что в умелых руках всё может начать работать быстрее (например, тебе не нужно передавать каждый раз полноразмерный HTML с одними и теми же кусками, а только изменившиеся кусочки).
StaSyaN
+1
а только изменившиеся кусочки

Угу, это бы сильно ускорило работу Табуна… Но ради этого придется вообще весь движок переделывать(
Sasha-Flyer
0
Я считаю, что сделать свой движок — не так уж плохо. Если бы я делал свой сайт, например свой личный чатик, то теперь, после того как я знаю, что примерно за жесц происходит внутри Табуна, я уже предпочёл бы сделать своё. Тем более что теперь мой опыт побольше, и в принципе мог бы. И торопиться некуда — можно делать спокойно. Но теперь уже нет мотивации (и уже не уверен, что это нужно). Таким образом, вопрос сводится к: стоит ли заморачиваться с этим ради Табуна. Думаю, именно этим вопросом и руководствовались все ведущие разработчики Табуна в своё время, только никто публично не озвучил его и свой ответ на него (или озвучили, но я пропустил).
Farxial Изменён автором
0
Там проблема в том, что часто это только в проекте «новый движок» выглядит лучше, быстрее и проще; потом, когда он обрастает функционалом, он точно так же начинает превращаться в неповоротливого бегемота. Это беда любого крупного проекта(
makise_homura
+1
Конечно, новый движок делается под конкретную модель, но она уже известна — организация данных как у Табуна, может, с небольшими изменениями, и ещё с изменениями, часть из которых тоже уже известна. Нужно просто перед созданием уточнить, и можно поднять некоторые хотелочки (голосую за веб-сокеты и голосовые чаты).
А дальше зависит уже от того, какими будут новые хотелки и насколько движок будет гибким, чтобы принять их.
Farxial Изменён автором
0
Проблема в том, что ты при этом должен реализовать ВСЕ старые фичи (которыми кто-то пользуется). А это намного сложнее, чем сделать совсем новый проект с новым с нуля заданным функционалом.
makise_homura
0
Как переписыватель сториза заявляю, что это несложно)
andreymal
0
И сториз совсем не изменился по сравнению со старым вариантом?)
makise_homura
0
Вот, и я о том же)
makise_homura
0
Всенепременно
Хотя… Облачный гейминг (и не только) двигают же. А это и есть «вся логика на стороне сервера», причём буквально.
oxide Изменён автором
0
Google прекратит поддержку Basic HTML в Gmail в 2024 году

Корпорация зла как обычно… Впрочем, GMail ненужон.
Облачный гейминг

Осуждаю по многим причинам. Даже не почитерить, фу.
MTH_Root
+2
Забавно. я перешел на этот комментарий через gmail почту.
Sasha-Flyer
+2
Главное, чтобы они POP3 не забанили (в таком случае я почту перестану читать вообще, и так блин читаю её раз в месяц, а то и реже).
makise_homura
0
Эм, IMAP?
andreymal
0
IMAP мне не нравится своей сервероцентричностью (например то, что сервер может удалить какое-то письмо у меня из локального ящика при синхронизации, няз).
makise_homura
+1
Вот кстати тут не соглашусь: В 99% случаев стоковая функция быстрее и безглючнее костыля/велосипеда. И этот случай, кажется — лишь тот самый 1% исключений, подтверждающих правило))
makise_homura
0
Это не стоковая функция, а функция лайвстрита, написанная 15 лет назад.
Sasha-Flyer
+2
Ага. В таком случае, думаю, сейчас есть более быстрые и простые стоковые альтернативы)
makise_homura
0
ток проблема в том, что этот date_format в коде Табуна вызывается примерно в 50 разных местах…
Sasha-Flyer
+2
Ну, быстрая реализация и так это ускорит, а если её ещё выпилить оттуда, где она не нужна...)
makise_homura
0
В 99% случаев стоковая функция быстрее и безглючнее костыля/велосипеда.

Безглючнее — да, быстрее — далеко не всегда. SysUtils.Format для формирования двух двузначных чисел в строке будет явно медленнее, чем выделение буфера на 5 байт и запихивание в него X mod 10, X div 10.
Там, где очень критична скорость, но не так важна универсальность — пишут все сами, включая операции сложения :-)
NTFS Изменён автором
+1
А потом где-то в другом месте программы у тебя X, к примеру, становится отрицательным, и в итоге в строке у тебя появляется какой-то мусор, а то и вообще непечатные символы (да, некоторые любят преобразовывать цифры в символы выражением типа 0x30 + n без каких-либо проверок).
Или кто-то решит, что строка теперь у нас не UTF-8, а UCS-2, и ты её теперь будешь ломать такими преобразованиями.
Экономить десяток микросекунд ради такого — явно лишнее.
Ну, если у тебя числа сгенерены вот прям тут же, ты заранее сделал все нужные проверки, и это у тебя узкое место программы — то так можно делать, но опять же, если ты так делаешь, то для этого должно быть куда более веское обоснование, чем «я хочу эту функцию сам написать, потому что это, наверное, будет чуточку быстрее».
makise_homura
+1
Это всё относится к безглючности. И да, строка вида

add al,48

всё еще является самым быстрым в мире способом перевести число от 0 до 9 в десятичный вид. Только область применимости маловатоа.
NTFS Изменён автором
0
как это на питоне сделать?) или js-е
(именно с такой же скоростью)
Sasha-Flyer Изменён автором
0
На питоне или js — очевидно, никак, эти языки не имеют прямого доступа к командам ассемблера и регистрам процессора.
По многим причинам.
Впрочем, это и не требуется — там где нужны прям высокие скорости прям огромных объемов обработки — питон и не применяют, применяют, скорее всего, C или Фортран с библиотеками.
NTFS Изменён автором
0
Для Питона можно делать нативные библиотеки, вроде (но я не знаю, как). А там уже можно использовать и Ассемблер.
UPD: Что-то нашёл, но пока не вник. docs.python.org/3/extending/extending.html
UPD 2: На ЯВУ обычно используют Си, но Ассемблер можно использовать в ассемблерных вставках или в отдельной уже нативной библиотеке.
Farxial Изменён автором
0
Скорее всего, не нативные библиотеки, а просто подключение dll/so с прямыми вызовами. Это почти любой компилятор и интерпретатор умеют, главное, соблюсти стандарт передачи (стек, регистры, порядок аргументов)
NTFS
0
главное, соблюсти стандарт передачи
Няз, сейчас об этом задумываться не надо, сейчас это всё спрятано в прокладки для биндингов.
makise_homura
0
Ассемблер хорош, и я в юности с ним игрался) (пока меня не достала необходимость подстраиваться под постоянно меняющуюся архитектуру x86 (а сейчас ведь не только x86 популярна уже))
Но там можно затрахаться делать сложную большую систему (и я лично таких так и не сделал — слишком прокрастинатор)
Но как искусство, если ты хочешь сделать вылизанный дешевр, подобный кристаллу, Ассемблер подходит) Проблема в том, что к твоей идеально созданной пони потом захотят приделать ещё руку или седло или что-то ещё, ну знаешь, как обычно происходит в постоянно развивающемся IT (особенно в бизнесе), там именно это всё время и пытаются сделать (хотя, казалось бы, сделай стабильный рабочий качественный продукт и успокойся). Если последнее не относится к Табуну, то, наверное, можно (но логика Табуна всё равно большая)
Farxial
0
Для бизнеса на Ассемблере никто и не пишет, там даже стандартные ЯВУ уже бесполезны, только готовые фреймворки.
NTFS
0
вылизанный дешевр, подобный кристаллу
на ассемблере его сделать на порядок сложнее, чем на языках типа С или даже Python. И скорее всего, если это что-то объёмное, ты там наделаешь ошибок или уязвимостей.
makise_homura
0
Это можно понять как «ты не сможешь или тебе будет слишком сложно сделать вылизанный дешевр, подобный кристаллу». Я бы не стал так никого демотивировать. Но да, это сложнее, так же как нарисовать картину руками может быть сложнее, чем в редакторах с инструментами/фильтрами и затем напечатать, например.
Farxial Изменён автором
0
Опять же, вопрос цели. Одно дело — просто абстрактно показать свой скилл (а-ля демосцена), другое — сделать работающий крупный коммерческий продукт с приемлемыми затратами на разработку при нужном уровне качества.
makise_homura
0
Мы написали почти об одном и том же, только акцентируясь на разных вещах. У тебя «с приемлемыми затратами на разработку» втесалось в задачу по умолчанию, а я отметил, что сделать сложную большую систему можно, но затрахаешься (а дальше — про то, что дальше ещё может потребоваться её расширять, хотя всё, ты уже сделал всё, что было нужно и можно поставить точку). И вот, если твою готовую систему потом не будут пытаться раздуть, то и лишних проблем не будет. И тогда в принципе можно, если прям хочешь. Но сложнее, чем на ЯВУ, да.
Конечно же, всё зависит от целей. Просто я акцентируюсь на целях для индивида (хочет ли он и не влом ли ему), а не для бизнеса.
Farxial
0
самым быстрым
Во-первых, емнип, нет — на x86_64 маскированный доступ к регистрам медленнее доступа к полному регистру (это, правда, может нивелироваться длительностью сложения, но в эпоху быстрых АЛУ с синтезированным carry lookahead это вряд ли); а ещё ведь число надо в регистр положить и потом оттуда его обратно вернуть. А во-вторых это совершенно непортируемая вещь. И знаешь, какими словами я называю тех, кто вот так вот использует ассемблер x86_64 (а то и i8086, как у тебя) там, где он не нужен? Лучше тебе не знать, обидишься =)
Не надо оптимизировать то, что оптимизировать не нужно. Это почти гарантия (у тебя — точно гарантия) непортируемости (твой код не заработает на ARM), несовместимости (твой код неприменим к UCS-2) и/или багов (ты не проверяешь al на корректность, и с большой долей вероятности содержимое al, равное (EOF — 48) приведёт к вылету твоей проги по SIGHUP), особенно там, где реализация отличается от идеальной.
makise_homura
0
там, где он не нужен

А зуб дашь, что он там не нужен? Вот стоит задача, перевести число на процессоре x86 из вида 0 в вид '0'
Больше ничего.
Не нравится al, пожалуйста, из памяти

add [es:si], 48

А во-вторых это совершенно непортируемая вещь

Я про это и пишу, что так можно, но страдает переносимость и универсальность. И с чем мы тут спорим? Что библиотечный вызов лучше самодельных манипуляций с памятью? Да, лучше. Что спуск на уровень процессора может поднять скорость, но потерять всё остальное? Да, может.
NTFS
0
Вот стоит задача, перевести число на процессоре x86 из вида 0 в вид '0'
Это не задача.
Задача — это «перевести число на процессоре i8086 из вида 0...9 в вид '0'...'9' в 8-битной ASCII кодировке; число лежит в регистре al, имеются гарантии, что байт в al точно лежит внутри диапазона [0x00; 0x09], результат должен также находиться в регистре al; имеются гарантии, что дальнейшего развития этого кода не будет».
Такую задачу твой код решает. Проблема в том, что это задача настолько узкая, что практическая её целесообразность сравнима с нулём.
makise_homura
0
Ну раскрой задачу до близкой к реальной, например, дамп битового массива в хекс-вид на диск.
Всё равно в некоторых случаях комбинация shr 4, shl 4 и заполнения al,ah с последующей записью по таблице 0..9A...F окажется быстрее, чем библиотечный вызов ToString(«X2»)
И еще раз повторю, быстрее — не значит, удобней и универсальней. Это само собой.
NTFS
0
дамп битового массива в хекс-вид на диск
А тут мне отчего-то кажется, что SIMD будут портабельнее (если, конечно, писать их билтинами, а не ассемблером) и быстрее. И даже если их не использовать, массив из 256 предварительно созданных подстрок «00 »...«FF » и что-то типа for(size_t outpos = 0; outpos < insize * 3; outpos += 3) {memcpy(outbuf[outpos], bytes_text[*indata++], 3);} будет ещё быстрее — потому что компилятор сумеет такой цикл заанроллить и либо векторизовать, либо запрефетчить, ну и в любом случае дать процессору самому запараллелить выполнение.
makise_homura
0
Хе, так я про то же самое и пишу — что ты вместо библиотечной функции пишешь свою, на простейшем ассемблере x86, на операциях расширенных команд или на С++ с помощью подготовленных таблиц (таким образом, например, тригонометрию вычисляют, где стандартные sin(x), cos(x) тормозят).

И как это опровергает мой исходный тезис?
окажется быстрее, чем библиотечный вызов ToString(«X2»)
NTFS
0
Так я-то про другой тезис:
всё еще является самым быстрым в мире способом перевести число от 0 до 9 в десятичный вид
makise_homura
0
По-моему, было очевидно, что я использовал

add al,48

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

add [ebx],eax

что в два с половиной раза быстрее работает, в грубом измерении. Скорее всего, использование SSE еще это улучшит.
NTFS Изменён автором
0
Делая все эти измерения, ты учитываешь тот факт, что современные процессоры могут предсказывать, какие команды им дадут, и сильно ускорять свою работу при выполнении однотипных вычислений?
Sasha-Flyer
+1
Естественно. Потому в реальности никакого стократного ускорения не будет. Но даже в 2-4 раза — уже неплохо.
NTFS
0
В реальности, как видно в моём примере ниже, ассемблер даже медленнее.
makise_homura
0
было очевидно
Нет.
Я, например, тебя понял так, что «ассемблер быстрее (написанного ручками) сишного кода», что, как видно ниже, неверно.
makise_homura
0
«ассемблер быстрее (написанного ручками) сишного кода», что, как видно ниже, неверно

Я уже видел мнение, что интерпретируемые языки по скорости могут начать обгонять компилируемые на уровне больших проектов, ибо поддаются оптимизации в рантайме исходя из данных и статистики исполнения, в то время как настолько умный компилятор будет работать крайне медленно и все равно не сможет перестраиваться на лету.
StaSyaN
0
В этом есть доля правды — даже PGO не всегда помогает столь глубоко оптимизировать код, потому что профиль может не совсем соответствовать реальным условиям. Кроме того, на OoO-процессорах, по факту, и так любой язык, по сути, интерпретируемый — ибо на EU исполняется совсем не то, что льётся в префетчер инструкций как раз благодаря переупорядочиванию инструкций, предсказанию ветвлений и спекулятивке.
makise_homura
0
partizan150 Изменён автором
+1
Итак, я потратил немного времени, дабы пояснить мою мысль. Не для makise_homura — он сам знает, чего ему лучше.
Для тех, кто не понял исходный тезис.

Безглючнее — да, быстрее — далеко не всегда.

Вот задача: дан в памяти массив байт со значениями от 0 до 9, нужно его преобразовать в символы соответствующие в той же области памяти. Пишем на Паскале (потому что его я знаю лучше).
Библиотечная функция преобразования — IntToStr, параметр — элемент массива, на выходе обрезаем до символа.

for i:=0 to SZ-1 do
  c[i]:=IntToStr(p[i])[1] ;

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

for i:=0 to SZ-1 do
  c[i]:=chr(p[i]+48) ;

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

label m1 ;
asm
  mov ebx,p
  mov ecx,SZ
  mov al,48
m1:
  add [ebx],al
  inc ebx
  loop m1

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

label m1 ;
asm
  mov ebx,p
  mov ecx,SZ
  shr ecx,2

  mov eax,48
  shl eax,8
  add eax,48
  shl eax,8
  add eax,48
  shl eax,8
  add eax,48
m1:
  add [ebx],eax
  add ebx,4
  loop m1

Итоги испытаний на массиве 100 Мб:
Скрин запусков

В 100 раз быстрее при отказе от библиотечной функции
Еще в 4 раза быстрее при переходе к прямому общению с процессором
И еще в 2 раза лучше при обработке по четыре байта за раз, работая с 32-битными регистрами.
Полагаю, что при использовании 64-битных регистров, пойдет еще улучшение, оставлю это в качестве домашнего задания для желающих.

Полный код примера:
FPC 3.0.4, Win32
program testasm ;

{$mode objfpc}
{$asmmode intel}

{.$define uselibfunc}
{$define asmproc}

uses SysUtils ;

const SZ = 1024*1024*100 ;

procedure int2str_asm(p:Pointer); assembler ;
label m1 ;
asm
  mov ebx,p
  mov ecx,SZ
  mov al,48
m1:
  add [ebx],al
  inc ebx
  loop m1
end ;

procedure int2str_asm4byte(p:Pointer); assembler ;
label m1 ;
asm
  mov ebx,p
  mov ecx,SZ
  shr ecx,2

  mov eax,48
  shl eax,8
  add eax,48
  shl eax,8
  add eax,48
  shl eax,8
  add eax,48
m1:
  add [ebx],eax
  add ebx,4
  loop m1
end ;

var p:^Byte ;
    v:Pointer ;
    c:^Char ;
    i:Integer ;
    dfix:TDate ;
begin
  GetMem(p,SZ) ;

  v:=p ;
  c:=v ;

  Randomize() ;

  for i:=0 to SZ-1 do
    p[i]:=Random(10) ;

  Writeln('Byte arr:') ;
  for i:=0 to 32 do Write(p[i]) ;
  Write('...') ;
  for i:=SZ-33 to SZ-1 do Write(p[i]) ;
  Writeln ;

  dfix:=Now() ;

  {$ifdef asmproc}
  int2str_asm4byte(p) ;
  {$else}
  {$ifdef uselibfunc}
  for i:=0 to SZ-1 do
    c[i]:=IntToStr(p[i])[1] ;
  {$else}
  for i:=0 to SZ-1 do
    c[i]:=chr(p[i]+48) ;
  {$endif}
  {$endif}

  Writeln('Timedelta: ',(Now()-dfix)*3600*24:3:3) ;

  Writeln('Char arr:') ;
  for i:=0 to 32 do Write(c[i]) ;
  Write('...') ;
  for i:=SZ-33 to SZ-1 do Write(c[i]) ;

  FreeMem(p,SZ) ;
end.
<pre>
</pre>


Отлично, а минусы будут. Да, будут:
Попытка перенести этот код в другой проект — будет плохо.
При появлении в массиве других байт, кроме интервала 0..9 — будет плохо.
Запуск на другой архитектуре процессора — ну вы поняли, да.

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

У меня всё.
PS: makise_homura может переписать пример на С с его itoa, думаю, разрыв будет менее мощным, но будет.
NTFS Изменён автором
+2
Эхх, вот бы Табун 7к комментариев рендерил за 0.3 секунды…
Sasha-Flyer
+4
Не для makise_homura
:(
makise_homura может переписать пример на С с его itoa, думаю, разрыв будет менее мощным, но будет.
Во-первых, ты читеришь. Ты делаешь замену in-place, предполагая, что у тебя уже *p подготовлен — то есть, ты ожидаешь там не 0x12345678, а 0x0102030405060708. В то время как сравниваешь это с реализациями, где ничего такого нет, это крайне некорректно.
К тому же, я не понимаю, зачем тебе «mov eax,48; shl eax,8; add eax,48; shl eax,8; add eax,48; shl eax,8; add eax,48», когда сразу можно написать «mov eax, 0x30303030».

Давай, короче, честно сделаем — решим _реально_ поставленную тобой задачу, «дамп битового массива в хекс-вид» (подразумевая, что у нас не будет цифр 0xa...0xf).

Код
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>

void conv_printf(void *outbuf, const void *inbuf, size_t sz)
{
    while(sz--)
    {
        uint8_t q = *(const char*)inbuf++;
        sprintf(outbuf, "%01u%01u", (q>>4)&0xF, q&0xF);
        outbuf += 2;
    }
}

void conv_math(void *outbuf, const void *inbuf, size_t sz)
{
    while(sz--)
    {
        uint8_t q = *(const char*)inbuf++;
        *(uint16_t*)outbuf = ((q&0xF0)>>4)|((q&0xF)<<8)|0x3030;
        outbuf += 2;
    }
}

void conv_asm16(void *outbuf, const void *inbuf, size_t sz)
{
    asm(
        "movq %0, %%rsi;"
        "movq %1, %%rdi;"
        "movq %2, %%rcx;"
    "1:"
        "xorw %%ax, %%ax;"
        "movb (%%rsi), %%al;"
        "shlw $4, %%ax;"
        "shrb $4, %%al;"
        "xchgb %%ah, %%al;"
        "orw $0x3030, %%ax;"
        "movw %%ax, (%%rdi);"
        "addq $2, %%rdi;"
        "incq %%rsi;"
        "loop 1b;"
        :
        :"r"(inbuf), "r"(outbuf), "r"(sz)
        : "memory", "rsi", "rdi", "rdx", "rcx", "rbx", "rax"
    );
}

void conv_asm64(void *outbuf, const void *inbuf, size_t sz)
{
    asm(
        "movq %0, %%rsi;"
        "movq %1, %%rdi;"
        "movq %2, %%rcx;"
        "shrq $2, %%rcx;"
        "movabsq $0x3030303030303030, %%rbx;"
        "movabsq $0x0f0f0f0f0f0f0f0f, %%rdx;"
        "movabsq $0x000f000f000f000f, %%r8;"
        "movabsq $0x0f000f000f000f00, %%r9;"
    "1:"
        "movl (%%rsi), %%eax;"
        "pdepq %%rdx, %%rax, %%rax;"
        "movq %%rax, %%r10;"
        "andq %%r8, %%r10;"
        "shlq $8, %%r10;"
        "andq %%r9, %%rax;"
        "shrq $8, %%rax;"
        "orq %%r10, %%rax;"
        "addq %%rbx, %%rax;"
        "movq %%rax, (%%rdi);"
        "addq $4, %%rsi;"
        "addq $8, %%rdi;"
        "loop 1b;"
        :
        :"r"(inbuf), "r"(outbuf), "r"(sz)
        : "memory", "rsi", "rdi", "rdx", "rcx", "rbx", "rax"
    );
}

int main()
{
    printf("Testing correctness:\n");

    char inbuf[16] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x09, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x90, 0x09 };

    char *outbuf_printf = calloc(33, 1);
    char *outbuf_math   = calloc(33, 1);
    char *outbuf_asm16  = calloc(33, 1);
    char *outbuf_asm64  = calloc(33, 1);

    conv_printf (outbuf_printf, inbuf, 16);
    conv_math   (outbuf_math,   inbuf, 16);
    conv_asm16  (outbuf_asm16,  inbuf, 16);
    conv_asm64  (outbuf_asm64,  inbuf, 16);

    printf(" - printf: %s\n", outbuf_printf);
    printf(" - math:   %s\n", outbuf_math);
    printf(" - asm16:  %s\n", outbuf_asm16);
    printf(" - asm64:  %s\n", outbuf_asm64);

    free(outbuf_printf);
    free(outbuf_math);
    free(outbuf_asm16);
    free(outbuf_asm64);

    printf("Testing speed:\n");

    size_t sz = 10000000;
    char *inbuf2 = malloc(sz);
    for (int i = 0; i < sz; ++i)
    {
        uint8_t u = random() % 100;
        inbuf2[i] = u % 10 + u / 10 * 16;
    }

    outbuf_printf = calloc(1 + 2 * sz, 1);
    outbuf_math   = calloc(1 + 2 * sz, 1);
    outbuf_asm16  = calloc(1 + 2 * sz, 1);
    outbuf_asm64  = calloc(1 + 2 * sz, 1);

    struct timespec tp1, tp2;

    clock_gettime(CLOCK_MONOTONIC, &tp1);
    conv_printf (outbuf_printf, inbuf2, sz);
    clock_gettime(CLOCK_MONOTONIC, &tp2);
    printf(" - printf: %lf\n", (double)(tp2.tv_nsec - tp1.tv_nsec) / 1000000000 + tp2.tv_sec - tp1.tv_sec);
    clock_gettime(CLOCK_MONOTONIC, &tp1);
    conv_math   (outbuf_math,   inbuf2, sz);
    clock_gettime(CLOCK_MONOTONIC, &tp2);
    printf(" - math:   %lf\n", (double)(tp2.tv_nsec - tp1.tv_nsec) / 1000000000 + tp2.tv_sec - tp1.tv_sec);

    clock_gettime(CLOCK_MONOTONIC, &tp1);
    conv_asm16  (outbuf_asm16,  inbuf2, sz);
    clock_gettime(CLOCK_MONOTONIC, &tp2);
    printf(" - asm16:  %lf\n", (double)(tp2.tv_nsec - tp1.tv_nsec) / 1000000000 + tp2.tv_sec - tp1.tv_sec);

    clock_gettime(CLOCK_MONOTONIC, &tp1);
    conv_asm64  (outbuf_asm64,  inbuf2, sz);
    clock_gettime(CLOCK_MONOTONIC, &tp2);
    printf(" - asm64:  %lf\n", (double)(tp2.tv_nsec - tp1.tv_nsec) / 1000000000 + tp2.tv_sec - tp1.tv_sec);

    free(outbuf_printf);
    free(outbuf_math);
    free(outbuf_asm16);
    free(outbuf_asm64);
    free(inbuf2);

    return 0;
}


Результаты
reisen ~/conv_test # gcc -O2 test.c -o test && ./test
Testing correctness:
 - printf: 12345678900987654321123456789009
 - math:   12345678900987654321123456789009
 - asm16:  12345678900987654321123456789009
 - asm64:  12345678900987654321123456789009
Testing speed:
 - printf: 0.704234
 - math:   0.013110
 - asm16:  0.024204
 - asm64:  0.007658
reisen ~/conv_test # gcc -O3 test.c -o test && ./test
Testing correctness:
 - printf: 12345678900987654321123456789009
 - math:   12345678900987654321123456789009
 - asm16:  12345678900987654321123456789009
 - asm64:  12345678900987654321123456789009
Testing speed:
 - printf: 0.693491
 - math:   0.007417
 - asm16:  0.024200
 - asm64:  0.007656
reisen ~/conv_test #
Как ты видишь, при -O3 даже самая быстрая написанная ручками реализация (через pdep + or) уступает развёрнутому компилятором циклу. При -O2 компилятор не оптимизирует столь глубоко, но всё равно, реализация средствами языка быстрее аналогичной реализации средствами ассемблера (asm16). При этом референсная максимально универсальная реализация, разумеется, намного медленнее в любом случае, но мы не о ней, а об ассемблере.
То есть, если у тебя есть задача, ты её можешь оптимизировать, но для этого лезть в ассемблер совсем не обязательно — компилятор умнее тебя и может сделать лучше, чем ты думаешь (и намного быстрее — ему не надо вспоминать синтаксис того же pdep).
makise_homura
0
Мой пример решал другую задачу — перевод чисел от 0 до 9 в их строковое представление.
Про хекс-дамп я к тому времени забыл, но могу и под него переписать.

При этом референсная максимально универсальная реализация, разумеется, намного медленнее в любом случае


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

В итоге, мы оба подтвердили мысль, что библиотечные функции надежные, но медленные.
NTFS
0
Про хекс-дамп я к тому времени забыл, но могу и под него переписать.
Ну, формально у нас BCD-дамп. Hex сделать сложнее, потому что, няз, в x86_64 нет условного add по части регистра. Но суть в том, что ты сравниваешь неэквивалентный код.
выигрыш ассемблера давал скорее всего потому, что FPC не так тщательно оптимизирует
Вероятно. То есть из-за этого создаётся ошибочное мнение, что написанный ручками ассемблер быстрее, чем компилятор.
библиотечные функции надежные, но медленные
Причём часто они медленные именно потому, что библиотечные — т.е. не могут быть проинлайнены или сделаны через близкий jmp вместо call. А там всякие прологи, эпилоги, работа со стеком и т.п.
makise_homura
0
То есть из-за этого создаётся ошибочное мнение, что написанный ручками ассемблер быстрее, чем компилятор.

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

что уже в ряде мест может дать неплохую экономию, если в цикле на 10^7 не будет вызываться каждый раз функция, а обрабатываться на месте.

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

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

мой пример показывает, что это не так, как минимум, для одной задачи и для одного компилятора. Не все проекты в мире пишутся на gcc. Табун ты на gcc не напишешь, и оконное приложение для Windows — я бы тоже предпочел паскалевские варианты с Delphi или Lazarus.
NTFS
0
Как минимум, не медленнее.
Как видишь, медленнее. ISA x86_64 слишком сложна, чтобы 99% людей умели писать лучше компилятора (если только мы как раз не обходим какой-то конкретный баг конкретного компилятора, но это совсем за рамками того, зачем обычно пытаются писать ассемблерный код).
уже в ряде мест может дать неплохую экономию
Конечно, поэтому горячие участки всегда пишут в одном линк-юните.
и для одного компилятора
Очевидно, что тут вина именно компилятора. Выбрав другой компилятор, эту задачу можно сделать без применения ассемблера.
Это всё равно, что писать на ассемблере «потому что у меня с -O0 медленнее работает»: тут надо не ассемблер в руки брать, а -O3 включать.
Не все проекты в мире пишутся на gcc.
Те, которым важно быстродействие — почти все написаны на С или Fortran (по крайней мере из тех, которые я видел, и пусть не целиком, но горячие участки). К тому же у меня есть подозрение, что связка FPC + современный LLVM сможет, скорее всего, генерировать куда более быстрый код, чем FPC с его стандартным кодогенератором.
Ну и опять же, ассемблер — это как бы уже не паскаль. Почему бы, если всё равно используется другой язык, не использовать С вместо паскаля?
Табун ты на gcc не напишешь
У него и нет такой цели. У него узкое место — в основном из-за работы с DOM и, возможно, с базой, а не из-за строковых преобразований как таковых.
makise_homura
0
Ну и опять же, ассемблер — это как бы уже не паскаль. Почему бы, если всё равно используется другой язык, не использовать С вместо паскаля?

Потому что у нас есть задача, которую решаем на Паскале и нужно ускорить решение на Паскале?

а не из-за строковых преобразований как таковых.

Вроде бы весь этот пост — как раз о строковых преобразованиях, которые тормозят в 10 раз. Я плохо помню строковые системы в JS, но думаю, что даже простая замена функции на складывание строк, приведенных из целых — будет быстрее.

Я всё еще не понимаю, о чем ты споришь. Мой тезис — библиотечную функцию меняем на самодельную, и выйдет хорошо по скорости. При этом, самодельную можно попробовать сделать на Ассемблере, это может дать еще пользу (а может и не дать)
NTFS
0
и нужно ускорить решение на Паскале?
Так зачем же ты тогда ускоряешь его на ассемблере?
но думаю, что даже простая замена функции на складывание строк, приведенных из целых — будет быстрее.
Ты же понимаешь, что в приведённом коде на одну реализованную по месту строковую операцию будет ещё тысяча при её вставке в DOM?
библиотечную функцию меняем на самодельную, и выйдет хорошо по скорости
И не забывай, что обычно при этом плохо по универсальности и надёжности. Важен баланс этих двух вещей в каждом конкретном случае.
А ещё не забывай, что функция в том же линк-юните, скорее всего, проинлайнится, что как раз и может дать основной плюс (а не реализация как таковая).
это может дать еще пользу (а может и не дать)
В 99% случаев не даст.

То есть, если более конкретно: твой тезис справедлив не «как есть», а с определёнными условиями, которые надо обязательно учитывать, а не заменять напропалую все подряд функции на свои велосипеды, как некоторые иногда пытаются.
makise_homura
0
Так зачем же ты тогда ускоряешь его на ассемблере?

Потому что ассемблерная вставка — стандартное решение для большинства компилируемых языков в нативный код? Да, сейчас это не так часто применяется, но всё еще применяется.

И не забывай, что обычно при этом плохо по универсальности и надёжности.

Я с этого и начал.

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

И про это я специально писал, и не раз.

В 99% случаев не даст.

В моём коде я явных косяков не вижу. Значит, или у меня 1% случай попал, или компилятор плохо работает. Попробую сегодня с оптимизацией поиграться ключами, и еще на Linux проверю — порой сборки под Windows и GNU сильно разно работают.

UPD: Вижу, функция chr по сути лишняя, но это принципиальная особенность Паскаля — нельзя просто присвоить целое в символ, его нужно кастовать. Возможно, на неё половина скорости и теряется, в итоге ассемблер и самодельная функция примерно равны. Можно заранее привести указатель, как вариант.
NTFS Изменён автором
0
стандартное решение для большинства компилируемых языков в нативный код?
Я бы назвал это не стандартным решением, а лютым костылём, особенно в эпоху, когда ARM уже прочно вышел на рынок. Стандартное решение — это отдельный объектник или библиотека. Тем более, что сейчас есть LTO и издержки на «мостик» между языками при линковке они обычно хорошо убирают.
или компилятор плохо работает
This. Ну а ещё ты, как я говорил, сравниваешь разный по функционалу код. То есть, к примеру, то, что ты написал на ассемблере, можно прекрасно сделать нативными средствами языка, не используя библиотечные функции вообще.
makise_homura
0
особенно в эпоху, когда ARM уже прочно вышел на рынок.

Ты просто смотрищь с позиций разработчика по железу, я же могу быть уверенным, что мой код будет работать на Windows x64, 90% на Интеле, 10% на AMD

можно прекрасно сделать нативными средствами языка

Опять же, я это сделал и сравнивал все четыре реализации (библиотека, средства языка, ассемблер, улучшенный ассемблер).
NTFS Изменён автором
0
я же могу быть уверенным, что мой код будет работать на Windows x64
Вот потому владельцы даже не то, что Raspberry Pi, но и просто машин с линуксом люто ненавидят тех, кто пропагандирует такой подход.
сравнивал все четыре реализации
Ещё раз: твои реализации не соответствуют друг другу. Тебе нужно было либо делать так
for i:=0 to SZ-1 do
  p[i] += 48;
либо так
label m1 ;
asm
  mov edi,c
  mov ebx,p
  mov ecx,SZ
m1:
  mov al,[esi]
  add al,48 // я б написал "or al,0x30" - or понятнее и в некоторых случаях быстрее; но мы именно про аналогию
  mov [ebx],al
  inc ebx
  loop m1
makise_homura
0
Попробую выровнять, но скорее всего, всё равно Ассебмлер будет быстрее, просто за счет отсутствия лишнего кода между циклом и телом.

Вот потому владельцы даже не то, что Raspberry Pi, но и просто машин с линуксом люто ненавидят тех, кто пропагандирует такой подход.

За 20 лет практики мне ничего именно под Rapsberry и Linux еще не заказывали :-) да, могу быть уверен.
На самом деле, кроссплатформенно иногда просили, но как дополнение — всё равно запускать и работать будут под Windows.
NTFS Изменён автором
0
просто за счет отсутствия лишнего кода между циклом и телом.
Не понял, про какой код ты.
За 20 лет практики мне ничего именно под Rapsberry и Linux еще не заказывали :-)
А потом твою прогу возьмут и попытаются там запустить. И будут бомбить, мол, а чего это она там не запускается. Я не первый раз такое вижу (правда, у меня обычно вместо ARM — эльбрус), и всегда горю с безальтернативных ассемблерных вставок, которые блин никто не додумался хотя бы на интринсиках написать.
makise_homura
0
А потом твою прогу возьмут и попытаются там запустить.

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

Не понял, про какой код ты.

Те же самые call, операции по сохранению регистров и т.д.

И будут бомбить, мол, а чего это она там не запускается.
.
Да. я помню, люди спрашивали, почему они не могут установить на Linux их пиратку Word.
NTFS
0
Пока мне за это не заплатили
Ну, тебе никто не запрещает, конечно, требовать денег за реализацию кроссплатформенности. Как и тем, кто занимается портированием, не запрещает бомбить на тебя из-за ненужного усложнения портируемости.
Те же самые call, операции по сохранению регистров и т.д.
Ты в своей реализации через нативную математику языка же не используешь вызовы волатильных функций (да и вообще никаких не используешь).
почему они не могут установить на Linux их пиратку Word.
Ну, ты всё же не ворд пишешь и можешь задуматься о кроссплатформенности заранее.
makise_homura
+1
Ну, тебе никто не запрещает, конечно, требовать денег за реализацию кроссплатформенности.

Как и за любую работу, в общем-то.
кто занимается портированием

тоже получают за это деньги или иные плюшки.

можешь задуматься о кроссплатформенности заранее.

Могу, но зачем? Там где это нужно (игры) я уже подумал, а думать о проекте, который с шансом 99% будет работать на Windows 10 под Intel — ну так себе думание. Голова не казенная.
NTFS Изменён автором
0
Выровнял, привел всё к одной задаче (увеличить весь массив в памяти на 48). Также ввел новый метод — паскалевский Inc по четырем байтам (приведением к ^Cardinal)

Новый код:
if ParamStr(1)='libfunc' then 
    for i:=0 to SZ-1 do
      c[i]:=IntToStr(p[i])[1] ;
  if ParamStr(1)='myfunc8' then 
    for i:=0 to SZ-1 do
      Inc(p[i],48) ;
  if ParamStr(1)='myfunc32' then
    for i:=0 to SZ div 4-1 do
      Inc(p4[i],$30303030) ;
  if ParamStr(1)='asm8' then 
     int2str_asm(p) ;
  if ParamStr(1)='asm32' then 
     int2str_asm4byte(p) ;


И результаты (FPC 3.0.4, Windows 64):


Как видно, только при O3 методы сравнялись по скорости. Насколько я помню, О3 не рекомендуют использовать для рабочих проектов, только для экспериментов (обычная рекомендация это О1 для отладки, О2 для релиза).

То есть, как минимум для FPC ассемблерные вставки не бесполезны. При том, что я их не оптимизировал — использовал по сути инструкции 80386. Подрублю SSE — будет лучше. Задействую 64-битные регистры — тоже лучше (правда, тогда и в самом Паскале можно приводит к ^QWord)
NTFS
0
Насколько я помню, О3 не рекомендуют использовать для рабочих проектов, только для экспериментов
Неправильно помнишь. Основной минус -O3 — если его применять к проекту целиком, то во-первых, компиляция будет намного дольше, во-вторых, код будет заметно увеличен (в основном из-за развёртки циклов), в-третьих, если он написан неправильно (к примеру, со слишком вольным отношениям к синхронизации, когда ты вместо нормальных мьютексов просто используешь волатильные переменные и считаешь, что if(x++) — атомарная операция), это может повлечь странное поведение вплоть до падений. Поэтому применять -O3 ко всему проекту и не стоит. Его применяют только к горячим юнитам — что, собственно говоря, ты и пытаешься сделать, только руками, впихивая ассемблерные вставки на место вполне обычного кода. Зачем делать это самому, если можно дать это сделать компилятору?
К слову, странно, что оптимизированный «восьмибитный inc» не быстрее ассемблерной вставки — видимо, FPC не умеет в адекватную развёртку циклов.
обычная рекомендация это О1 для отладки, О2 для релиза
-O0 -g для отладки.
То есть, как минимум для FPC ассемблерные вставки не бесполезны.
Как мы видим, бесполезны (-O3 не опаснее твоего ассемблера; если -O3 ломает твой код, то значит, ты достаточно невнимателен, чтобы налажать и в ассемблере).
Подрублю SSE — будет лучше.
Тогда и остальной код надо компилировать с -msse и прочими ключами типа -march и -mtune.
правда, тогда и в самом Паскале можно приводит к ^QWord
Именно. В твоём коде нет ничего, что бы не покрывалось средствами языка.
makise_homura
0
Зачем делать это самому, если можно дать это сделать компилятору?

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

Тогда и остальной код надо компилировать с -msse и прочими ключами типа -march и -mtune.

Старый FPC так не умел, посмотрю по свежему, вроде указать процессор было можно, но не точно.
NTFS Изменён автором
0
С другой стороны, зачем это делать компилятору, если можно сделать самому?
Затем, что компилятор не сломает тебе совместимость и кроме того, сделает наилучшим образом, о котором ты можешь не знать или не помнить (вот ты сейчас все SSE-инструкции помнишь? А AVX? Я подозреваю, что ты сходу даже порядок операндов в PDEP/PEXT не вспомнишь без мануала — ну, как минимум, я вот не вспомнил, я даже название этих инструкций-то не сразу вспомнил).
но если пример более сложный и менее очевидный (например, что-то с условными переходами и использованием флагов) — может оказаться и лучше
Я пока ни одного такого случая не видел, кроме совсем уж вариантов уровня «библиотека для конкретной архитектуры, поддержка которой (библиотеки) реализована в самой архитектуре» (камень в сторону интела, у которого пунктик на то, чтобы линпаки и спеки показывали максимальные значения, вплоть до рофлов в плане «в Core i69420 будет реализована инструкция LINPACK, которая запускает на процессоре линпак-бенчмарк»).
Старый FPC так не умел, посмотрю по свежему, вроде указать процессор было можно, но не точно.
Как минимум, так умеет LLVM, можно попробовать FPC скомпилить с ним и потестировать.
makise_homura
0
К слову
Выбрав другой компилятор

выбор-то не очень богат, если честно. Чтобы позволял собирать под несколько ОС, поддерживал весь ООП, был бесплатным хотя бы для некоммерческих приложений, применялся многими и не требовал изучений всякой новомодной экзотики — это разные версии сишных компилятор и FPC.
D и Dart мало применяются, Бейсик уже не развивается (хотя на FBC я делал проект, он неплох), Rust и Go нужно учить заново (и я не уверен, что по ним много готовых вопросов/ответов)
NTFS
0
выбор-то не очень богат, если честно. Чтобы позволял собирать под несколько ОС, поддерживал весь ООП, был бесплатным хотя бы для некоммерческих приложений, применялся многими и не требовал изучений всякой новомодной экзотики
clang и gcc всем этим критериям удовлетворяют, а заодно и являются де-факто промышленным стандартом.
makise_homura
0
clang и gcc всем этим критериям удовлетворяют

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

Выбор это всегда хорошо. Хочешь, printf, хочешь, Writeln, а хочешь PRINT
Сейчас по факту мне почти всегда приходится printf использовать, и это удручает.
NTFS
0
Ну вот ты на скольких языках разговариваешь обычно? Я подозреваю, что не больше двух, ну или если живёшь в нацреспублике — трёх (английский, русский и местный). Тебе ж не нужно знать десяток языков для полного счастья (исключение в виде тех, кому это надо для профессии, не учитываем). Ну вот и компиляторов достаточно парочки примерно по той же причине, если ты пишешь не что-то особенное, а универсальные быстродействующие библиотеки.
makise_homura
0
исключение в виде тех, кому это надо для профессии, не учитываем

Программист это профессия, знать один язык — это как переводчику знать два языка. Лучше чем один, но мало.
NTFS
0
Напомню, в плане программиста мы сейчас не про языки, а про компиляторы одних и тех же языков (да, согласен, наверное, моя аналогия в этом плане запутывающая)
makise_homura Изменён автором
0
И почему-то все перечисленные компиляторы идут для С.
NTFS
0
И почему-то все перечисленные компиляторы идут для С.

>Currently the main GCC distribution contains front ends for C (gcc), C++ (g++), Objective C, Fortran, Ada (GNAT), Go, and D.
А ещё у них есть внешние фронтэнды под всякую экзотику, типа модулы, кобола и твоего любимого паскаля. А LLVM (к которому С/С++-фронтэнд — clang) умеет брать IR от твоего же любимого FPC.
makise_homura
0
Fortran, Ada (GNAT), Go, and D

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

Релизный FPC пока с LLVM работать не умеет, насколько я помню. Надо посмотреть.
NTFS
0
Сарказм считан :3 Ну, на самом деле, оно есть и есть, не мешает же. Кому надо — те развивают.
Релизный FPC пока с LLVM работать не умеет, насколько я помню.
Вроде когда я смотрел насчёт этого, говорилось, что он умеет (но прям самостоятельно я не проверял, да).
makise_homura
0
Пока только в разработке, в релизе будет позже.
wiki.freepascal.org/FPC_Roadmap
NTFS
0
«В активной разработке» не значит, что ещё не работает. Я вот вижу, что вроде как у них с ним нормально (todo-список у них больше по дополнительной, нежели базовой функциональности). Более-того, как я и ожидал, они там пишут оценки типа «is about 10% faster when compiled with LLVM on an Intel Haswell processor, or 18% if you also enable link-time optimization», что явно говорит о том, что LLVM оптимизирует лучше нативного бэкенда FPC.
makise_homura
0
Там его вроде как надо собирать из исходников, это слишком сложно для разовых опытов. Как выпустят 4.0 — опробую.
NTFS
0
Тогда вопрос к разрабам FPC, почему тот же clang+llvm или gcc собрать просто, а FPC почему-то сложно…
makise_homura
0
Просто неохота. Одно дело скачать релиз и поставить его в два клика, другое дело запускать сборку.
NTFS
0
Какие сложные у вас разговоры xD

Самое низкоуровневое что я делал это менял отдельные биты в байте чтобы хранить инфу побитово, оказалось что это медленее, чем хранить в обычных буленах и примерно также места занимает =(
Niko_de_Andjelo Изменён автором
0
оказалось что это медленее, чем хранить в обычных буленах и примерно также места занимает =(
Именно. Битовые поля — вещь достаточно медленная, а при современных объёмах памяти даже в embedded-е — никаких проблем в том, чтобы на булеан отдавать int целиком, пусть и остальные 31/63 байта из него использоваться не будут.
makise_homura
0
никаких проблем в том, чтобы на булеан отдавать int целиком

Да int зачем-то? byte или char под булевый тип идет.

Битовые типы удобны для множеств — когда доступ по индексу особо не нужен, а нужно проверять вхождение или складывать/перемножать. Если работать как с массивом — то да, это бессмысленно и бесполезно, если только речь не идет о проектах COM-формата для DOS или под старые машины типа PDP/БК, там за каждый бит идет сражение.
NTFS Изменён автором
0
Да int зачем-то? byte или char под булевый тип идет.

Емнип, это зависит от размера регистров платформы. Просто забивается регистр целиком и все.
StaSyaN
+1
Да int зачем-то? byte или char под булевый тип идет.
Вот именно потому, что ты не в курсе, зачем, ты и не сможешь написать код лучше компилятора.
int — это by definition тип, максимально быстрый на данной платформе. Да, char медленнее int — банально потому, что невыровненные и короткие чтения медленнее выровненных и полноразмерных (т.к. это чтение + сдвиг и/или bzhi). Да, это единичные такты, а ещё это часто нивелируется кэшем, особенно если ты правильно расставляешь префетчи (в безкэшевых архитектурах, или в тех, где кэш опционален, невыровненные чтения, к примеру, вообще невозможны — на RISCV lw a0, 1 тебе тут же кинет SIGILL и не поморщится); но если ты собираешься именно оптимизировать код, а не показывать своё знание ассемблера, то это важно знать.
makise_homura Изменён автором
0
Даже с учетом массива? То есть, обработка массива 1000 булевых переменных, определенных как int, будет быстрее, чем обработка того же массива булевых переменных, определенных как unsigned char с одинаковыми константами TRUE 1, FALSE 0?
Скорее всего, по скорости будет сравнимо, но памяти заберет больше.
NTFS
0
Проверил, опять же на FPC, с -O3, раз его можно применять без опаски.

Как и ожидал, скорости сравнимые, чуть-чуть снижается по мере размера типа, расход памяти очевидно пропорционален SizeOf(MyBool)

Испытания суммирования массива самодельных булевых значений

К слову, в Паскале, C# и C++ — sizeof(Boolean) даёт единицу.

Код проекта:

program testbool ;

{$mode objfpc}

uses SysUtils ;

const SZ = 1024*1024*100 ;

//type MyBool = Integer ;
//type MyBool = shortint ;
type MyBool = Smallint ;
//type MyBool = QWord ;

const MYTRUE = 1 ;
      MYFALSE = 0 ;

var p:^MyBool ;
    i:Integer ;
    dfix:TDate ;
    cnttrue,cntfalse:Cardinal ;
begin
  Writeln('SizeOf(MyBool)=',SizeOf(MyBool)) ;
  GetMem(p,SZ*SizeOf(MyBool)) ;

  Randomize() ;

  for i:=0 to SZ-1 do
    if Random(2)=1 then p[i]:=MYTRUE else p[i]:=MYFALSE ;

  Write('MyBool arr: ') ;
  for i:=0 to 32 do Write(p[i]) ;
  Write('...') ;
  for i:=SZ-33 to SZ-1 do Write(p[i]) ;
  Writeln ;

  dfix:=Now() ;

  cnttrue:=0 ;
  cntfalse:=0 ;
  for i:=0 to SZ-1 do
    if p[i]=MYTRUE then Inc(cnttrue) else Inc(cntfalse) ;

  Writeln('TRUE= ',cnttrue,' FALSE= ',cntfalse,' ALL=',cntfalse+cnttrue,' SZ= ',SZ) ;
  Writeln('Timedelta: ',(Now()-dfix)*3600*24:3:3) ;

  FreeMem(p,SZ*SizeOf(MyBool)) ;
end.

NTFS
+1
в Паскале, C# и C++ — sizeof(Boolean) даёт единицу
В GCC это задаётся при сборке компилятора через BOOL_TYPE_SIZE (правда, влияет прежде всего на встроенный _Bool, который потом, видимо, становится stdbool-ным bool). Зачем так сделано? Всё просто: именно потому, что когда ты используешь _Bool, ты обычно делаешь это не для скорости, а для смыслового разграничения, чтобы тот, кто читает твой код, сразу понял, что это не просто int, а булеан. Кроме того, да, куда вероятнее, что ты будешь работать с массивами, а не с отдельными переменными в стеке, поэтому тут обычно размер важнее скорости. Но если мы выжимаем именно максимальную скорость и не ограничены доступом к памяти — тогда логичнее использовать int.
makise_homura
0
Но если мы выжимаем именно максимальную скорость и не ограничены доступом к памяти — тогда логичнее использовать int.

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

В любом случае, мой исходный тезис о использовании однобайтных типов в качестве булевых подтверждается и опытами, и sizeof в популярных системах. Четырехбайтный логический тип — это очень специфичное решение.
NTFS
0
Я не могу придумать пример, где потребуется большая скорость обработки небольшого количества булевых переменных
А я не могу придумать пример, кроме ретрокомпьютинга и микроконтроллеров, где нужно было бы ужиматься в uint8_t вместо «родной» ширины регистров процессора.
мой исходный тезис о использовании однобайтных типов в качестве булевых
Исходный тезис Нико был о скорости, а не о том, как это реализовано во встроенных типах. Если вопрос именно в скорости, то int как минимум не медленнее uint8_t, а в идеале — быстрее.
makise_homura
0
Даже с учетом массива?
Если мы говорим о массивах, то там уже в игру включаются кэши, APB и префетчи, которые компилятор может расставлять, видя, что ты последовательно обрабатываешь огромную строку. И это я уже не говорю о том, что цикл, скорее всего, развернётся и/или векторизуется — то есть в реальности ты будешь обрабатывать всё равно 64-битные значения, а не 8-битные. А если массив ещё и большой, то всё равно упираться всё будет в чтение из памяти.
makise_homura
0
т.е. нампай — не самая оптимизированная либа для работы с числами?
Sasha-Flyer
0
Ага, меня в ранних реализациях InterBase удивляло то, что можно в структуру серверной БД внедрять код процедур, написанных чуть ли не на ассемблере.
Randy1974
0
Почему нет, если соблюсти конвенции и аккуратно обращаться с памятью — какая серверу разница, на чем код? Я на Delphi делал, и на FreePascal.
NTFS
+1
Ну, в чём-то мы коллеги, получается, я долго занимался небольшими клиент-серверными БД (большие не делают в одиночку), где серверная часть на FireBird, а клиентская на Дельфях, теперь перешёл на Lazarus, ибо и то и другое бесплатное и кроссплатформенное, но начинал я с Паскаля, который ещё под DOS.
Randy1974 Изменён автором
0
Лол, неужели банальная функция форматирования даты столь тормозная? О.о
Кажется, в лайвстрите что-то намудрили)
makise_homura
0
Жесть, а почему?
Niko_de_Andjelo
+2
Ответ на твой вопрос кроется где-то здесь
Спойлер
if (isset($aParams['now'])) {
        if ($iDate+$aParams['now']>$iNow) {
            return $oEngine->Lang_Get('date_now');
        }
    }

    /**
     * Если указана необходимость на проверку minutes back
     */
    if (isset($aParams['minutes_back'])) {
        require_once('modifier.declension.php');

        $iTimeDelta = round(($iNow- $iDate)/60);
        if ($iTimeDelta<$aParams['minutes_back']) {
            return ($iTimeDelta!=0)
                ? smarty_modifier_declension(
                    $iTimeDelta,
                    $oEngine->Lang_Get('date_minutes_back', array('minutes'=>$iTimeDelta)),
                    $oEngine->Lang_GetLang()
                )
                : $oEngine->Lang_Get('date_minutes_back_less');
        }
    }

    /**
     * Если указана необходимость на проверку minutes back
     */
    if (isset($aParams['hours_back'])) {
        require_once('modifier.declension.php');

        $iTimeDelta = round(($iNow- $iDate)/(60*60));
        if ($iTimeDelta<$aParams['hours_back']) {
            return ($iTimeDelta!=0)
                ? smarty_modifier_declension(
                    $iTimeDelta,
                    $oEngine->Lang_Get('date_hours_back', array('hours'=>$iTimeDelta)),
                    $oEngine->Lang_GetLang()
                )
                : $oEngine->Lang_Get('date_hours_back_less');
        }
    }

    /**
     * Если указана необходимость автоподстановки "Сегодня", "Вчера", "Завтра".
     */
    if (isset($aParams['day']) and $aParams['day']) {
        switch (date('Y-m-d', $iDate)) {
            /**
             * Если дата совпадает с сегодняшней
             */
            case date('Y-m-d'):
                $sDay=$oEngine->Lang_Get('date_today');
                break;
            /**
             * Если дата совпадает со вчерашней
             */
            case date('Y-m-d', mktime(0, 0, 0, date("m"), date("d")-1, date("Y"))):
                $sDay=$oEngine->Lang_Get('date_yesterday');
                break;
            /**
             * Если дата совпадает с завтрашней
             */
            case date('Y-m-d', mktime(0, 0, 0, date("m"), date("d")+1, date("Y"))):
                $sDay=$oEngine->Lang_Get('date_tomorrow');
                break;

            default:
                $sDay=null;
        }
        if ($sDay) {
            $sFormat=str_replace("day", preg_replace("#(\w{1})#", '\\\${1}', $sDay), $aParams['day']);
            return date($sFormat, $iDate);
        }
    }

Но что именно тут не так — я хз, ибо не шарю в php)
Sasha-Flyer
+3
Тут вообще много кода, который хз зачем нужен (в смысле, come on, можно всегда писать дату в (моём любимом) формате ISO, нах нужны все эти «дни назад»? Да, сорри, я эгоист немножко)… но не знаю, насколько быстро он работает. А вот про регулярные выражения (preg_replace) я читал, что они медленные. Хотя не знаю, так ли это в случае с PHP и в данном случае. Но если ты производишь профилировку/профилирование, можно попробовать замерить скорость без preg_replace — т.е. отработает весь код, но без preg_replace. Просто в конце $sFormat= ждёт нужного формата даты.
Farxial
+1
Ой. А это title на комментарии что ли? Я просто не очень вникал в то, где и как это используется. Но сейчас навёл на комментарий и там показывается «X минут назад» и т.д. Но, если да, то интересно: многие ли наводят на дату комментария мышку ради этой всплывашки? Может, если нельзя быстро убыстрить, это можно вынести в настройки?
Farxial
+2
Я уверен, что 99% табунчан и не знало, что если навести мышку на дату, то будет это. А именно ЭТО и замедляло рендер ветки комментов в 10 раз.
Sasha-Flyer
+3
Я вот знал, но тоже узнал в своё время совершенно случайно (и не пользовался по прямому назначению ни разу =)
makise_homura
0
Стоп, такое реально было?
TheScriptComp
0
до сих пор есть, и всё это время было. И вот щас мы в дев чатике спорим — выпиливать ли это полностью (ибо об этом никто не знает и никто не пользуется) либо пытаться как-то оптимизировать это)
Sasha-Flyer
+2
нах нужны все эти «дни назад»?
Особенно бесит, когда это безальтернативно, и ты такой «блин, стопицот минут назад… а это было до того как я в 4:20 случайно обновил этот тред, погасив все непрочитанные, или после?..», а включить показ нормальных дат нельзя(
makise_homura
+1
, и ты такой «блин, стопицот минут назад… а это было до того как я в 4:20 случайно обновил этот тред, погасив все непрочитанные, или после?..»

Если с этим мнением будет согласно большинство Табунчан — то можно смело выпиливать эту «фичу».
Sasha-Flyer
+2
Ну, табун, к счастью, даёт возможность видеть в обоих форматах, так что тут не тот случай, о котором я говорю)
makise_homura
+1
Только вот второй формат буквально потребляет 9/10 процессорноно времени от общего времени во время рендера коммента на сервере. Подумаешь…
Sasha-Flyer
0
Ну потому его и надо оптимизировать (или выкинуть, если спрос на него нулевой)
makise_homura
0

как вам?)
(Я учел факт того, что комменты могут быть закешированы, а могут не)
Sasha-Flyer
+2
Феерично)
(эх, помню, как мы курисабу оптимизировали, чтобы она кушала треды, близкие к бамплимиту, за приемлемое время, а не десятки секунд))
makise_homura
0
Считаю справедливым/адекватным/корректным добавить рядом с тегом «говнокод» тег «LiveStreet».
Farxial
+2
Готово :)
Sasha-Flyer
+3
А прогу мне сделаешь раз прогать могёшь?
Andrelyx
0
что нужно сделать?
Sasha-Flyer
0
Давай я всё-таки займусь, если задача та же, которую обсуждали. Сейчас как раз небольшое окошко в заказах есть, могу выполнить.
NTFS Изменён автором
0
Так у меня никогда не будет никакой работы :/
Sasha-Flyer
0
Ну давай оба сделаем, а клиент выберет лучший вариант.
NTFS
0
не в этом дело. Я в месяц +-10к зарабатываю, занимаясь программированием каждый день. Мне реально нужны любые заказы, чтобы банально квартиру оплачивать.
Sasha-Flyer
0
Этот заказ я собирался делать бесплатно, в рамках своих акций «накодь понилюбам».

Я в месяц +-10к зарабатываю, занимаясь программированием каждый день.


Мне кажется, ты что-то делаешь не так. Даже на начальном этапе без опыта, это 30-40 тысяч за разработку, даже в провинции.
NTFS
+2
Если у тебя прямо сейчас есть предложение на 30к в месяц — я очень внимательно слушаю.
Sasha-Flyer Изменён автором
0
Это абсолютная типовая вакансия начинающего питониста в любой компании. Скорее всего, даже больше.
NTFS
+2
Ты мне готов предложить что-то или нет? Никакая компания с моими психическими расстройствами меня не возьмет.
Sasha-Flyer
0
А ты им не скажи.
Randy1974
+1
Никакая компания с моими психическими расстройствами меня не возьмет.

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

Просто отправляй резюме везде, где нужен питон, жди ответа. На 20-30 резюме обычно один отклик, на 4-5 откликов идет одно собеседование.
NTFS Изменён автором
+3
Ты со своей колокольни говоришь, будто как это просто и будто я кому-то могу быть нужен, но при этом сам не можешь ничего мне предложить по работе. Не видишь несостыковки?
Sasha-Flyer Изменён автором
0
будто я кому-то могу быть нужен
Может быть, это точка пересечения твоей сложности заводить знакомства и входить к работодателям? Если да, то эти вещи могут работать схожим образом, в т.ч. схожим образом обходиться.
Farxial
+1
будто как это просто

Я не говорил, что это просто, поиск работы тоже работа.

но при этом сам не можешь ничего мне предложить по работе

Сейчас мне не требуются сотрудники. Но я не являюсь единственным сферическим работодателем в космосе.
NTFS
0
Никому не требуются. Время сейчас такое.
Sasha-Flyer
0
Сколько резюме ты отправил за сентябрь? Если меньше 30 — то не засчитывается за попытку.
NTFS
+2
ну вот тебе сейчас пишу.
Sasha-Flyer
0
То есть, 1 штука. Причем без резюме, я ж даже не знаю, что ты умеешь, кроме питона.
NTFS
+2
как это просто
Но это и правда просто обычно.
сам не можешь ничего мне предложить по работе
Так он не работодатель.
makise_homura
+1
Так он не работодатель.

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

Ну так смысл поиска работы в том, чтобы убедить работодателя что именно ты ему и нужен.
partizan150
+2
Рабочая сила много кому нужна. Месяц назад, например, я с ног сбился, из-за исполнителя для проекта с джангой. И как я мог узнать, что ты что-то умеешь, и что ты ищешь работу?
Необходимо резюме, резюме необходимо вывесить на платформы и их рассылать.
NightPony
+2
Табун работодательный. Пожалуй, тоже в профиль надо добавить «беру деньги заказы»
NTFS
+2
Как же так? Мне говорили прогеры зарабатывают 300кк/сек?

Ну а если без сарказма — просто слишком много «индусов» со Скиллбокса пришло. На них можно задёшево вешать бэкэнд и фронтэнд простых одностраничников, пока сеньоры играют в игры и ждут, когда «индус» уронит прод, чего не произойдёт никогда.
H215
+2
Не совсем, гении с этих курсов забивают только джунские позиции, которых примерно 10% от всех. Люди на таких позициях редко способны выполнять сколько-то сложные задачи. С текущей ситуацией, мы имеем огромный голод в старших программистах и буквально сотни на место в младших
NightPony
+2
У меня обратные сведения, что за каждое место сейчас сражаются десяток разработчиков на мечах, дубинах и моргенштернах, до первой крови или до смерти, смотря какая вакансия.
NTFS
0
Круто, но ведь если я ищу работу, я как раз претендую на должность джуна (или того хуже, стажёра), так что для меня эта новость вообще адская, верно?

Например у меня в городе на hh по запросу разработчик ПО в должности стажёр 7 объявлений, из них 4 связаны с БД, 2 требуют 3 лет опыта, 1 требует React и вообще про фронтенд, другая про нод жс и бэк, третья разработчик мобилок. Ну короче все они именно в тех областях, в которых я шарю даже меньше чем в других.
Niko_de_Andjelo
+1
Джуном быть херово, да. Сам через это проходил. Приходится намного больше трудиться, чтобы выделиться. Однако, не всё так плохо: миллиард выпускников скиллбоксов в основном — крайне грустное зрелище, поэтому, если у тебя есть всякие петпроекты, или, например участие в хакатонах, то вполне можно выделиться и взять своё.
Но тяжело, признаю.
NightPony
+1
Я полагаю, что выпускники всех этих штук более востребованы, их же там наверное как раз популярным фреймворкам учат?

А я могу только на голом шарпе/паскале писать логику и с трудом копипастить что-нибудь самое простое для питона
Niko_de_Andjelo
0
мы имеем огромный голод в старших программистах и буквально сотни на место в младших
Кстати да, ППКС. Даже мидлов очень сильно не хватает на фоне джунов, не говоря уже о сеньорах.
makise_homura
0
Давай, да всё та же прога. Флаеру кинул с гитхаба другой проект который меня как и многих заинтересовал.
Andrelyx
+1
Отлично, займуся. Постараюсь на следующей неделе прототип подогнать.
PS: CRM я тоже занимаюсь, но там очевидно больше работы и не неделю неспешного кодирования. Как доделаю свой сайт, покажу что есть, может, чего еще заинтересует.
NTFS Изменён автором
0
Сидишь, читаешь, ничего не пноимаешь0))0
demogog
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.
Скрыто Показать