LINUX.ORG.RU

char это и не signed и не unsigned а непонятно что

 


3

6
> cat main.cpp
#include <type_traits>

int main() {
        static_assert((::std::is_same<char, signed char>::value) == true);
        static_assert((::std::is_same<char, unsigned char>::value) == true);

        return 0;
}

> g++ -Wall -Wextra main.cpp
main.cpp: In function 'int main()':
main.cpp:4:2: error: static assertion failed
  static_assert((::std::is_same<char, signed char>::value) == true);
  ^~~~~~~~~~~~~
main.cpp:5:2: error: static assertion failed
  static_assert((::std::is_same<char, unsigned char>::value) == true);
  ^~~~~~~~~~~~~
★★★★
Ответ на: комментарий от khrundel

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

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

То-то и оно. И раз уж мы выяснили, что проекты как правило пишутся под конкретные системы. То фактически каждый раз программист выбирает тип на основе того, сколько бит ему нужно. Он знает, сколько в данном случае бит скажем у int или char. На системе, где данные типы имеют другую битность (в смысле количество бит в типе) будет выбран при прочих равных уже другой тип. Т.е. я к тому, что даже сейчас, даже использую типы char/int/long и т.п. мы все равно выбираем типы на основе битности, просто в каждом случае мы смотрим, сколько бит у типа в целевой системе/компиляторе. Так и что плохого в том, чтобы у типов было жестко указано количество бит? Что в целом и сделали в C++ сделав надстройку над фундаментальными типами в виде int8_t, int16_t и т.п.

rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от Iron_Bug

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

И это тоже. Но мы не про такие языки.

человек, который пишет код для работы с какой-то архитектурой, не «надеется, что влезет», он точно знает, сколько у него бит на шине

Как раз ровно наоборот.

Если ты пишешь for(int32_t i = 0; i < 100; i++){... i*1000...} тогда ты знаешь сколько у тебя бит, какие значения может принимать i и то что его можно безопасно умножать на 1000. Если ты пишешь int то ты именно что надеешься на то что его хватит для представления 99000. Потом твой код может оказаться на другом компиляторе под другую архитектуру, его могут тупо копипастнуть в чужой проект, просмотрев снаружи и не заметив криминала. Т.е. даже под определение «переносимый ассемблер» в данном случае C не очень-то подходит. Если уж так заботиться об удобстве компилятора, надо было вводить типы типа int_at_least32_t, с UB в случае переполнения 32 бит.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

надо было вводить типы типа int_at_least32_t, с UB в случае переполнения 32 бит.

long уже есть.

d_a ★★★★★
()
Ответ на: комментарий от khrundel

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

если ты хочешь переносить код на другую платформу - надо его весь просматривать на предмет подобных вещей. если ты хочешь писать код под кроссплатформу - это вообще отдельная тема, для этого есть typedef и define и там организуют типы под разные платформы, и платформозависимый код выносят в отдельные файлы, которые собираются в зависимости от архитектуры. это всё делается осознанно.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от khrundel

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

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

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

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

А почему это МОЖЕТ оказаться? Мы ведь уже пришли к соглашению, что проекты пишут под определенные платформы

Я больше скажу - под конкретные семейства чипов (Intel vs AMD), и под конкретные компиляторы / системное окружение. Можно, конечно, создавать условного сферического коня в вакууме который «работает всегда и везде», но цена (особенно в плане TTM) будет зашкаливать. А так: unit тесты прошли (спешу заострить внимание на том что всё покрыть невозможно) - есть определенная уверенность (надежда?) что ничего не взорвётся, и можно выкатывать. А вот когда возникнет необходимость спортироваться куда то ещё - тогда и будем думать «как» и «чего это будет стоить». Мои 2 копейки.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от rumgot

стандарт Си определяет логику над пространством адресов и некоторые операции, производимые ALU. он более абстрактен, чем ассемблеры, и поэтому более удобен. но нет смысла прибивать его гвоздями к какой-то архитектуре. стандарт определяет общие свойства, а не частные реализации.

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от rumgot

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от rumgot

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Во первых, я обсуждаю плюсы. Во вторых ты путаешься в последовательности обсуждения.

Есть фундаментальные типы определенные в стандарте так: минимум столько-то бит и не более чем тип такой то.

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

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

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

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

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

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

Прошу прощения, что ставлю под сомнение идиллию вашего консенсуса, но может объясните мне, а как проекты типа:

  • библиотек, вроде Qt или ffmpeg?

  • СУБД, вроде MySQL, SQLite, BerkeleyDB и пр.?

  • десктоп-приложения, которые развиваются в течении 30-35 лет, зародившись на 16-битовых 8086, а сейчас работающие на 64-битных x86 и ARM-ах?

Они что, каждый раз пишутся под определенные архитектуры?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Они что, каждый раз пишутся под определенные архитектуры?

Думаю да. Вот на примере Qt: https://doc.qt.io/qt-5/supported-platforms.html

Каждый проект, лучше даже сказать конкретная версия проекта, разве не идет под что-то определенное (не обязательно под одну платформу, но под некоторое количество платформ, вон как Qt)? И даже почитав описание сборки на том же CMake можно увидеть условия для конкретных платформ.

rumgot ★★★★★
()
Ответ на: комментарий от Iron_Bug

Нет.

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

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

khrundel ★★★★
()
Ответ на: комментарий от Iron_Bug

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

Я вот наоборот, не понимаю, зачем нужны платформозависимые дефолтные типы. Вот нахрена мне, как программисту, нужен инт, который или 16 или 32 бита? А может все 64? Какая вообще польза? Я знаю, что существует много платформ в которых невыравненный доступ происходит медленнее или даже вообще невозможен, но при чём тут размер инта? Как располагать переменные в стеке или в структуре по умолчанию решает компилятор, так что там где не нужна упаковка он понаставит достаточно отступов. Извращаться с битовыми операциями при чтении из массива байтов придётся только в операциях доступа к элементам массива. Но это копейки. Не копейками это станет только если делать массовые операции, но, как мне кажется, вдолбить начинающим программистам на C идею «всегда используй memcpy/memset, если нужно обработать интервал внутри массива» было бы ненамного сложнее чем вдолбить «sizeof(short) <= sizeof(int) <= sizeof(long)».

Таким образом единственной проблемой осталась бы обработка больших объёмов массивов, для которых непринципиален реальный размер инта, непринципиальна упакованность, memset/memcpy не подходит, зато принципиальна экономия нескольких циклов на доступе. Ну такие случаи как мне кажется, логичнее считать исключительными и тогда уже пердолиться с платформозависимыми тайпдефами или просто сэмулировать выравнивание через замену массива интов массивом структур содержащих 1 поле инт.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Я вот наоборот, не понимаю, зачем нужны платформозависимые дефолтные типы. Вот нахрена мне, как программисту, нужен инт, который или 16 или 32 бита? А может все 64? Какая вообще польза?

Вы серьёзно? Это самый быстрый integer для платформы. Чтобы далеко не ходить - посмотрите ради интереса во что for-лупчики разворачиваются с int64_t, особенно в -m32.

bugfixer ★★★★★
()
Ответ на: комментарий от Iron_Bug

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

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

Это настолько большая тема, что я даже не знаю, с чего начать, и не особо хочу разжевывать такие базовые вещи, извините... Этот разговор потянет добрую половину Computer Science, лучше просто почитать какую-нибудь книжку.

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

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

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

Crocodoom ★★★★★
()
Последнее исправление: Crocodoom (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Зачем мне int64_t в качестве индекса цикла если мне не нужно 64 бита? А если мне необходимы именно 64 бита, нафига мне «быстрый инт», который эти 64 бита не вместит, но зато будет быстрым?

Я понимаю ход мыслей авторов этой идеи, «пусть инт будет удобного для железа размера, у нас тут значения небольшие, авось поместятся», я просто считаю это решение бессмысленным. Если ты уверен что твоему коду хватит и 16 битного инта, почему бы не использовать int16?

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Если ты уверен что твоему коду хватит и 16 битного инта, почему бы не использовать int16?

Ну при конпеляции там же всё равно 32/64 битные регистры будут в конечном итоге. В си то как раз это имеет большое значение

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Ну, так как мы обсуждаем гипотетический си с нормальными типами, это будет либо UB при переполнении, т.е. 32767 + 1 может не гарантироваться равенство с -1, хоть тип и называется 16битным. Тогда компилятору вообще ничего не нужно делать, просто использовал бы родные 32битные регистры. Либо он гарантировал бы правильное переполнение, тогда в некоторых местах пришлось бы добавить битовое И.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Если ты уверен что твоему коду хватит и 16 битного инта, почему бы не использовать int16?

Потому что его может вовсе не быть на данной архитектуре? Для примера

char, signed char    16 bits
pointers (data)
  small memory mode  16 bits
  large memory mode  23 bits
pointers (function)  24 bits
long long            40 bits

Note: C55x Byte is 16 Bits
By ISO C definition, the size of operator yields the number of bytes required
to store an object. ISO further stipulates that when sizeof is applied to char,
the result is 1. Since the C55x char is 16 bits (to make it separately addressable), a byte is also 16 bits. This yields results you may not expect; for example, sizeof (int) == 1 (not 2). C55x bytes and words are equivalent (16 bits).

Оно просто не умеет читать меньше машинного слова. Правда кривой long long, не осилила эта железка минимальных требования по размеру. Говорят, что есть какие-то DSP с byte == 32 бита.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от Crocodoom

Во-первых, непринципиально. Считаешь что int16 не нужен - используй int32. Во-вторых, как не нужен? Во времена доса/win16 был нужен как тот самый быстрый целочисленный тип. Сейчас тоже нужен, simd же и автоматическая векторизация циклов. Если платформа умеет бить SIMD-регистры на 16битные, то вот этот int16 увеличит пропускную способность цикла вдвое. В-третьих, как я уже писал, UB на переполнении или достаточно умный компилятор - всё это решит проблему неоптимальной кодогенерации. В любом случае, это меньшая проблема чем ломающийся при смене компилятора корректный код, и проявляться такие проблемы будут в одних и тех же случаях, когда код компилируется под платформу с размером регистра, отличным от того, который имел в виду программист.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Во-первых, непринципиально. Считаешь что int16 не нужен - используй int32.

Принципиально. int32 тоже не нужен, нужен в первую очередь int, о чём вам и пытаются втолковать

Если платформа умеет бить SIMD-регистры на 16битные, то вот этот int16 увеличит пропускную способность цикла вдвое.

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

Во времена доса/win16 был нужен как тот самый быстрый целочисленный тип.

Правильно, и он тоже может и должен быть скрыт за алиасом int

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

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

В любом случае, это меньшая проблема чем ломающийся при смене компилятора корректный код

Так не пишите код, который ломается при смене компилятора

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

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

khrundel ★★★★
()
Ответ на: комментарий от khrundel

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

Симметрично

Crocodoom ★★★★★
()
Последнее исправление: Crocodoom (всего исправлений: 1)
Ответ на: комментарий от Crocodoom

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

Это немного меняет взгляд на мир и позволяет проще относиться к собственным тезисам

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Так ты и есть дебил. Вот берём твой «убийственный» аргумент с 32битным алу и умножением на нем int16. Как же быть? Просто читаешь доку по языку, если там написано, что переполнение UB - просто загружаешь числа в 32битные регистры и умножаешь. 5*5=25 независимо от того байты это или qword. Если результат получился не тот, значит код некорректный.

И самая трагедия, что я написал тебе это раза 2, тебе всё как о стенку горох, ни возразить что-то, ни принять, тупо повторяешь одну и ту же мантру.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

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

Crocodoom ★★★★★
()
Ответ на: комментарий от khrundel

Если ты уверен что твоему коду хватит и 16 битного инта, почему бы не использовать int16?

Использование shorts полезно только если оно позволяет сэкономить на memory bandwidth. Ну, если совсем на пальцах - пробежаться по int16_t[1000000] будет ровно в 2 раза быстрее чем по int32_t[1000000]. Использование размеров отличных от register size для temporaries - преступление за которое в приличных домах больно бьют по рукам (и не только). Погуглите «partial register stall». Не благодарите.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Ещё один безграмотный «учитель» вылез.

Вопрос как будут использоваться регистры - это вообще не твоя забота. Ты пишешь на языке высокого уровня. Кодогенерацией занимаешься не ты. Эти самые partial register stalls - это просто частный случай проблем конкретной архитектуры с которыми должен разбираться компилятор. Ситуация «операции с половинками регистров выполняются медленно» ничем не хуже ситуации «операции с половинками регистров вообще не поддерживаются». Однако и это легко решается компилятором. Вообще даю подсказку: операции над 16битными числами необязательно делать в 16битном регистре.

Кроме того я привел пример, автоматическая векторизация для SIMD-архитектуры. Почему-то тот факт, что огульное использование 32 бит снизит скорость такой обработки в 2 раза вообще никого не беспокоит, а то что криво написанный компилятор под x86 может нагенерить кода с partial register stall вдруг стало проблемой. Проблемой, за которую отвечает не автор кривого компилятора, а автор прикладного кода.

Что характерно: языки, спроектированные в последнюю пару десятков лет все как-то не решились повторять этот фокус. И не то что бы авторы языков никогда не слышали о C. Не то что бы не существовали миллионы программистов, привыкших писать int и ожидающих непонятое с размерностью. Максимум - сделают алиас int для удобства, на конкретный целочисленный тип, чтоб каждый программист знал, что int - это 32 бита. При этом всякие шорты/лонги - не, вот вам ребята типы с прописанной явно битностью. А если язык не пытается мимикрировать под C, тот же Rust например, так из него инт специально выкинут, типа бросайте эту вредную привычку. Всем очевидно, что копеечная экономия на сложности кодогенератора не стоит ошибок связанных с переполнением. Однако паре фанатиков C лорчика конечно виднее.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

А если достаточно счета до 2^10, что мешает сделать спец MCU с такой ширирой машинного слова (и ригстров)? Должно стать неудобно перед уважаемым Rust’ом, которые жестко вшил размеры? Проще схема, энергоэффективней, дешевле. Можно выбрать любую сетку - 12, 24, 48, 96 бит, например. Типы вообще не обязаны быть кратны 8 битам, ничто не помешат спец железкам забить на ожидания свидителей 8 битных байтов.

привыкших писать int и ожидающих непонятое с размерностью

Нет там непонятного, тебе гарантируют минимум.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от khrundel

Вопрос как будут использоваться регистры - это вообще не твоя забота.

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

Ты пишешь на языке высокого уровня. Кодогенерацией занимаешься не ты. Эти самые partial register stalls - это просто частный случай проблем конкретной архитектуры

Вы реально так думаете?

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

И знаете что он думает - «наверное этот дурачок всё таки имел в виду int, и вроде как ничего не сломается если подменить int16 им, а ну ка…».

Кроме того я привел пример, автоматическая векторизация для SIMD-архитектуры.

Щаз. Когда вы последний раз на disasm смотрели? Оставьте уже эти свои влажные мечты.

bugfixer ★★★★★
()
Ответ на: комментарий от Crocodoom

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

А разве если твой умножитель может принять на умножение 32-битные числа (то есть умножитель 64-битный и результат помещается), то в чем проблема ему умножить 16-битные числа? Аппаратура же может просто сделать zero-extend до 32-ух бит. Я как-то занимался обратной задачей, когда блок умножения меньшей битности, чем результат умножения, на riscv на verilog: у меня на плисе был аппаратный dsp, который умеет выдавать 45-битный результат умножения, что при умножении 32-ух битных чисел порождало неэффективную топологию и я вот как раз разделял числа по 16 бит и умножал уже 16x16 бит.

snake266 ★★★
()
Последнее исправление: snake266 (всего исправлений: 1)
Ответ на: комментарий от snake266

А разве если твой умножитель может принять на умножение 32-битные числа (то есть умножитель 64-битный и результат помещается), то в чем проблема ему умножить 16-битные числа? Аппаратура же может просто сделать zero-extend до 32-ух бит.

Проблема в падении перфа. Если есть умножитель, который умеет умножать два 32-битных числа, то не надо в него умножения 16-битных пихать. Обе операции займут один тик [1], но умножать нативную разрядность эффективнее. Внезапно, когда пишешь на сях, надо думать об эффективности, о боже мой!

[1] Фиксированное число тиков, вообще говоря, но для изложения несущественно

Crocodoom ★★★★★
()
Последнее исправление: Crocodoom (всего исправлений: 1)
Ответ на: комментарий от kvpfs

Мы уже сейчас живём в мире распространившихся векторных CPU. И x86, и ARM имеют векторные расширения, я уж не говорю о всяких специализированных. При этом никого не беспокоит, что стандартный C не поддерживает эту векторность вообще никак, fp-арифметику делают используя части SSE регистров, оптимизирующие компиляторы сами изгаляются пытаясь развернуть циклы в векторный код. Но всё чем обеспокоены эксперты с лорчика - потерей 2х бит при компиляции int8_t на архитектуру с 10 битами или генерацию 4 сложений вместо 1 для 32битного инта.

Нет там непонятного, тебе гарантируют минимум.

Какой минимум? Вот в Борланд C, компиляторе на котором учились современные бородатые сионисты, это было 16 бит. Это уже минимум, или допустимы int и меньшего размера?

Представь, что вместо идиотского набора short, int, long в C добавили бы in8, in16, in32, in64. Куча программистов привыкли бы писать in16 ещё со времени DOS. У них бы изначально голова так работала, типа если для данной задачи очевидно что хватает 16 бит то и нечего требовать больше. Ты бы копировал этот код со стековерфлоу и компилировал бы его не глядя, а компилятор эмулировал бы in16 2мя 10битными словами. А если бы ты захотел вот этот кусок оптимизировать, ты бы просмотрел, убедился бы что ничего не ломается и разрядности хватает, и руками бы в нем заменил на in10, которого в стандарте языка нет, зато компилятор твоей платформы знает. В обратную сторону аналогично, взял бы кто твою программу, офигел от странных типов, но парой тайпдефов смог бы заставить этот код работать.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Но всё чем обеспокоены эксперты с лорчика - потерей 2х бит при компиляции int8_t на архитектуру с 10 битами или генерацию 4 сложений вместо 1 для 32битного инта.

Да, именно так

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

Потому что нечего молодёжь дурному учить. Пишите программы эффективно, да благословит вас Дональд Эрвин Кнут. Аминь.

Crocodoom ★★★★★
()
Ответ на: комментарий от khrundel

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

А где тут проблема? Зачем мне делать это самому? Но если очень хочется, то развернуть цикл самостоятельно - не проблема (во всяком случае в крестах).

Какой минимум? Вот в Борланд C, компиляторе на котором учились современные бородатые сионисты, это было 16 бит. Это уже минимум, или допустимы int и меньшего размера?

int - at least 16 bits.

в C добавили бы in8, in16, in32, in64.

открой для себя <cstdint>

а компилятор эмулировал бы in16 2мя 10битными словами

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

kvpfs ★★
()
Ответ на: комментарий от Crocodoom

Чувак, ты этот бред написал уже раза 3. И за всё это время так и не пояснил, почему это умножать нативную ширину эффективнее. Нет, я не спорю, если ты пишешь либу для криптографии и делаешь длинные операции, то круче использовать ALU на полную ширину, а не эмулировать 2048битное умножение через int8_t на 32битном процессоре.

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

Я вот погуглил насчёт латентности команды imul на x86 процессорах. Разницы нет. Ну т.е. есть, относительно старые процессоры типа Core 2 умножают 32 бита чуть-чуть быстрее чем 64. Но всё меняется если посмотреть на idiv. Даже на самых современных процессорах деление 8битных чисел быстрее чем 16 битных, а 16битных быстрее чем 32. Так что ты попал пальцем в небо, операции с меньшим чем родная размерность по меньшей мере не медленнее, а скорее всего и быстрее.

Почему же для перемножения небольших чисел используют 32битные Инты? 1) потому что не хотят париться определением правильного типа и 2) потому что int писать удобнее. Производительность тут вообще никаким боком

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Не удержался.

