Почему программа, которая обновляет большой статический массив с плавающей точкой медленнее в Rust, чем в C?



performance (1)

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 with pattern) в память. Это означает, что ваша ОС, запускающая приложение, скорее всего, отображает диапазон и полагается на виртуальную память, чтобы делать правильные вещи.

Если вы измените код для запуска того же цикла перед измерением, время выполнения значительно снизится. Это на самом деле быстрее, чем версия 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();

Я написал простую программу для сравнения производительности 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 <sys/time.h>
#include <stdio.h>

#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 < STREAM_ARRAY_SIZE; j++)
    {
        A[j] = 1.0;
    }

    int t = mysecond();
    for (j = 0; j < STREAM_ARRAY_SIZE; j++)
    {
        A[j] = 2.0E0 * A[j];
    }
    printf("%d us.\n", mysecond() - t);
    return 0;
}

Скомпилируйте и запустите его с помощью -O0 и -O2 :

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

Оптимизированная версия Rust сравнивается с gcc -O0 и очень слаба по сравнению с gcc -O2 . Это разумно? Как я могу улучшить производительность версии Rust?





rust