Блог* – Telegram
1.9K subscribers
3.46K photos
135 videos
15 files
3.7K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
И ещё у этого правила есть совсем неожиданное исключение: доступ по указателю на char, unsigned char и std::byte. При передаче value по указателю на один из этих типов компилятор оставляет доступ в цикле.

Внимательные читатели могли заметить, что этот список не включает в себя signed char (и, кстати, в этом месте C++ отличается C, в котором алиаситься могут указатели любых вариантов char). Тем не менее, вариант increment с указателем на signed char последние версии и GCC (12.2), и clang (15.0.0) не могут скомпилировать с выносом доступа к value из цикла. Почему — не ясно. Наверное, это можно считать багом.

Поиграться со всеми упомянутыми вариантами: godbolt.org/z/8rr3hWTbf
(ссылка для расшаривания, к сожалению, потеряла все имена вкладок с кодом. Ну хоть имена вкладок с компиляторами оставила)
🔥4👍2
В СМЫСЛЕ УЖЕ НОЯ то есть ДЕКАБРЬ
👍5
Блог*
#prog #cpp #моё В C++ есть такая вещь, как strict aliasing. Если коротко, то это предположение компилятора о том, что доступы по указателям (и ссылкам) существенно разных типов не пересекаются между собой. Подробнее про это можно прочитать, например, тут…
В комментариях сказали, что не хватает демонстрации того, что Rust лучше. Что ж, исправляю.

Вот как выглядит идиоматичная функция с аналогичным функционалом (с поправкой на то, что, как и в коде на C++ в постах выше, идиоматично было бы принимать value вообще по значению):

fn increment(arr: &mut [i32], value: &i32) {
for x in arr {
*x += *value;
}
}

Rust 1.65.0 с флагом -C opt-level=1 выдаёт следующий код:

test rsi, rsi
sete al
je .LBB3_4
lea rsi, [rdi + 4*rsi]
mov ecx, dword ptr [rdx]
.LBB3_2:
not al
movzx eax, al
add dword ptr [rdi], ecx
and eax, 1
lea rdi, [rdi + 4*rax]
cmp rdi, rsi
sete al
je .LBB3_4
test rdi, rdi
jne .LBB3_2
.LBB3_4:
ret

Вывод несколько шумный (эти манипуляции с al и eax явно излишни), но самое главное: доступ к содержимому value осуществляется через

mov ecx, dword ptr [rdx]

— инструкцию вне цикла! И это вполне ожидаемо: в Rust есть более тонкие способы отслеживать aliasing, чем просто через типы. Тот факт, что набор чисел передаётся через ссылку &mut (и, в отличие от span в C++, это действительно именно ссылка, а не value-тип с ссылочной семантикой), уже позволяет считать все остальные доступы к памяти, как непересекающиеся. поскольку в Rust &mut — это уникальная ссылка. При кодогенерации компилятор может рассчитывать на этот факт, а при создании ссылок проверяет, что ссылка действительно уникальна.

(также можно было бы переписать эту функцию через доступы по индексам вместо прямого for x in arr, но, хотя тело цикла в этом случае чище, компилятор частично раскручивает цикл, что генерировало несколько больше ассемблерного кода, чем имеет смысл помещать в пост)

Возможно ли повторить на Rust семантику из C++? Да... С сигнатурой вида

fn increment(arr: *mut [UnsafeCell<MaybeUninit<i32>>], value: *mut MaybeUninit<i32>)

...Ладно, ладно, если что-то не столь страшное, а всё-таки более идиоматичное, но с возможностью пересечения доступа:

fn increment(arr: &[Cell<i32>], value: &Cell<i32>) {
for x in arr {
x.set(x.get() + value.get());
}
}

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

test rsi, rsi
sete cl
je .LBB4_4
lea rax, [rdi + 4*rsi]
.LBB4_2:
not cl
movzx ecx, cl
mov esi, dword ptr [rdx]
add dword ptr [rdi], esi
and ecx, 1
lea rdi, [rdi + 4*rcx]
cmp rdi, rax
sete cl
je .LBB4_4
test rdi, rdi
jne .LBB4_2
.LBB4_4:
ret