Всем очевидно, что копеечная экономия на сложности кодогенератора

Я так понимаю - кто-то с десяток компиляторов написал и знает о чём говорит.

не стоит ошибок

Что вы знаете о стоимости ошибок?

Однако паре фанатиков C лорчика конечно виднее.

Я бы ярлыки не вешал. Но вам тут многие пытались мозги вправить. Походу - бесполезно.

bugfixer ★★★★★
()
Ответ на: комментарий от Crocodoom

И благодаря нам, экспертам с лорчика, чайники с лорчика (такие как ты) получают по башке за свой говнокод

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

khrundel ★★★★
()
Ответ на: комментарий от kvpfs

А где тут проблема? Зачем мне делать это самому?

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

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

int - at least 16 bits.

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

И ты говорил про векторные вычисления … Ты представляешь какой это костыль? Это люто тормозная какашка

Представляю. Если чё, я даже лабораторку про умножение методом Тоома-Кука когда-то писал, а уж тупое умножение в столбик 2-4значных чисел меня вот ни капли не пугает. И тем более не пугает, если я знаю, что это всё напишут авторы компилятора.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

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

Оно может и надо, но не в стандарте (хотя пытаются что-то протащить), слишком изменчиво, новые наборы, разные размеры регистров. Оно спокойно может существовать в виде сторонних либ, например вот https://github.com/root-project/veccore, с ходу загуглил (не ковырял), в общем это надо как-то так решать, а не втаскиванием в стандарт.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от khrundel

