要使用旧vector的内容创建新vector(我想清空旧vector),我将使用:用途:
foo.bar = my_vector.drain(0..).collect();
这是惯用的Rust吗?drain()是否被优化掉了,而原始向量的内存只是给了新向量?或者每个元素都被一个接一个地复制到一个新的堆分配中?
xa9qqrwz1#
我会做
foo.bar = std::mem::take(&mut my_vector);
这是一个更一般的解决方案。std::mem::take()改变其参数,以便 * 窃取 * 其内容并使其保持默认状态。窃取的内容用于创建相同类型的新值。实际的 * 窃取 * 操作是结构本身的简单字节交换(一些指针/整数用于Vec,String,任何其他容器...)。存储的元素,可能是堆分配的,留在原地,因此这个操作最终是非常便宜的(复制然后清除很少的字节,即sizeof结构)。
std::mem::take()
Vec
String
sizeof
flvlnr442#
将所有元素从一个Vec移动到另一个take的最便宜的方法是take它们:
take
这将给予分配和所有元素的所有权给foo.bar,并使my_vector处于默认状态(空,没有分配空间)。你可以使用.drain() + .collect()来移动元素,同时保留my_vector的分配,但是你可以通过使用上面的方法来避免复制成本,然后再简单地将空间放回.reserve()。如果foo.bar也分配了元素或空间,那么最好是swap它们(交换它们的元素和分配),然后清除my_vector,如果你想让它为空:
foo.bar
my_vector
.drain()
.collect()
.reserve()
swap
std::mem::swap(&mut foo.bar, &mut my_vector); my_vector.clear();
这可以避免以后将元素放入my_vector时的分配。
ctzwtxfj3#
我不认为编译器看到了这种优化,我也不完全确定它是否有效。你可以看到on the compiler explorer,你的代码确实发出了分配,后面跟着一个memmove:
memmove
//… 355: mov edi, 396 356: mov esi, 4 357: call qword ptr [rip + __rust_alloc@GOTPCREL] //… 442: call qword ptr [rip + memmove@GOTPCREL] //…
为了drain一个完整的向量,我会使用drain(..),越少的显式数字越好,但更符合你的意图的可能是
drain
drain(..)
或
let cap = my_vector.capacity(); foo.bar = std::mem::replace(&mut my_vector, Vec::with_capacity(cap));
r7xajy2e4#
如果你不想重新分配,请使用std::mem::take(),因为@ fx-fh已经回答过了。我只是想添加一些示例,说明这对生成的机器代码有什么影响。下面是你的代码编译成的内容:
pub fn move_drain(my_vector: &mut Vec<u8>) -> Vec<u8>{ my_vector.drain(0..).collect() }
core::ptr::drop_in_place<alloc::vec::Vec<u8>>: test rsi, rsi je .LBB0_1 mov edx, 1 jmp qword ptr [rip + __rust_dealloc@GOTPCREL] .LBB0_1: ret core::ptr::drop_in_place<alloc::vec::drain::Drain<u8>>: push r15 push r14 push rbx lea rax, [rip + .Lanon.716d6a888caeb235278dc75f1465c66b.0] mov rcx, qword ptr [rdi + 8] mov rbx, qword ptr [rdi + 32] cmp rcx, qword ptr [rdi] mov qword ptr [rdi], rax mov qword ptr [rdi + 8], rax test rbx, rbx je .LBB1_4 mov rax, qword ptr [rdi + 16] mov rsi, qword ptr [rdi + 24] lea r14, [rax + 16] mov r15, qword ptr [rax + 16] cmp rsi, r15 je .LBB1_3 mov rdi, qword ptr [rax] add rsi, rdi add rdi, r15 mov rdx, rbx call qword ptr [rip + memmove@GOTPCREL] .LBB1_3: add r15, rbx mov qword ptr [r14], r15 .LBB1_4: pop rbx pop r14 pop r15 ret alloc::raw_vec::finish_grow: push r15 push r14 push rbx mov rbx, rdx mov r14, rdi test rsi, rsi je .LBB2_6 mov r15, rsi cmp qword ptr [rcx + 8], 0 je .LBB2_7 mov rsi, qword ptr [rcx + 16] test rsi, rsi je .LBB2_7 mov rdi, qword ptr [rcx] mov rdx, r15 mov rcx, rbx call qword ptr [rip + __rust_realloc@GOTPCREL] test rax, rax jne .LBB2_11 .LBB2_4: mov qword ptr [r14 + 8], r15 jmp .LBB2_5 .LBB2_7: test rbx, rbx je .LBB2_8 mov rax, qword ptr [rip + __rust_no_alloc_shim_is_unstable@GOTPCREL] movzx eax, byte ptr [rax] mov rdi, rbx mov rsi, r15 call qword ptr [rip + __rust_alloc@GOTPCREL] test rax, rax je .LBB2_4 .LBB2_11: mov qword ptr [r14 + 8], rax xor eax, eax jmp .LBB2_12 .LBB2_6: mov qword ptr [r14 + 8], 0 .LBB2_5: mov eax, 1 .LBB2_12: mov qword ptr [r14 + 16], rbx mov qword ptr [r14], rax pop rbx pop r14 pop r15 ret .LBB2_8: mov rax, r15 test rax, rax jne .LBB2_11 jmp .LBB2_4 alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle: push r14 push rbx sub rsp, 56 add rsi, rdx jb .LBB3_10 mov rbx, rdi mov rax, qword ptr [rdi + 8] lea rcx, [rax + rax] cmp rcx, rsi cmova rsi, rcx cmp rsi, 9 mov r14d, 8 cmovae r14, rsi mov rsi, r14 not rsi shr rsi, 63 test rax, rax je .LBB3_2 mov rcx, qword ptr [rbx] mov qword ptr [rsp + 8], rcx mov qword ptr [rsp + 16], 1 mov qword ptr [rsp + 24], rax jmp .LBB3_4 .LBB3_2: mov qword ptr [rsp + 16], 0 .LBB3_4: lea rdi, [rsp + 32] lea rcx, [rsp + 8] mov rdx, r14 call alloc::raw_vec::finish_grow cmp qword ptr [rsp + 32], 0 mov rdi, qword ptr [rsp + 40] je .LBB3_5 movabs rax, -9223372036854775807 cmp rdi, rax jne .LBB3_8 add rsp, 56 pop rbx pop r14 ret .LBB3_5: mov qword ptr [rbx], rdi mov qword ptr [rbx + 8], r14 add rsp, 56 pop rbx pop r14 ret .LBB3_8: test rdi, rdi jne .LBB3_9 .LBB3_10: call qword ptr [rip + alloc::raw_vec::capacity_overflow@GOTPCREL] ud2 .LBB3_9: mov rsi, qword ptr [rsp + 48] call qword ptr [rip + alloc::alloc::handle_alloc_error@GOTPCREL] ud2 example::move_drain: push r15 push r14 push r13 push r12 push rbx sub rsp, 112 mov rbx, rdi mov rax, qword ptr [rsi] mov r14, qword ptr [rsi + 16] mov qword ptr [rsi + 16], 0 mov qword ptr [rsp + 72], rax add rax, r14 mov qword ptr [rsp + 80], rax mov qword ptr [rsp + 88], rsi mov qword ptr [rsp + 96], r14 mov qword ptr [rsp + 104], 0 test r14, r14 je .LBB4_1 setns al js .LBB4_5 movzx r15d, al mov rax, qword ptr [rip + __rust_no_alloc_shim_is_unstable@GOTPCREL] movzx eax, byte ptr [rax] mov rdi, r14 mov rsi, r15 call qword ptr [rip + __rust_alloc@GOTPCREL] test rax, rax jne .LBB4_2 mov rdi, r15 mov rsi, r14 call qword ptr [rip + alloc::alloc::handle_alloc_error@GOTPCREL] jmp .LBB4_6 .LBB4_1: mov eax, 1 .LBB4_2: mov qword ptr [rsp + 8], rax mov qword ptr [rsp + 16], r14 mov qword ptr [rsp + 24], 0 vmovups ymm0, ymmword ptr [rsp + 72] vmovups ymmword ptr [rsp + 32], ymm0 mov rcx, qword ptr [rsp + 104] mov qword ptr [rsp + 64], rcx mov r15, qword ptr [rsp + 32] mov r12, qword ptr [rsp + 40] mov rdx, r12 sub rdx, r15 cmp r14, rdx jae .LBB4_3 lea rdi, [rsp + 8] xor esi, esi vzeroupper call alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle mov rax, qword ptr [rsp + 8] mov rcx, qword ptr [rsp + 24] jmp .LBB4_11 .LBB4_3: xor ecx, ecx .LBB4_11: mov r13, qword ptr [rsp + 48] mov rsi, qword ptr [rsp + 56] mov r14, qword ptr [rsp + 64] cmp r15, r12 je .LBB4_14 .LBB4_12: movzx edx, byte ptr [r15] inc r15 mov byte ptr [rax + rcx], dl inc rcx cmp r15, r12 jne .LBB4_12 .LBB4_14: mov qword ptr [rsp + 24], rcx test r14, r14 je .LBB4_18 mov r15, qword ptr [r13 + 16] cmp rsi, r15 je .LBB4_17 mov rdi, qword ptr [r13] add rsi, rdi add rdi, r15 mov rdx, r14 vzeroupper call qword ptr [rip + memmove@GOTPCREL] .LBB4_17: add r15, r14 mov qword ptr [r13 + 16], r15 .LBB4_18: mov rax, qword ptr [rsp + 24] mov qword ptr [rbx + 16], rax vmovups xmm0, xmmword ptr [rsp + 8] vmovups xmmword ptr [rbx], xmm0 mov rax, rbx add rsp, 112 pop rbx pop r12 pop r13 pop r14 pop r15 vzeroupper ret .LBB4_5: call qword ptr [rip + alloc::raw_vec::capacity_overflow@GOTPCREL] .LBB4_6: ud2 mov rbx, rax lea rdi, [rsp + 32] call core::ptr::drop_in_place<alloc::vec::drain::Drain<u8>> mov rdi, qword ptr [rsp + 8] mov rsi, qword ptr [rsp + 16] call core::ptr::drop_in_place<alloc::vec::Vec<u8>> mov rdi, rbx call _Unwind_Resume@PLT ud2 mov rbx, rax lea rdi, [rsp + 72] call core::ptr::drop_in_place<alloc::vec::drain::Drain<u8>> mov rdi, rbx call _Unwind_Resume@PLT ud2 .Lanon.716d6a888caeb235278dc75f1465c66b.0: DW.ref.rust_eh_personality: .quad rust_eh_personality
与此相反,这里使用std::mem::take:
std::mem::take
pub fn move_take(my_vector: &mut Vec<u8>) -> Vec<u8>{ std::mem::take(my_vector) }
example::move_take: mov rax, rdi mov rcx, qword ptr [rsi + 16] mov qword ptr [rdi + 16], rcx vmovups xmm0, xmmword ptr [rsi] vmovups xmmword ptr [rdi], xmm0 mov qword ptr [rsi], 1 vxorps xmm0, xmm0, xmm0 vmovups xmmword ptr [rsi + 8], xmm0 ret
4条答案
按热度按时间xa9qqrwz1#
我会做
这是一个更一般的解决方案。
std::mem::take()
改变其参数,以便 * 窃取 * 其内容并使其保持默认状态。窃取的内容用于创建相同类型的新值。实际的 * 窃取 * 操作是结构本身的简单字节交换(一些指针/整数用于
Vec
,String
,任何其他容器...)。存储的元素,可能是堆分配的,留在原地,因此这个操作最终是非常便宜的(复制然后清除很少的字节,即sizeof
结构)。flvlnr442#
将所有元素从一个
Vec
移动到另一个take
的最便宜的方法是take
它们:这将给予分配和所有元素的所有权给
foo.bar
,并使my_vector
处于默认状态(空,没有分配空间)。你可以使用
.drain()
+.collect()
来移动元素,同时保留my_vector
的分配,但是你可以通过使用上面的方法来避免复制成本,然后再简单地将空间放回.reserve()
。如果
foo.bar
也分配了元素或空间,那么最好是swap
它们(交换它们的元素和分配),然后清除my_vector
,如果你想让它为空:这可以避免以后将元素放入
my_vector
时的分配。ctzwtxfj3#
我不认为编译器看到了这种优化,我也不完全确定它是否有效。你可以看到on the compiler explorer,你的代码确实发出了分配,后面跟着一个
memmove
:为了
drain
一个完整的向量,我会使用drain(..)
,越少的显式数字越好,但更符合你的意图的可能是或
r7xajy2e4#
如果你不想重新分配,请使用
std::mem::take()
,因为@ fx-fh已经回答过了。我只是想添加一些示例,说明这对生成的机器代码有什么影响。
下面是你的代码编译成的内容:
与此相反,这里使用
std::mem::take
: