- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
前言:我一般不是优化师。
大多数时候,在用 Rust 解决编码难题时,我使用 Vec::with_capacity
来初始化我的向量,然后通过 push
将它们插入向量.对于大多数用途来说,这很好,但我最近遇到了一个需要更快程序的难题,这激发了我重新思考我的方法。
因为我知道向量的容量恰好是某个数字,所以我决定比较我常用的 with_capacity
和 push
方法的结果,以创建一个充满0 并替换它们。这是我用来对这两个操作进行基准测试的代码:
#![feature(test)]
extern crate test;
#[cfg(test)]
mod tests {
use test::Bencher;
// Create a vector with a capacity of 10,000 u16s
// and populate it by pushing.
#[bench]
fn push_fill(b: &mut Bencher) {
b.iter(|| {
let mut v: Vec<u16> = Vec::with_capacity(10000);
for i in 0..10000 as u16 {
v.push(i);
}
})
}
// Create a vector of 10,000 u16s, initialize them
// to 0, and then replace them to populate the vector.
#[bench]
fn replace_fill(b: &mut Bencher) {
b.iter(|| {
let mut v: Vec<u16> = vec![0u16; 10000];
for i in 0..10000 {
v[i] = i as u16;
}
})
}
}
令我惊讶的是,当我运行 cargo +nightly bench
时,替换解决方案比 with_capacity
解决方案好一个数量级。
Compiling benchmarks v0.1.0 (file:///C:/Users/CEUser/Documents/Programs/rustprojects/benchmarks)
Finished release [optimized] target(s) in 10.75 secs
Running target\release\deps\benchmarks-0b553bf1dfb7e9a4.exe
running 2 tests
test tests::push_fill ... bench: 26,756 ns/iter (+/- 4,046)
test tests::replace_fill ... bench: 1,902 ns/iter (+/- 802)
test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out
我对时间上的差异感到惊讶,特别是考虑到我预计 replace
版本需要更长的时间(假设它必须创建一个充满填充的向量,然后然后 用实际数据替换填充数据)。
replace_fill
比 push_fill
快那么多有什么直观原因吗?这两个函数的作用有何区别?
最佳答案
如有疑问,请检查装配!
您可以使用 godbolt或 Playground ;尽管我在这里更喜欢 godbolt,因为它使用突出显示将程序集部分与源代码相匹配,从而更容易探索。
在上面的链接中,replace_fill
函数被优化为:
example::replace_fill:
push rbp
mov rbp, rsp
sub rsp, 48
lea rdx, [rbp - 24]
mov edi, 20000
mov esi, 2
call __rust_alloc_zeroed@PLT
test rax, rax
je .LBB3_4
movdqa xmm0, xmmword ptr [rip + .LCPI3_0]
mov ecx, 32
movdqa xmm1, xmmword ptr [rip + .LCPI3_1]
movdqa xmm2, xmmword ptr [rip + .LCPI3_2]
movdqa xmm3, xmmword ptr [rip + .LCPI3_3]
movdqa xmm4, xmmword ptr [rip + .LCPI3_4]
movdqa xmm5, xmmword ptr [rip + .LCPI3_5]
.LBB3_2:
movdqu xmmword ptr [rax + 2*rcx - 64], xmm0
movdqa xmm6, xmm0
paddw xmm6, xmm1
movdqu xmmword ptr [rax + 2*rcx - 48], xmm6
movdqa xmm6, xmm0
paddw xmm6, xmm2
movdqu xmmword ptr [rax + 2*rcx - 32], xmm6
movdqa xmm6, xmm0
paddw xmm6, xmm3
movdqu xmmword ptr [rax + 2*rcx - 16], xmm6
movdqa xmm6, xmm0
paddw xmm6, xmm4
movdqu xmmword ptr [rax + 2*rcx], xmm6
paddw xmm0, xmm5
add rcx, 40
cmp rcx, 10032
jne .LBB3_2
mov esi, 20000
mov edx, 2
mov rdi, rax
call __rust_dealloc@PLT
add rsp, 48
pop rbp
ret
.LBB3_4:
mov rax, qword ptr [rbp - 24]
movups xmm0, xmmword ptr [rbp - 16]
movaps xmmword ptr [rbp - 48], xmm0
mov qword ptr [rbp - 24], rax
movaps xmm0, xmmword ptr [rbp - 48]
movups xmmword ptr [rbp - 16], xmm0
lea rdi, [rbp - 24]
call __rust_oom@PLT
ud2
后一部分 (LBB3_4) 是 OOM 处理,因此从未使用过。因此,执行流程为:
example::replace_fill
,执行分配 + 初始设置,.LBB3_2
这是循环。注意事项有2个:
Vec
代码出现,另一方面,push_fill
有点复杂:
example::push_fill:
push rbp
mov rbp, rsp
push r15
push r14
push rbx
sub rsp, 40
lea rdx, [rbp - 48]
mov edi, 20000
mov esi, 2
call __rust_alloc@PLT
mov rcx, rax
test rcx, rcx
je .LBB2_11
mov qword ptr [rbp - 48], rcx
mov qword ptr [rbp - 40], 10000
mov qword ptr [rbp - 32], 0
xor r15d, r15d
lea r14, [rbp - 48]
xor esi, esi
.LBB2_2:
mov ebx, r15d
add bx, 1
cmovb bx, r15w
jb .LBB2_3
cmp rsi, qword ptr [rbp - 40]
jne .LBB2_9
mov rdi, r14
call <alloc::raw_vec::RawVec<T, A>>::double
mov rcx, qword ptr [rbp - 48]
mov rsi, qword ptr [rbp - 32]
.LBB2_9:
mov word ptr [rcx + 2*rsi], r15w
mov rsi, qword ptr [rbp - 32]
inc rsi
mov qword ptr [rbp - 32], rsi
movzx eax, bx
cmp eax, 10000
mov r15w, bx
jb .LBB2_2
.LBB2_3:
mov rsi, qword ptr [rbp - 40]
test rsi, rsi
je .LBB2_5
add rsi, rsi
mov rdi, qword ptr [rbp - 48]
mov edx, 2
call __rust_dealloc@PLT
.LBB2_5:
add rsp, 40
pop rbx
pop r14
pop r15
pop rbp
ret
.LBB2_11:
movups xmm0, xmmword ptr [rbp - 40]
movaps xmmword ptr [rbp - 64], xmm0
movaps xmm0, xmmword ptr [rbp - 64]
movups xmmword ptr [rbp - 40], xmm0
lea rdi, [rbp - 48]
call __rust_oom@PLT
ud2
mov rbx, rax
lea rdi, [rbp - 48]
call core::ptr::drop_in_place
mov rdi, rbx
call _Unwind_Resume@PLT
ud2
更多的 block ,意味着更多的分支,在循环的每次迭代中检查是否超出容量,...
不过,以上示例都不是惯用的。
我会这样写:
#[inline(never)]
pub fn extend_fill() {
let mut v = Vec::new();
v.extend(0u16..10000);
}
此方法来自于实现 Extend
trait .当与信任长度迭代器(如这个迭代器)一起使用时,它将在必要时执行单个“增长”步骤,然后推送而无需再次检查。
程序集不像 replace_fill
那样精简,但看起来仍然不错:
example::extend_fill:
push rbp
mov rbp, rsp
sub rsp, 64
mov qword ptr [rbp - 24], 2
xorps xmm0, xmm0
movups xmmword ptr [rbp - 16], xmm0
lea rdx, [rbp - 48]
mov edi, 20000
mov esi, 2
call __rust_alloc@PLT
test rax, rax
je .LBB4_7
mov qword ptr [rbp - 24], rax
mov qword ptr [rbp - 16], 10000
xor ecx, ecx
movdqa xmm0, xmmword ptr [rip + .LCPI4_0]
movdqa xmm1, xmmword ptr [rip + .LCPI4_1]
jmp .LBB4_2
.LBB4_6:
movd xmm2, edx
pshuflw xmm2, xmm2, 0
pshufd xmm2, xmm2, 80
movdqa xmm3, xmm2
paddw xmm3, xmm0
paddw xmm2, xmm1
movdqu xmmword ptr [rax + 2*rcx + 32], xmm3
movdqu xmmword ptr [rax + 2*rcx + 48], xmm2
add rdx, 16
mov rcx, rdx
.LBB4_2:
movd xmm2, ecx
pshuflw xmm2, xmm2, 0
pshufd xmm2, xmm2, 80
movdqa xmm3, xmm2
paddw xmm3, xmm0
paddw xmm2, xmm1
movdqu xmmword ptr [rax + 2*rcx], xmm3
movdqu xmmword ptr [rax + 2*rcx + 16], xmm2
lea rdx, [rcx + 16]
cmp rdx, 10000
jne .LBB4_6
mov qword ptr [rbp - 8], 10000
mov rsi, qword ptr [rbp - 16]
test rsi, rsi
je .LBB4_5
add rsi, rsi
mov rdi, qword ptr [rbp - 24]
mov edx, 2
call __rust_dealloc@PLT
.LBB4_5:
add rsp, 64
pop rbp
ret
.LBB4_7:
movups xmm0, xmmword ptr [rbp - 40]
movaps xmmword ptr [rbp - 64], xmm0
movaps xmm0, xmmword ptr [rbp - 64]
movups xmmword ptr [rbp - 40], xmm0
lea rdi, [rbp - 48]
call __rust_oom@PLT
ud2
我鼓励您尝试一下,并在总体上熟悉 Rust 迭代器:甜美的代码、良好的性能,它们是您需要的工具。
关于vector - 为什么通过替换默认值填充 Vec 比填充具有预设容量的东西快得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49519504/
padding:initial 比 padding:0 有什么优势吗?示例: textarea { padding: 0; } Hello, world! 最佳答案 它们的意思是一
我尝试通过按钮填充 JList,然后在先前填充的 Jlist 上使用 DoubleClick 填充 JTextField。 代码: private void extractUsedVariables
我正在尝试做 var width = ($(this).width() + $(this).css('padding-left') + $(this).css('padding-right' ));
我在导航中添加了悬停效果,遗憾的是悬停也影响了上面的文字。如何在不影响文本位置的情况下向导航添加悬停? 可悲的是,我找不到解决这个问题的方法。 HTML 模板:http://projects.help
我是 F# 初学者,下面代码中的 %-5s 和 %5s 有什么作用?我认为它提供了空间填充,但我不确定它是如何填充的? printfn "%-5s %5s" "a" "b" 当我尝试 prin
我需要选择带狗的用户(带 type 等于“狗”的宠物) var User = Waterline.Collection.extend({ identity: 'user', attribute
我一直在尝试让 Excel 在一组列上应用公式,然后将模式扩展到整个行集。 这导致了以下代码: For i = 0 To avgsheetNames.Count - 1 If Contains(CSt
随着 Flutter 2.0 的发布,FlatButton已被替换为 TextButton . 因此,填充属性不再直接可用,而是作为 ButtonStyle属性(property)。 我的问题是,我该
这似乎是一个简单的问题,但我已经尝试了一个小时,似乎无法弄清楚。 我要做的就是用 Canvas 填充 MainWindow。我找不到任何允许这样做的属性,我能想到的唯一方法是设置 Canvas.Wid
这是a website具有移动 View 。 网站宽度为 640 像素,但 iPhone 以 678 像素渲染文档。在 Android 中看起来很棒。 我添加了视口(viewport)元: 主体 C
我正在使用 GridBagLayout到(当前)显示两行。我知道这种布局对于这项任务来说太过分了,但我正在努力学习如何使用它。问题是我已将两个面板添加到两个单独的行中,并且内容周围存在巨大差距(请参见
我有以下代码已传递给我并创建多边形: var map; function initialize() { var myLatlng = new google.maps.LatLng(-36.4
我在 Jpanel 中有一些项目,然后将其推到顶部并用作基本搜索引擎的工具栏。我遇到一个问题,因为没有足够的空间,所以我的最后一个组合框没有显示。但是,左侧有很多空白空间,我需要移动所有内容来填充 J
我创建了带有阈值的二进制图像。如下图所示如何改变白色形状的颜色以使其可索引? 到目前为止,这是我的代码: void threshold() { cv::Mat src_8uc3_img = c
我有一个 JTable,我想知道是否有更好的方法来填充它,这是我的代码: //Metodo para llenar un jtable con datos de la base public stat
我想要做的是裁剪一个卷以删除所有不相关的数据。例如,假设我有一个 100x100x100 的体积,其中填充了 0,但其中的 50x50x50 体积则填充了 1。如何从原始体积中获得裁剪后的 50x50
因此,我正在创建一种对一组数字进行洗牌的方法,其想法是创建这些数字的总体。因此,我创建了一个循环,对数字进行洗牌,然后将其添加到数组列表中,但是经过一些调试语句后,我发现它确实对数字进行洗牌,但只将最
假设我有这两个类: public class A where T : IEntityWithID, new() { private static EntityInfo entityInfo =
我正在尝试添加用户输入的两个大整数作为字符串。当两个输入字符串的长度不同时,我尝试用零填充较短的数字,但它不起作用。因此,如果我输入 456 和 7,它会给出 3,前面有一些随机字符。感谢您的任何建议
这是我将内容打印到表格 View 的代码 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: Index
我是一名优秀的程序员,十分优秀!