Чувак, ты этот бред написал уже раза 3. И за всё это время так и не пояснил, почему это умножать нативную ширину эффективнее. Нет, я не спорю, если ты пишешь либу для криптографии и делаешь длинные операции, то круче использовать ALU на полную ширину, а не эмулировать 2048битное умножение через int8_t на 32битном процессоре.

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

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

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

Здесь ты, по-моему, даже в русском языке запутался. Не увеличит, а уменьшит. И не производительность, а эффективность (читай внимательнее, пожалуйста).

Эффективность здесь определяется через утилизацию процессорных блоков. Для начала, давай заменим умножение на сложение, умножение — это слишком сложно. Также для простоты возьмём типы с квалификатором unsigned, про UB поговорим в другой раз, чтобы не раздувать скоуп дискуссии. Операция uint32+uint32=uint32 не содержит UB при любом значении операндов.

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

  • Далее, есть некий говнокод от хрюнделя, который использует сложение uint16. Примем, что компилятор сделает zero-extension и твой код будет использовать этот 32-битный блок сложения.
  • А также есть чуть-менее-говнокод от чуть-более-умного-и-сбавившего-гонор хрюнделя, который использует сложение uint32 уже на уровне языка

Сравним эффективность этих кодов. Первый использует лишь половину ширины регистров обоих операндов. Второй полностью. Это означает, что у более глупого хрюнделя его процессор гоняет 50% мусорных данных (нули складываются с нулями). А более умный хрюндель использует свой процессор на 100% и радуется жизни. Разница в эффективности ровно в 2 раза. За то же самое время, что первый хрюндель обрабатывает 1 гигабайт данных, второй успевает обработать 2 гигабайта. Разницу видишь?..

Какой же отсюда следует вывод? 16-битные инты можно использовать на 32-битных архитектурах, но это будет неэффективно. Лучше переписать алгоритм, чтобы он не закладывался жёстко на 16-битное сложение. Как это конкретно сделать — ну, зависит от задачи, общего рецепта я не дам.

Я вот погуглил насчёт латентности команды imul на x86 процессорах. Разницы нет. Ну т.е. есть, относительно старые процессоры типа Core 2 умножают 32 бита чуть-чуть быстрее чем 64. Но всё меняется если посмотреть на idiv. Даже на самых современных процессорах деление 8битных чисел быстрее чем 16 битных, а 16битных быстрее чем 32. Так что ты попал пальцем в небо, операции с меньшим чем родная размерность по меньшей мере не медленнее, а скорее всего и быстрее.

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

P.S. Если ты ответишь на этот коммент культурно, то я даже могу попробовать в 5-ый раз. Вчера ты как-то совсем жёстко быдлил, сегодня вроде пытаешься разобраться. Не обижайся, обнял.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

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

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

N-разрядные сумматоры и умножители всегда оперируют данными разрядности N. Они по-другому не умеют в принципе. Они так устроены. Если данные имеют меньшую разрядность, то их сначала надо (знако)расширить. (Как иначе можно состыковать аппаратный блок M (multiplier), у которого входы разрядности N, с аппаратными блоками R (register) разрядности N/2? Хотя на самом деле блок R тоже разрядности N, откуда берётся N/2, непонятно.)

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

Доступно?

akk ★★★★★
()
Ответ на: комментарий от akk

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

Я рад, что смог вам доставить это удовольствие

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

Доступно?

Ага. Только если значащих бит N1 < N, то эффективность утилизации сумматора падает. До величины N1/N

Доступно?

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Операция uint32+uint32=uint32 не содержит UB при любом значении операндов.

Вот опять начали хорошо, а закончили «нули складываются с нулями». Тут же дело в другом. Для uint32 переполнение обработается аппаратно, а для uint16 скомпилированному коду надо программно проверять, было ли переполнение, а если было, то корректировать результат.

С другой стороны, khrundel же говорит о предсказуемом поведении на разных платформах. Что будет, если мы перенесём этот код на какой-нибудь 8-битный микропроцессор? Нормально там с int-ом везде работать?

Kogrom
()
Ответ на: комментарий от Kogrom

закончили «нули складываются с нулями»

Нули с нулями хочет складывать как раз хрюндель, а «3 идиота» (myself, bugfixer, kvpfs) которыми он так любезно назвал своих оппонентов, пытаются его отговорить.

Для uint32 переполнение обработается аппаратно, а для uint16 скомпилированному коду надо программно проверять, было ли переполнение, а если было, то корректировать результат.

Не надо ничего программно проверять. Тип uint16 вообще не надо использовать на 32-битной платформе в «горячем» коде, это есть мой главный месседж в этой дискуссии.

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

С другой стороны, khrundel же говорит о предсказуемом поведении на разных платформах. Что будет, если мы перенесём этот код на какой-нибудь 8-битный микропроцессор? Нормально там с int-ом везде работать?

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

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Нули с нулями хочет складывать как раз хрюндель

