All streams
Search
Write a publication
Pull to refresh
51
0.2
Valentin Nechayev @netch80

Программист (backend/сети)

Send message
> На каком-нибудь K7 RSQRTPS вообще быстрее умножения.

Смешно — но показывает странное направление работы AMD.

> На современных процессорах на оптимизацию подобных инструкций «забили», потому что кто же считает подобные вещи на CPU? Для этого GPU есть…

Ну, похоже, не совсем. Например, после Nehalem повырезали много идиотизмов работы с денормализованными (там, где наследие K-C-S тратило тысячи тактов). Как минимум в SSE оба вендора не игнорируют плавучку. Но это происходит медленнее, чем хотелось бы…
> По сути: расскажите где в этом куске из трех строчек нужен volatile

Вы сузили контекст до конкретного куска кода. Этого не было в предыдущем обсуждении.

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

  int i = *(int*)&x;
  i = 0x5f3759df - (i >> 1);
  *(int*)&x = i;

(одна только последняя строчка поменялась)

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

И GCC начинает об этом предупреждать — мне он на неё дословно сказал

fsq3.cc:3:19: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
   int i = *(int*)&x;  // представим биты float в виде цел
                   ^
fsq3.cc:5:11: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
   *(int*)&x = i;
           ^


К вопросу о том, структуры это или нет, это не имеет ни малейшего отношения. Коллега Halt в соседних комментариях приводил пример (в видео), как это реально влияло на каком-то компиляторе — аналогом для данной функции было бы, что код свернулся до { return x; }

К этому, все Ваши реплики как
> он обязан следовать логике преобразования для каста как написано (а не как вы думаете).
> Тип int (как и float) не являются структурами…

относятся к совсем другим аспектам, но не к этому.

А вот volatile помогло бы именно за счёт того, что запись в *(int*)&x переписало бы независимо от того, что компилятор думает о том, как относятся x и i друг к другу — он бы просто обязан был держать обоих в памяти и не делать никаких предположений о том, что x не изменилось, раз он не видит данного изменения. И это даже для локальных переменных. В выходном коде видно, что они явно пишут в память (в стек) и тут же из неё читают. Именно об этом я говорил как о «паре лишних тактов».

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

Если хотите возражать — прошу это делать со ссылками на стандарты (на публичные final drafts, если иного источника нет). Любая отсылка вида «у меня всё работает» тут, увы, совершенно непригодна.
> Да ну? А вы про атомарные операции (конструкты, инструкции) что-нибудь слышали?

«Мы» слышали. В том числе то, что атомарные переменные вообще-то не объявляют volatile, у них подобная функциональность заложена в штатные средства доступа.

> Т.е. простейшие вещи (ака одна переменная типа int) вы правда мютексами защищаете?

Давайте Вы внимательно прочтёте, на что отвечаете. А то такие Ваши домыслы, мягко говоря, удивляют.

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

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

> А то что эту функцию (неявно для компилятора) исполняет прямо сейчас другой поток, это куда?

Какое отношение вообще имеет _исполнение_ функции в другом потоке? Или в Вашей обстановке у них при этом общие локальные данные?

> Я вам еще раз повторю volatile однопоточно не интересен

И я повторю, что он решает проблему со strict aliasing.

> Про остальное же я промолчу, да…

Настоятельно прошу вместо подобных «многозначительных» «умолчаний» вести обсуждение по сути.
> Т.е. конкретно тут — ни о чем…

Конкретно тут — о том, что компилятор обязан записать в память то, что пишут по указателю volatile, сразу, не задерживаясь; и прочитать по другому volatile указателю, не делая никаких предположений, что по этому указателю и почему.
Этого достаточно, чтобы убрать проблему strict aliasing в компиляторе.

> volatile нужен platform-agnostic, multi-threaded ONLY.

К тредовости оно вообще-то отношения в C/C++ не имеет от слова «никак», пока пользуетесь стандартными механизмами вроде mutex lock/unlock.
(Это отличается, кстати, от Java — где на volatile нагрузили синхронизацию доступа между тредами.)
Компилятор не имеет права кэшировать чтение или запись переменной даже без volatile через вызов любой неизвестной компилятору функции с побочным эффектом изменения чего-то в памяти. Операции с мьютексами декларируются со свойством такого изменения, даже если они инлайнятся.

> В худшем случае UB (да и UB на касте int/float мне очень сомнительно)…

Таки может :(
Мнэээ…

1. Таки нет, чисто формально, ибо RSQRTPS — команда SSE1, а это 1999, а не 2005 :)

2. Вопрос был про «корня и деления» против «суммирования и умножения». Эти фокусы позволяют, например, ускорять деление через обратный корень, но с потерей точности — вот Intel про это пишет в 248966:

> In microarchitectures that provide DIVPS/SQRTPS with high latency and low throughput, it is possible to speed up single-precision divide and square root calculations using the (V)RSQRTPS and (V)RCPPS instructions. For example, with 128-bit RCPPS/RSQRTPS at 5-cycle latency and 1-cycle throughput or with 256-bit implementation of these instructions at 7-cycle latency and 2-cycle throughput, a single Newton-Raphson iteration or Taylor approximation can achieve almost the same precision as the (V)DIVPS and (V)SQRTPS instructions.

но всё равно это сильно дороже умножения…

В этом плане лучше поставить на оптимизацию Goldschmidt method — AMD клянётся, что получает на делении до 16 циклов на single, 20 на double, 24 на extended — это заведомо быстрее типичного SRT. И наверняка там ещё можно что-то выжать. (А для квадратного корня ещё быстрее получается.)
Обратная сторона — всё в плавучке — прибавлять 10 тактов на конверсию в обе стороны для целых это уже сравнимо с интеловским SRT…
Да. Тут уже надо компромисс искать.
Подозреваю, именно для Z80-based сокращение таблицы было важнее, но точными данными не владею.
В ряде источников (например, тут) сказано, что обходить проблему strict aliasing можно надёжно через unionʼы — правда, сказано это для C, а не C++. У Вас утверждается, что union только условно безопасен. Кому верить и отчего это зависит?
Доступ к памяти всё равно пройдёт через кэш, потеря будет в пару тактов на операции — это малосущественно.

А ещё — для реализации под конкретный процессор можно вставить операцию местного ассемблера, и volatile не потребуется :)
Да, до сих пор тысячи людей её вспоминают совершенно ностальгически. Хотя для трезвого взгляда в её дизайне было таки огромное количество ляпов, не говоря уж про общую неэффективность по сравнению с «вылизанными» аналогами — ностальгию это не отменяет :)

Из современных приёмов — её следует считать если не зачинателем, то хотя бы очень эффективным популяризатором little endianness, NZVC логики флагов условий…
C современными возможностями по количеству вентилей вообще умножение строят на сетках и затем параллельных сумматорах… так что обычно не «эта» оптимизация. Хотя результат ещё лучше :)
Есть более известный вариант:

ab = (a+b)^2/4 - (a-b)^2/4


только два лукапа по таблице, для a+b и a-b; деление на 4 выполняется нацело, дробные взаимно компенсируются.
> По-моему, на БК-0010 были еще и какие-то проблемы с ассемблером, так как один мой знакомый программировал на БК-0010 просто «в кодах».

«Проблема» в том, что на PDP-11 всех разновидностей и клонов было настолько легко писать всё прямо в кодах, что ассемблер часто не использовали :)
> cat /dev/urandom > /dev/null

Это загрузит только одно ядро.
Ещё сейчас тему асинхронной логики копает (и старается понятно объяснять) Сергей Вакуленко.
> Возможно, у нас больше ошибок, чем у разработчиков ПО, пишущих подробные юнит-тесты, но по крайней мере, мы исправляем их быстро.

Как-то не очень убедительно звучит. :)

> Весь код и комментарии пишутся на британском английском.

Боюсь, на наши условия это не переносимо.
> Тесты обычно решают прямую задачу «найти ошибки».

Это только кратковременная задача для одноразовых тестов. Долговременная же задача — обеспечить условия для проверки по сути (вычитка, верификация).
В это может входить что угодно вплоть до проверки базовых свойств среды. У меня в одном проекте есть в тестах assert(sizeof(long)) == 8 — вот решили, что лучше завязаться и обложить проверками.
Но чаще ими покрываются крайние случаи.

Да, я видел и помню Ваши предыдущие реплики по поводу, например, невыгодности написания тестов авторами кода. У меня другая специфика, и >90% тестирования идёт таки авторами. Поэтому тут идеально общей позиции не будет.

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

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

> Основная беда TDD — тестирование по модели белого ящика.
Не тестирование. А написание кода под тесты.

> Если девелопер или тест забывают какую-то сложную ситуацию с шансом 10%, то шансы, что забудут оба — всего лишь 1%. Таким образом, отказ от TDD в пользу независимых тестов в 10 раз улучшает работу программы в сложных ситуациях.

Это аргументы за независимое тестирование — они сильнее, чем просто против TDD.
> вы не используете главное преимущество этого редактора

И что с того?

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

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

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

Верно. Но заметьте, что среди тех, кто тут хвалил vim, на Ваши неоднократные «вопияния в пустыне» что-то отклика чуть менее, чем никак. Видимо, таки мало кого реально волнует это «главное преимущество». Можете подумать на досуге :) только лучше без глобальных выводов про человечество :)
> Логичный вывод напрашивается сам по себе: если человеку не нужен десятипальцевый метод набора, то ему не нужен и vim.

А вот это уже ничуть не логично. Если из A следует B, то из не-A не следует не-B.

> vim ему не подойдёт, это будет просто мучение

Я набираю 4-6 пальцами, в зависимости от фазы Луны, но при этом в основном использую vim. И никаких мучений. Достаточно для контпримера?

> редактирование вслепую не отрывая рук, при помощи моторики, своего рода «играя аккорды».

Из использования «аккордов» ничуть не следует необходимость редактирования вслепую.
Во-первых — наоборот, тесты без вычитки не гарантируют куда больше. Формальное удовлетворение тестов превращает реализацию просто в набор готовых ответов на условия тестов, и ничего больше.
Во-вторых, если о _формальной_ корректности — то вычитка и верификация как раз дают её, и остаётся вопрос в качестве самой формальности. В ней могут лежать (и очень часто лежат) неверные представления о среде исполнения. Тесты приближают _фактическую_ корректность, а не формальную.
Вот +100. Хотя с поправкой (мне совершенно безразличной, но формально актуальной) — это относится к тестированию в целом, а не к TDD.
Вычитка (слово понравилось, надо зафиксировать) и тесты ортогональны, тесты ловят проблемы, которые не замечаются человеком из-за специфики мышления (то же «замыливание» только первый эффект из многих) и проблемы соответствия внешней среды ожидаемому, а вычитка — что реализация в принципе делает то, что ожидает человек. Тесты без вычитки превращаются в формальность, не требующую работы кода за пределами тестов, а вычитка без тестов — в сферическую работу в вакууме. При хорошем качестве разработчиков (как Вы уверждаете про своих) глупых ошибок и мифов — очень мало, остаются ошибки и несоответствия внешней среды (тот пример с USB за 3 часа — очень показателен).

Information

Rating
2,648-th
Location
Киев, Киевская обл., Украина
Date of birth
Registered
Activity