Снова кучка лишних операций, но главное — это вот эти две операции в теле цикла:

mov esi, dword ptr [rdx]
add dword ptr [rdi], esi

То есть значение читается по ссылке каждый раз, как мы и (не) хотели.
👍12🔥1
Блог*
#prog #cpp #моё В C++ есть такая вещь, как strict aliasing. Если коротко, то это предположение компилятора о том, что доступы по указателям (и ссылкам) существенно разных типов не пересекаются между собой. Подробнее про это можно прочитать, например, тут…
Кстати, тот факт, что мы принимаем &[Cell<i32>], вовсе не означает, что нам нужно бегать по массивам и оборачивать элементы в Cell-ы. Можно воспользоваться двумя методами Cell:

fn into_cell_slice<T>(arr: &mut [T]) -> &[Cell<T>] {
    Cell::from_mut(arr).as_slice_of_cells()
}

Cell::from_mut работает за счёт того, что забирает переданную mut-ссылку и потому отбирает уникальный доступ до тех пор, пока возвращённая ссылка и её копии не будут дропнуты. Не смотря на то, что &Cell<T> даёт разделяемый доступ с возможностью поменять значение, это не создаёт проблем за счёт того, что изначально переданный доступ заведомо уникален, а доступ по &Cell<T> хоть и и не уникален, но заведомо находится в пределах одного потока: Cell<T> не реализует Sync, или, иными словами, ссылка на Cell не реализует Send, поэтому ссылку на Cell нельзя передать в другой поток. В рамках же одного потока доступы к одной локации в памяти никогда не происходят одновременно, и потому возможность создания гонок данных исключена.

Ну а Cell::as_slice_of_cells имеет очень простое оправдание: если мы имеем де-факто мутабельный доступ к какому-то значению, то мы можем разделить его на мутабельные доступы к его отдельным компонентам. При этом как-то подменить слайс целиком, чтобы поменять его длину, и инвалидировать ссылки на его элементы нельзя: все соответствующие методы или требуют уникальную ссылку, или работают только с Sized типами, которым слайс не является.
3👍3🔥2
Forwarded from Jem
😁16😐1
Forwarded from ЕНОТ ИЗДАЕТ
Ваша последняя книжная покупка? 👀
8
а ведь реальна блин
(кто скажет что боян тому зуб откушу)
11🤡2😁1
Forwarded from ТГ Шевченка
👍132
Forwarded from 🇺🇦 Go for two :)
The [adventofcode] just started

Happy hacking!
https://adventofcode.com/2022/day/1
🤔3👍2
Что-то в этот раз знакомых лиц значительно меньше
😭5
"TAIT — это красиво, но это nightly, у нас, например, прод на stable"

Слабаки!
🤬2
Блог*
@goldsteinq про name-it рассказывает. Интересно. Говорит, miri ругается на UB в крейте futures
#prog #rust #rustlib

Узнал про elain.

The type Align<N> is a zero-sized-type with alignment equal to N:

use elain::Align;
use core::mem::{align_of, align_of_val};

assert_eq!(align_of::<Align<1>>(), 1);
assert_eq!(align_of::<Align<2>>(), 2);
assert_eq!(align_of::<Align<4>>(), 4);

const FOO_ALIGN: usize = 8;

#[repr(C)]
struct Foo {
_align: Align<FOO_ALIGN>,
}

let foo: Foo = Foo { _align: Align::NEW };

assert_eq!(align_of_val(&foo), 8);


Valid alignments are powers of two less-than-or-equal to 2^28. Supplying an invalid alignment to Align is a type error:

use elain::Align;

struct Foo(Align<3>); // Compile Error
👍7🤔2
Мой кулинарный гений не знает границ

#rustcon2022
👍9🥴63👎2🔥2😱1🤮1