Так и при uint32 те же нули с нулями будут складываться. В чём разница?

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

Я выше не видел условия про «горячий» код. И спор изначально был не uint16 vs uint32, а uint с конкретной разрядностью vs uint с неопределённой разрядностью.

Kogrom
()
Ответ на: комментарий от Kogrom

для uint16 скомпилированному коду надо программно проверять, было ли переполнение

Тут я похоже ошибся. Проверять не надо - просто обнулять два байта.

Kogrom
()
Ответ на: комментарий от Kogrom

И спор изначально был не uint16 vs uint32, а uint с конкретной разрядностью vs uint с неопределённой разрядностью.

Так и есть. До сих пор не удалось донести до товарища «Хрюнделя» что применимость фиксированной разрядности довольно узка. В подавляющем числе случаев нужен просто int, и это именно то что будет работать наиболее эффективно.

bugfixer ★★★★★
()
Ответ на: комментарий от Kogrom

Я выше не видел условия про «горячий» код.

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

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

И спор изначально был не uint16 vs uint32, а uint с конкретной разрядностью vs uint с неопределённой разрядностью.

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

Но всё чем обеспокоены эксперты с лорчика - потерей 2х бит при компиляции int8_t на архитектуру с 10 битами или генерацию 4 сложений вместо 1 для 32битного инта.

Crocodoom ★★★★★
()
Последнее исправление: Crocodoom (всего исправлений: 1)
Ответ на: комментарий от Kogrom

Так и при uint32 те же нули с нулями будут складываться. В чём разница?

Какие «те же»? Надо писать код так, чтобы нули с нулями вообще не складывались! Зачем процессор то зря насиловать?

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Писать холодный код на си особого смысла не имеет

Сильное утверждение. Или у нас терминология существенно различается.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Писать холодный код на си особого смысла не имеет

Сильное утверждение. Или у нас терминология существенно различается.

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

Холодным я называю код, оптимизации в котором существенно не влияют на overall performance. Личный опыт состоит в том, что такой код можно писать на питоне, ну и потом приклеить сишную часть через FFI. Я не говорю, что это возможно сделать во всех случаях... Но связка (ЯП высокого уровня) + (тяжелые части на сях) довольно популярна, и не просто так.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

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

Хех. Мы доходим до того что гоняем «холостые» events чтобы держать CPU cache hot…

Личный опыт состоит в том, что такой код можно писать на питоне

Подозреваю что речь идёт о числодробилках (batch kind of jobs).

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Подозреваю что речь идёт о числодробилках (batch kind of jobs)

Ага. Но, imho, даже в остальных случаях на си per se писать не нужно, если доступен си++. То есть, самые горячие места пишем в сишном стиле (потому что он близок к железу), которые обвязываем уже высокоуровневыми примочками языка си++ (классы, шаблоны, итераторы, you name it).

То есть предлагаю такую схему. Если заранее понятно, что FFI неплохо налезет, то его и брать. Если ожидается, что сам интероп будет занимать существенное время, то берём си++ и пишем на нём, опускаясь до сишного байто*бства там, где требуется. Если си++ никак не берётся, то вздыхаем и пишем всю программу на си... Короче, для меня это fallback option.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

на си per se писать не нужно, если доступен си++.

В этом контексте - я разницы не делаю. Собственно на голом “C” я не писал лет N-дцать как.

Если заранее понятно

Знал бы прикуп - жил бы в Сочи, что называется. Мы немножко в разных мирах существуем: я за latency гоняюсь, и поверьте - очень далеко готов зайти чтобы порвать конкурентов.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

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

Рыбак рыбака узнает издалека..

Мы немножко в разных мирах существуем: я за latency гоняюсь

HFT, если я правильно понимаю? Для меня это действительно незнакомый мир. В некотором[1] смысле даже противоположный моему миру HPC...
Тем забавнее, что из обоих этих миров домов товарищу хрюнделю идёт один и тот же месседж:

Использование размеров отличных от register size для temporaries - преступление за которое в приличных домах больно бьют по рукам

Надеюсь, он всё-таки сможет его услышать!

[1] У нас оптимизируют интегральное время расчёта, а у вас отзывчивость системы, что есть дифференциальная характеристика.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Рыбак рыбака узнает издалека..

А то ж :) По моей информации Вы же с ФУПМ? 13-я группа здесь. Предполагалось что я чипы дизайнить буду, но получилось то что получилось. Но тем не менее - понимание как оно устроено очень помогает в написании софта. Осталось только разобраться «как устроен этот мир» in general, но это в процессе ;)

HFT, если я правильно понимаю?

Есть чутка ;)

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от Crocodoom

То, что ты назвал «для криптографической либы», я бы назвал адекватным подходом к разработке софта.

Нет, дурак, ты просто не знаешь как делается большая арифметика. Но тебе и не нужно, главное понимать, что если тебе надо перемножать 2048 бит, то лучше это делать разбивая на куски по 16 чем на куски по 8. Рост производительности тут идёт от задачи, ей надо эти 2048 бит перемножать. Сложность умножения в столбик - N в квадрате, где N - число кусков на которые ты большое число поделил. Более хитрые алгоритмы дают вроде бы N*log(N), хотя тут могу соврать. В любом случае, конкретно в криптографии использование в 2 раза более широкой операции уменьшит время выполнения в 2 с лишним раза. Тут смысл действительно есть.

В прикладном коде тебе надо перемножать 52*78 и если ты, зная что числа ограничены 100 и их произведение, соответственно, 10 тысячами, заявишь компилятору, что тебе нужно 2 ярда, то никакой эффективности ты не получишь.

Эффективность здесь определяется через утилизацию процессорных блоков

Я вижу ты уже понял что обосрался и решил прикинуться дурачком-эстетом, для которого важно «загрузить блок полностью». Однако ранее ты пел иное, цитирую: «Проблема в падении перфа. Если есть умножитель, который умеет умножать два 32-битных числа, то не надо в него умножения 16-битных пихать.»

Что случилось?

Про загрузку всех блоков тебе уже объяснили. Да, внезапно, возможны 2 случая. В одном, самом простом и самом тупом, 16битыве операнды будут складываться на 64битном сумматоре и разницы не будет никакой. В другом возможны варианты. Причем заметь, железо ведь тоже разрабатывают не в вакууме. Если бы программисты чаще использовали типы с меньшей разрядностью, разве не догадались бы разработчики процессоров сделать возможность выполнять несколько малоразрядных операций на одном АЛУ? Вон, в GPU вполне себе умеют обрабатывать 16битные флоаты с двойной скоростью.

