热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么在Rust中比在C中更新大型静态浮点数组的程序更慢?

如何解决《为什么在Rust中比在C中更新大型静态浮点数组的程序更慢?》经验,为你挑选了1个好方法。

我写了一个简单的程序来比较Rust和C的性能.

Rust版本:

use std::time::Instant;

const STREAM_ARRAY_SIZE: usize = 10000000;
static mut A: [f64; STREAM_ARRAY_SIZE] = [1.0; STREAM_ARRAY_SIZE];

fn main() {
    let now = Instant::now();

    unsafe {
        for i in 0..STREAM_ARRAY_SIZE {
            A[i] = 2.0E0 * A[i];
        }
    }

    let duration = now.elapsed();
    println!("{}", (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) / 1000);
}

在调试和发布模式下运行它:

$ ./target/debug/calc
472046 us.
$ ./target/release/calc
62860 us.

与调试相比,发布版本具有显着的性能提升.

C版本做同样的事情并在同一台服务器上运行:

#include 
#include 

#define STREAM_ARRAY_SIZE   10000000

static double A[STREAM_ARRAY_SIZE];
int mysecond(void)
{
        struct timeval tp;
        struct timezone tzp;
        int i;

        i = gettimeofday(&tp,&tzp);
        return (tp.tv_sec * 1000000 + tp.tv_usec);
}

int main(void)
{
    int j = 0;
    for (j = 0; j 

使用-O0和编译并运行它-O2:

$ gcc test.c
$ ./a.out
41626 us.
$ gcc -O2 test.c
$ ./a.out
13499 us.

Rust优化版仅与之相比gcc -O0,并且与之相比非常弱gcc -O2.这合理吗?如何提高Rust版本的性能?



1> viraptor..:

Rust将循环编译为:

.LBB0_1:
    movupd  xmm0, xmmword ptr [rcx + 8*rax - 48]
    movupd  xmm1, xmmword ptr [rcx + 8*rax - 32]
    addpd   xmm0, xmm0
    addpd   xmm1, xmm1
    movupd  xmmword ptr [rcx + 8*rax - 48], xmm0
    movupd  xmmword ptr [rcx + 8*rax - 32], xmm1
    movupd  xmm0, xmmword ptr [rcx + 8*rax - 16]
    movupd  xmm1, xmmword ptr [rcx + 8*rax]
    addpd   xmm0, xmm0
    addpd   xmm1, xmm1
    movupd  xmmword ptr [rcx + 8*rax - 16], xmm0
    movupd  xmmword ptr [rcx + 8*rax], xmm1
    add rax, 8
    cmp rax, 100006
    jne .LBB0_1

而GCC 7.1.0编译为:

L6:
    movsd   (%rbx), %xmm0
    addq    $8, %rbx
    addsd   %xmm0, %xmm0
    movsd   %xmm0, -8(%rbx)
    cmpq    %rbp, %rbx
    jne     L6

Rust将数组放入数据部分,而C实际上(memset使用模式)写入内存.这意味着运行应用程序的操作系统可能只是映射范围并依赖虚拟内存来做正确的事情.

如果在测量之前更改代码以运行相同的循环,则运行时会显着下降.它实际上比我的机器上的C版本更快.(可能是由于该循环展开)

unsafe {
    for i in 0..STREAM_ARRAY_SIZE {
        A[i] = 2.0E0 * A[i];
    }
}

let now = Instant::now();

unsafe {
    for i in 0..STREAM_ARRAY_SIZE {
        A[i] = 2.0E0 * A[i];
    }
}

let duration = now.elapsed();


你可以在你答案的第一句话中说出生锈没有错并且基准不正确:).因此,在首先看一下你的答案之后,不会得到错误的印象.
什么生锈将数组的实际值一个接一个地放在它编译的结果二进制文件中.因此,当您启动二进制文件时,它们甚至可能无法从磁盘中完全读取.当您第一次阅读它们时,它们可能会导致需要处理的页面错误.C代替在开始时循环初始化整个数组,所以当你进入循环时,范围肯定是分配和写入的.使用我之前显示的命令编译并查看生成的文件的结尾.
推荐阅读
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 判断编码是否可立即解码的程序及电话号码一致性判断程序
    本文介绍了两个编程题目,一个是判断编码是否可立即解码的程序,另一个是判断电话号码一致性的程序。对于第一个题目,给出一组二进制编码,判断是否存在一个编码是另一个编码的前缀,如果不存在则称为可立即解码的编码。对于第二个题目,给出一些电话号码,判断是否存在一个号码是另一个号码的前缀,如果不存在则说明这些号码是一致的。两个题目的解法类似,都使用了树的数据结构来实现。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
author-avatar
fhuwiop
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有