LINUX.ORG.RU

Как быстро сконвертировать uint32_t в uint8_t

 , ,


1

4

У меня есть указатель на uint8_t. Я знаю, что он выровнен по адресу 4. Я хочу быстро читать и писать туда uint32_t значение.

Правильный по стандарту вариант использовать memcpy, но он очень медленный.

Быстрый вариант - кастовать uint8_t * в uint32_t * и полагаться на то, что он выровнен. Это генерирует одну инструкцию, но это UB по стандарту, хотя по факту работать будет…

Как тут можно поступить? Меня интересует конкретный компилятор gcc 9.2. Может быть gcc даёт какие-то дополнительные гарантии сверх стандарта для данного случая?

Код:

#include <stdint.h>
#include <string.h>

void save1(uint8_t *p, uint32_t v) {
    memcpy(p, &v, 4);
}

void save2(uint8_t *p, uint32_t v) {
    uint32_t *p32 = (uint32_t *)p;
    *p = v;
}

uint32_t load1(uint8_t *p) {
    uint32_t v;
    memcpy(&v, p, 4);
    return v;
}

uint32_t load2(uint8_t *p) {
    uint32_t *p32 = (uint32_t *)p;
    return *p32;
}

Во что он компилируется с -Os:

save1:
        push    {r0, r1, r2, lr}
        mov     r2, #4
        str     r1, [sp, #4]
        add     r1, sp, r2
        bl      memcpy
        add     sp, sp, #12
        ldr     lr, [sp], #4
        bx      lr
save2:
        strb    r1, [r0]
        bx      lr
load1:
        push    {r0, r1, r2, lr}
        mov     r2, #4
        mov     r1, r0
        add     r0, sp, r2
        bl      memcpy
        ldr     r0, [sp, #4]
        add     sp, sp, #12
        ldr     lr, [sp], #4
        bx      lr
load2:
        ldr     r0, [r0]
        bx      lr

godbolt

★★★★★
Ответ на: комментарий от MOPKOBKA

Да какая разница как оно там называется

Огромное. unsafe это буквально маркер ЗДЕСЬ ХТОНЬ. Ты её породил? Породил. Огреб. В следующий раз, читай гайды перед тем как её писать.

P.S. И нет, дело ваще не в касте i32 в f32.

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

Огромное. unsafe это буквально маркер ЗДЕСЬ ХТОНЬ. Ты её породил? Породил. Огреб. В следующий раз, читай гайды перед тем как её писать.

Ты понимаешь что это буквально Rust-версия совета «просто пиши на С без ошибок и никакие санитайзеры и rustы ненужны»?

P.S. И нет, дело ваще не в касте i32 в f32.

А в чем? И почему замена f32 на i32 дает другой результат?

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

Ты понимаешь что это буквально Rust-версия совета «просто пиши на С без ошибок и никакие санитайзеры и rustы ненужны»?

Конечно нет. Это аккуратно огороженные зоны, в которых надо быть очень-очень осторожным.

А в чем?

В том, что ты передаешь два указателя на одну и ту же переменную. Если заменить f32 на i32, то miri все ещё будет ругаться.

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

Тогда объясни почему два указателя i32 это ок.

Я не говорю что это ок, я буквально говорю что miri все ещё ругается. unsafe это не 1:1 C, это отдельная история с отдельными правилами.

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

P.S. И нет, дело ваще не в касте i32 в f32.

Дело в provenance. Temporal scope уникальных ссылок на одну переменную не могут пересекаться. Значит доступ по одному из указателей полученных из этих уникальных ссылок нарушает provenance. А это UB.

Чтобы получить указатель без ограничений temporal scope нужно делать core::ptr::addr_of_mut!()

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

Ты понимаешь что это буквально Rust-версия совета «просто пиши на С без ошибок и никакие санитайзеры и rustы ненужны»?

Конечно нет. Это аккуратно огороженные зоны, в которых надо быть очень-очень осторожным.

Ага, как скажешь, давай заново прочтем тред. ОП знает что некое действие это «неправильное действие», такое нельзя делать в unsafe (вся программа С), но ЕМУ НУЖНО это сделать, ты говоришь что Rust это избавление от проблем, но я показываю что там тоже при неправильных действиях, тупые результаты.

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

Ага, как скажешь, давай заново прочтем тред. ОП знает что некое действие это «неправильное действие», такое нельзя делать в unsafe (вся программа С), но ЕМУ НУЖНО это сделать, ты говоришь что Rust это избавление от проблем, но я показываю что там тоже при неправильных действиях, тупые результаты.

Ему не нужно делать UB, ему нужно ring buffer делать.

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

Ему не нужен ring buffer, ему нужно написать программу. Хотя нет, ему не нужно писать программу, ему нужно заработать на еду. Хотя и есть ему не надо, ему надо продлить свою жизнь доступными средствами и избегать отрицательных мотиваторов.

Да вроде он в ОП-посте прямо пишет в чем проблема. Часть его ring-buffer'ов требует определенной записи которой он пытается добиться.

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

Дело в provenance. Temporal scope уникальных ссылок на одну переменную не могут пересекаться. Значит доступ по одному из указателей полученных из этих уникальных ссылок нарушает provenance. А это UB.

Нет, не в нем. Убери там f32 и оно все ещё будет ругаться.

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

Temporal scope уникальных ссылок на одну переменную не могут пересекаться. Значит доступ по одному из указателей полученных из этих уникальных ссылок нарушает provenance. А это UB.

Да, miri нам примерно про это и говорит.

Чтобы получить указатель без ограничений temporal scope нужно делать core::ptr::addr_of_mut!()

Во, годнота.

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

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

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

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

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

Ну так я про это и пишу. В чем смысл Rust если там те же UB

Ну так адепт раста уже ответил: еще можно член дверью прищемить и рассказывать всем про неправильные двери.

Т.е. по этой логике раст нужен чтобы рассказывать что неправильный - член. Что вполне укладывается в их гендерные нарративы.

Не благодарите, Обезьян вернётся позже с новой прикормкой.

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

Так, стоп. На правах обезьяна, borrowing работает до stacking, а затем резко становится «а вот тут мы рыбу заворачиваем». Да, я понимаю, что still experemintal, но мне на самом деле интересно как вы раскроете мысль если поняли о чем я. Морковка показал пример гораздо глубже чем просто UB, давайте поразмышляем над этим и попробуем не скатиться в троллинг.

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

Temporal scope уникальных ссылок на одну переменную не могут пересекаться.

Ну наконец-то, в смысле, наконец мы переходим в обсуждение используя scope. Можете развить мысль дальше почему так? Что же такое с provenance, почему оно prescriptive и таки почему в итоги вся беда в компиляторе?

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

Его вообще нет т.к. это УБ, стрельнёт оно или нет - это как повезёт. Но если не пытаться сломать ссылки, то расту пофиг на типы как в примере Как быстро сконвертировать uint32_t в uint8_t (комментарий), до тех пор пока биты соответствуют валидным значениям, так что это не сишный стрикталиасинг. С другой стороны сломанным ссылкам нет соответствия в сишке, в ней ссылок нет. У них есть общее УБ связанное с алиасингом(отдалённо) которое запрещает гулять адресной арифметикой за пределами исходных объектов, но оно довольно ортогонально случаям выше

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

С другой стороны сломанным ссылкам нет соответствия в сишке

Есть: restrict-квалифицированные указатели. По крайней мере в части эксклюзивного доступа к данным.

Кстати, растовые уникальные ссылки транслируются в конструкции noalias LLVM, которые были созданы для обработки сишного restrict. Из LLVM пришлось вычищать кучу багов, связанных с noalias (в расте паттерны его использования сильно отличаются от сишных, так что раньше на эти баги не натыкались).

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

Есть: restrict-квалифицированные указатели. По крайней мере в части эксклюзивного доступа к данным.

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

zurg
()