Кстати, такой ещё к тебе лично вопрос. Вот у нас уже K8, Core 2 и пентиум 4 отправлены в отставку. На них даже винду не установить современную. Так, так что умножение 64 бит выполняется с той же скоростью, что и умножение 8 бит. Да и до этого, там разница была типа 4 такта латентности против 3х, не бог весть что. По твоей логике, раз полная ширина АЛУ - 64бита, то и инт должен стать 64битным. А его до сих пор 32битным держат. Где твое возмущение? Что за предатели окопались среди разрабов компиляторов?

khrundel ★★★★
()
Ответ на: комментарий от khrundel

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

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

Crocodoom ★★★★★
()
Ответ на: комментарий от bugfixer

По моей информации Вы же с ФУПМ

Был когда-то... Но HPC меня учил товарищ Келдыш. В «сильную» группу по информатике на ФУПМе мне попасть не удалось, так как со школы я знал только паскаль, и то средне

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Ага. Только если значащих бит N1 < N, то эффективность утилизации сумматора падает. До величины N1/N

Я тут на работу пришёл. Рискую хохотом обрушить офисное здание.

Мальчик, ты понимаешь словосочетание «аппаратный сумматор». Это такой «кубик» в процессоре, у которого вход А 32 битный и вход В 32-битный. Как ты на него собираешься подавать 16- или 8-битные данные?

akk ★★★★★
()
Последнее исправление: akk (всего исправлений: 1)
Ответ на: комментарий от akk

Дядя, я вообще не буду подавать в 32-битный сумматор 16-битные и 8-битные данные. Чего и вам желаю.

Crocodoom ★★★★★
()
Ответ на: комментарий от bugfixer

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

Тут как-то fsb4000 приводил ссылку на тему: How to find the first nonzero in an array efficiently?. В данной теме Rust показывал прирост производительности при использовании u128 вместо u32 (что соответствует сишному uint). fsb4000 с помощью ассемблерной вставки ещё ускорил алгоритм, использовав 256 бит.

Kogrom
()
Ответ на: комментарий от Kogrom

fsb4000 с помощью ассемблерной вставки ещё ускорил алгоритм, использовав 256 бит.

Довольно красиво сделал (там правда intrinsics а не asm, но это мелочи). Но я не до конца понимаю какое это имеет отношение к моему посылу использовать int а не int8/16/32/64 там где это возможно?

P.S. На самом деле в теме о нахождении первого не-нолика всё очень сильно зависит от входных данных: если мы ожидаем мало leading zeros не факт что банальный лупчик будет медленнее. А если ещё учесть что avx256 имеет тенденцию быть медленным поначалу (почитать можно туточки) то всё становится ещё менее очевидным. В общем - я бы с выводами не спешил, и прежде всего тестировался на нагрузках и данных приближенных к «боевым».

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Но я не до конца понимаю какое это имеет отношение к моему посылу использовать int а не int8/16/32/64 там где это возможно?

А смысл в чём? Если мы уже привязываемся к конкретному железу (а именно к современным компьютерам), то int32 и int64 не будут менее эффективны, чем int. Или нет?

Пока что единственное преимущество int, которое я вижу - более красивое название типа, если нам не важно количество бит. А что касается прочих long и short, то они уже точно не нужны - нагляднее будет с типы цифрами.

Kogrom
()
Ответ на: комментарий от Kogrom

А смысл в чём? Если мы уже привязываемся к конкретному железу (а именно к современным компьютерам), то int32 и int64 не будут менее эффективны, чем int. Или нет?

Как мы знаем на x86[-64] int и int32 это одно и тоже. Фундаментальная разница в наиболее точном выражении of intent: каждый раз когда я вижу гвоздями прибитую размерность у меня мгновенно возникает вопрос «а зачем что-то отличное от default человеку понадобилось?». Фиксированные размеры имеют полное право на жизнь там где memory layout важен (структурки живущие в SHM, длинные массивы etc), но если это какой-то temporary (например индекс цикла) - уж лучше ему оставаться обычным int’ом, в худшем случае size_t если переполнение 32bit в конкретном месте реально (но таких по факту немного).

А что касается прочих long и short, то они уже точно не нужны - нагляднее будет с типы цифрами.

Вот здесь - согласен. У нас long вообще забанен так как меняет размер между m32 и m64 - столько проблем с этим отгребли в свое время, что даже не смешно.

bugfixer ★★★★★
()
Ответ на: комментарий от Crocodoom

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

Платят деньги за то что заменяешь short, int16_t и int32_t на int? Сомневаюсь. Уточни у работодателя.

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

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

А что случилось? Неужели наш эксперт дважды обосравшись таки осознал, что его сила не в конкретике?

khrundel ★★★★
()
Ответ на: комментарий от bugfixer

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

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

Вот, во время игр в абстракцию и запилили тип «целое». 16 бит. Потом, заметив что предел в 32тысячи - это недостаточно для целого увеличили до 32х, там где это недорого. А вот до 64 как-то не стали, типа 2 миллиардов уже достаточно. На основании этого казуса куча народа поверило в существование Великого Правила Нативного Инта, которое правда работает только в промежутке от 16 до 32 бит.

Собственно в истории IT полно эпизодов, когда успешные проекты содержали ошибки. Как технического плана, так и таргетированная. Например Linux и Java изначально планировались для десктопа, а теперь вот широко используются везде кроме десктопов. Так и с C - язык вытеснен из ниши в которой имеет смысл оперировать понятиями «просто целое», а там где остался надо точно знать разрядность. Собственно это уже произошло, int де-факто стал синонимом int32_t. Посмотри например на openCl. Они добавили тип half - 16битный флоат. Потому что железо для которого программируют на openCl умеет оперировать в SIMD с 16битами и делает это быстрее чем с 32. И значит операции типа gather/scatter с 16битными компонентами есть. И 16битный тип short там тоже есть. Вероятно 16битные целые увеличат пропускную способность вдвое. Если не на существующих GPU, то на тех, которые могут появиться. Однако никому в голову не пришло для большей производительности определить int снова 16битным. Так что даже если завтра представят новую 8битную платформу, подозреваю что родной для нее компилятор c будет послушно перемножать столбиком 32битные инты, просто чтоб не подводить ожидания программистов.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Проблема в том, что нет у тебя «просто инта».

У меня - есть. И у миллиона остальных «лемингов» - тоже. И что он значит я уже неоднократно писал.

«Просто инт» - это BigInt, целое неограниченной размерности.

Нет. Просто int - это native register size, ни больше, ни меньше. Если кто-то это трактует по другому - это его личные проблемы. Главное чтобы авторы компиляторов от этой трактовки не отходили, но я думаю - в обозримом будущем им и не даст никто.

bugfixer ★★★★★
()
Ответ на: комментарий от khrundel

Собственно в истории IT полно эпизодов, когда успешные проекты содержали ошибки.

Не удержался. Вы видели безглючный софт сложнее «hello world»?

Однако никому в голову не пришло для большей производительности определить int снова 16битным.

Я могу себе представить что он подрастёт до 64бит (очень сомнительно, на самом деле), но движения в сторону 16ти не вижу никак.

bugfixer ★★★★★
()
Ответ на: комментарий от khrundel

а там где остался надо точно знать разрядность

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

SIMD

Что тебе это СИМД покоя не даёт? В СИМД регистр сначала данные нужно загрузить, сделать ты это можешь хоть из 100500 битного инта с потерей «хвоста». Нефиксированный размер типов вообще ничего здесь не значит.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

Ты сам это выдумал, зачем тебе знать точную разрядность в счетчике цикла, например?

Злые языки говорят, что некоторые обломились при переходе на 64-бита вот в таких случаях:

std::vector<char> data;
...
for(int i = 0; i < data.size(); ++i)
  data[i] = ...;

Вруть, наверное.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Если некоторые не осилили прочитать, что int гарантирует лишь 16 бит, то это их проблемы. Даже этот код не факт что станет невалиден, возможно там запас с головой. И если бы они заюзали int32, то при переходе они обломились бы точно так же.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

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

Даже этот код не факт что станет невалиден

Он в принципе невалиден, т.к. вместо int-а нужно было бы vector<char>::size_type использовать. Ну или хотя бы size_t.

И если бы они заюзали int32

Тем не менее, лишняя подсказка в коде.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Он в принципе невалиден, т.к. вместо int-а нужно было бы vector::size_type использовать. Ну или хотя бы size_t.

Ну т.е. нефиксированный тип. А фиксированный не менее ущербен в данном кейсе. Значит условный Хрюндель верующий в последние, также получит по лбу граблями.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

в данном кейсе

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

Не всегда нужно знать точно (16, 32 или 64), но нужно знать, что она достаточна. К сожалению, short, int и даже long не позволяют это знать.

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

Условному Хрюнделю (включая меня) было бы проще определять тот же самый size_type через int16/int32/int64, а не через short, int или long.

PS. Кстати говоря, еще хороший кейс, на котором легко обосраться, это преобразование int-ового значения в строку с сохранением в char[]. Типа:

char str_value[10];
my_int_to_str(int_value, str_value);

Вот как определить, какая размерность должна быть у str_value когда у нас int плавает туда-сюда, а мы не хотим тратить стек под лишнее?

В тоже время для int16/int32/int64 этот вопрос решается гораздо проще.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Злые языки говорят, что некоторые обломились при переходе на 64-бита

Для этого нужно встретить реальный вектор длиннее 31/32 бит. Таких мест действительно немного.

ПыСы. Знаете как выглядит самый быстрый цикл по вектору на любой платформе из тех с которыми ваш покорный сталкивался до сих пор?

for (int i = 0, n = v.size(); i < n; ++i) {
   ...
}

Справедливо вне зависимости от того что происходит в теле цикла. Не благодарите.

ПыПыСы. Всякие там SIMD и prefetch мы в этом контексте не рассматриваем.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Ответ на: комментарий от bugfixer

Для этого нужно встретить реальный вектор длиннее 31/32 бит. Таких мест действительно немного.

Но вот кому-то не повезло именно после перехода на 64 бита, т.к. появилась возможность создавать массивы больше 2GiB длиной.

for (int i = 0, n = v.size(); i < n; ++i)

Вроде бы нормальные компиляторы должны выдавать предупреждения на усечение unsigned (int/long) до int-а.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Вроде бы нормальные компиляторы должны выдавать предупреждения на усечение unsigned (int/long) до int-а.

В тех немногочисленных местах (подчёркиваю - немногочисленных) где переполнение таки возможно int меняется на size_t, и вуаля (с небольшой потерей на 64 битах).

bugfixer ★★★★★
()
Ответ на: комментарий от eao197

Вот как определить, какая размерность должна быть у str_value когда у нас int плавает туда-сюда, а мы не хотим тратить стек под лишнее?

А что бы было, если бы инт не плавал? Значит существует ситуация, когда в int_value пихают больше положенного (раз допускается выход за проектные пределы при росте int’a) -> переполнение и мусорное значение как итог. Аналог сделать несложно:

char str_value[10];
my_int_to_str(int_value % 1e5, str_value);

Но тут вопрос - а что лучше, мусорное значение с туманной перспективой отлова, или простое обнаружение санитайзером в этой «ловушке»? Т.е. в коде уже есть проблема и она живёт задолго до my_int_to_str().

Мелочи это всё. Меня вот куда больше интересует - почему в std крестов всунули специализации локалонезависмых codecvt фасетов для utf конверсий, но нет специализации для конвертаций между utf-16<->utf-32, очень странно, видимо предлагают делать цепочку из костылей: utf-16->utf-8->utf-32. Почему-то прокси кодировкой является utf-8, я бы понял если бы в этой роли выступал utf-32.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 2)
Ответ на: комментарий от kvpfs

А что бы было, если бы инт не плавал?

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

Чтобы пример был более реальным: представьте, что нам нужно сгенерировать строковый индентификатор на базе нескольких целочисленных значений (который затем будет использоваться в качестве имени файла/каталога, в качестве ключа в БД, в качестве параметра в URL и т.д.). И нам нужно уместиться в какой-то фиксированный размер (что особенно актуально для БД).

int32 в hex-е всегда будет 8 символов. Сколько будет int, если мы не ограничиваемся только случаями платформ на которых int всегда 32 бита? А хз.

Значит существует ситуация, когда в int_value пихают больше положенного (раз допускается выход за проектные пределы при росте int’a)

Не понял откуда вы это взяли.

my_int_to_str(int_value % 1e5, str_value);

В чем смысл этого примера не понял.

Во-первых, у вас тут double будет вместо int-а, и компилятор должен ударить по рукам.

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

Меня вот куда больше интересует - почему в std крестов всунули специализации локалонезависмых codecvt фасетов для utf конверсий

Простите, совсем не моя тема, я здесь вообще не копенгаген, а осло.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Сколько будет int, если мы не ограничиваемся только случаями платформ на которых int всегда 32 бита? А хз.

sizeof(int) * CHAR_BIT / 4 няп

bga_ ★★★★
() автор топика
Ответ на: комментарий от bga_

sizeof(int) * CHAR_BIT / 4

Типа того.

По хорошему так и нужно делать. К сожалению, я сам далеко не всегда вспоминаю про существование CHAR_BIT :(

eao197 ★★★★★
()
Ответ на: комментарий от kvpfs

Почему-то прокси кодировкой является utf-8

Потому что БАЗА

я бы понял если бы в этой роли выступал utf-32

🤦‍♂️

utf8nowhere ★★★
()
Ответ на: комментарий от bugfixer

У меня - есть. И у миллиона остальных «лемингов» - тоже.

Это неправда. Любой программист, даже изучающий, знает о пределе разрядности.

Нет. Просто int - это native register size

Глупость.

На x86_64 int 32битный.

khrundel ★★★★
()
Ответ на: комментарий от kvpfs

Ты сам это выдумал, зачем тебе знать точную разрядность в счетчике цикла, например?

Вот именно поэтому. Твои слова как раз демонстрируют.

Есть такое понятие, идеоматичный код. Для C идеоматичный цикл, когда не нужно что-то особое, это цикл по инту. Тебе привели пример с вектором, но это уже уровень выше, кто-то извне дал тебе вектор на обработку. Я предлагаю самый простой код, цикл до тысячи, и внутри умножение счётчика на 40, без явного приведения к 32 битам. Если бы джун написал такой код, ты бы на ревью докопался до «инт гарантирует только 16 бит»?

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

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Писать то надо чётко int_fastN_t где просить столько бит сколько нужно. А не надеяться на то что int хватит. Давно не использую int в коде. Только fast для работы и, с недавнего времени, least для хранения вместо фиксированных типов. Причём всё это typedef где то наверху чтобы коде использовать LogIndex_Store и LogIndex_Fast

bga_ ★★★★
() автор топика
Ответ на: комментарий от khrundel

в твоём проекте код, который неявно зависит от 32битных интов

можно static_assert добавить, тогда при смене платформы код не соберётся и компилятор подскажет что на этой платформе не выполняется.

Я так делал для long, что он 32 битный(на Windows это так) и чтобы тесты не прошли если вдруг когда-то кто-то захочет перенести это на Linux.

fsb4000 ★★★★★
()
Ответ на: комментарий от khrundel

Глупость. На x86_64 int 32битный.

Внезапно: на x86_64 32 битные операции быстрее чем 64 битные, некоторые - в разы. Я уверен вы много статей на эту тему найдёте погуглив.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Внезапно: уже нет. Умножение что 32битное, что 64битное. Деление да, но тут такой момент: idiv 8bit быстрее 16 бит, 16 быстрее 32, 32 быстрее 64. Так что если бы латентность деления была аргументом, пришлось бы int 8битным делать.

В общем попал ты пальцем в небо.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

В общем попал ты пальцем в небо.

Удачи в ваших изысканиях.

ПыСы. Ситуация заметно разнится между Intel и AMD. Одна из причин почему мы AMD потестировали, и выкинули на помойку.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Ситуация заметно разнится между Intel и AMD. Одна из причин почему мы AMD потестировали, и выкинули на помойку.

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

Но и бог с ними, с x86. Я вот что нашел: https://en.m.wikipedia.org/wiki/64-bit_computing#64-bit_data_models

ILP64 (т.е. когда int long и pointer все 64битные) и SILP64 не используются практически нигде. Вне зависимости от железа. В основном LP64. Майкрософт стоит особняком, в их компиляторе даже long остался 32 бита. И на x86_64, и на i64

Так что нет, размер инта сейчас никак не связан с размером регистра или скоростью выполнения 64битных операций

khrundel ★★★★
()
Ответ на: комментарий от khrundel

И те и другие складывают, вычитают и умножают 64битные целые

Выше - правда.

с той же скоростью что и 32битные

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

и верхний предел зависит от ширины операндов, меньше всего у 8битных.

Господи. Молодой человек - слюной не захлебнитесь.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Ответ на: комментарий от bugfixer

Поменьше фанбойства. У всех актуальных латентность и темп запуска 64 бит и 32 совпадает. Да, у Интел это случилось на 10 лет раньше, с core i*, видимо это ты и запомнил

Господи. Молодой человек - слюной не захлебнитесь.

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

khrundel ★★★★
()
Последнее исправление: khrundel (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Это правда что на Win long это 32 бита, а unsigned long - 64?

Сам-то как думаешь? Нет, конечно. Видимо кто-то неправильно понял java, которая 32битные беззнаковые предлагает обрабатывать через знаковый long.

khrundel ★★★★
()
Ответ на: комментарий от fsb4000

Спасибки! Я тоже «поморгал» когда услышал, но решил - «ну наверное» …

bugfixer ★★★★★
()
Ответ на: комментарий от khrundel

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

Вы знаете - а я должен вам сказать «большое спасибо». Вы таки заставили меня «расчехлиться» и замерить пару вещей на «боевом железе». Результаты, мягко говоря, совсем не то что я ожидал увидеть. И таки да - нынче походу indexes 32 vs 64 бит роли не играют, по крайней мере на том что под рукой (Gold 6242R) - идут «ноздря в ноздрю» что называется. Есть еще несколько эффектов которые я пока обьяснить не могу, будем разбираться.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Эммм.. Я же с самого начала говорил, что 32-битному сумматору в целом всё равно, какие данные в него загнаны, 32-битные или 16-битные. По умолчанию я считал, что это верно и для 64-битного сумматора и не только сумматора, если не доказано обратное.

А «не всё равно» становится, когда мы начинаем считать arithmetic throughput. Да, я по-прежнему считаю, что грузить 8-битные данные в 64-битные сумматоры можно, но это неэффективно. Сам я так стараюсь не делать и другим на советую. Хрюндель этот аргумент кстати понял (слава богам), но продолжил кидаться какашками, потому что ему эти соображения не кажутся существенными.

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

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

32-битные или 16-битные.

Емнип: 8/16 битный load не занулял старшие разряды, ножки именно оттуда растут. При переходе 32 -> 64 учли ошибки былых поколений.

bugfixer ★★★★★
()
Ответ на: комментарий от Crocodoom

идут «ноздря в ноздрю»

Справедливости ради: «десктопный» i7 ведёт себя совсем по другому. И ещё почему-то неожиданно вылезает разница signed vs unsigned, не в пользу signed. Вот этого я пока объяснить не могу (ожидал наоборот, если честно). На asm буду смотреть чуть позже, но однозначно буду.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

И ещё почему-то неожиданно вылезает разница signed vs unsigned, не в пользу signed. Вот этого я пока объяснить не могу

Арифметика над signed - UB в общем случае, а над unsigned - нет 🙂

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

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Арифметика над signed - UB в общем случае, а над unsigned - нет

Именно. Я бы ожидал больший простор для оптимизаций в signed. А оно эвона как…

bugfixer ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.