LINUX.ORG.RU

Правильно ли я пишу на Rust?

 , ,


1

7

Часто мелькает Rust и в новостях, и в темах. Решил попробовать переписать один тест с С на Rust. Для сравнения написал вариант и на C++. На Rust получилось в 4+ раза медленнее, чем на С и в 2+ раза медленнее, чем на C++. Есть подозрение, что я делаю что-то неправильно, но не знаю что. Помогите, пожалуйста, разобраться.

UPD. Мои цифры:

$ gcc c_v1.c -Ofast -march=native
$ ./a.out 3000
16.439091
-287.250083
$ g++ cpp_v2.cpp -Ofast -march=native
$ ./a.out 3000
31.3826
-287.25
$ rustc rust_v1.rs -C opt-level=3 -C target-cpu=native
$ ./rust_v1 3000
71.570172703s
-287.2500833333321
★★★

Последнее исправление: andalevor (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Ну куда уж им до анонимов с лора.

Это не помешает мне выразить вам РЕСПЕКТ за ваши разработки.

anonymous
()
Ответ на: комментарий от RazrFalcon

Ещё в 4? Кроссплатформенный гуй в 100к строк? Насмешил.

FLTK:

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C++                            197          11410          20332          59592
C/C++ Header                    58            370           1929          28353
C                               29            832           1601           6274
Objective C++                    4            494            881           5081
make                             2             66             45            720
CMake                            1             51             21            308
Bourne Shell                     3             55             45            249
-------------------------------------------------------------------------------
SUM:                           294          13278          24854         100577

И это тоже с «лишним» кодом и возможностями, которые не обязательно реализовывать, особенно на старте. А если пойти по пути CAPI, то можно и еще компактнее сделать.

anonymous
()
Ответ на: комментарий от RazrFalcon

Вы бы ещё мотиф из склепа достали.

FLTK работает на последних версиях всех актуальных ОС и умеет в HiDPI. Так что склепы бывают разные, а в каком-то из них возможно даже сидят хипстеры-мечтатели.

anonymous
()
Ответ на: комментарий от RazrFalcon

У некоторых и gopher работает. Не нужно им уподобляться

А у некоторых вообще ничего нет, а гонору столько, будто они уже все давно сделали и все во всем понимают. Не нужно им уподобляться.

anonymous
()
Ответ на: комментарий от anonymous

Ты чона ты чона! у нас борроу чекер есть! безопасность! стабильность! лучше чем %имязыка%! ррря! векторную библиотечку переписали! теперь надежно! редокс! мозила!

anonymous
()

Вот тебе для коллекции fortran.f03:

!!  gfortran -std=f2003 -Ofast -march=native

program matrix_mult

    implicit none

    integer, parameter :: dp = kind(1.d0)
    character(100) :: argv
    integer :: n, IOSTATUS
    real(dp), dimension(:, :), allocatable  :: AA, BB, CC
    real(dp) :: start, finish

    if(command_argument_count() .eq. 1) then
        call get_command_argument(1, argv)
        read(argv, *, IOSTAT=IOSTATUS) n
        if (IOSTATUS .ne. 0) then
            write(*, *) 'Error: Comman-line argument must be integer. Stop.'
            stop
        endif
    else
        write(*, *) 'Error: One command-line argument is required. Stop.'
        stop
    end if

    allocate(AA(n, n))
    allocate(BB(n, n))
    allocate(CC(n, n))

    call matrix_fill(AA, n)
    call matrix_fill(BB, n)

    call matrix_print(AA, n)

    call cpu_time(start)
    call matrix_prod(AA, BB, CC, n)

    call cpu_time(finish)
    print '("time: ", f10.3, " seconds")', (finish - start)
    write(*, '(f12.6)') CC(n/2 + 1, n/2 + 1)

    deallocate(AA)
    deallocate(BB)
    deallocate(CC)

    contains

        subroutine matrix_fill(A, n)

            implicit none

            real(dp), dimension(:, :), intent(out) :: A
            integer, intent(in) :: n

            real(dp) :: tmp
            integer :: i, j

            tmp = 1.0_dp / n / n

            do j = 0, n-1
                do i = 0, n-1
                    A(i+1, j+1) = tmp * (j - i) * (j + i)
                end do
            end do

        end subroutine matrix_fill


        subroutine matrix_print(A, n)

            implicit none

            real(dp), dimension(:, :), intent(in) :: A
            integer, intent(in) :: n

            integer :: j

            if ( n .le. 10 ) then
                do j = 1, n
                    write(*, '(*(f12.6))') A( : , j )
                end do
            else
                print '("Matrix is too large to print, n = ", (i4))', n
            end if

        end subroutine matrix_print


        subroutine matrix_prod(A, B, C, n)

            implicit none

            real(dp), dimension(:, :), intent(in) :: A, B
            real(dp), dimension(:, :), intent(out) :: C
            integer, intent(in) :: n

            real(dp), dimension(:, :), allocatable :: T
            integer :: i, j

            allocate(T(n, n))
            T = TRANSPOSE(B)

            do j = 1, n
                do i = 1, n
                    C(i, j) = sum( A(:, i) * T(:, j) )
                end do
            end do

            deallocate(T)

        end subroutine matrix_prod

end program matrix_mult

Для gfortran из mingw64 при n = 3000 работает либо так же, либо на 0.5 секунды медленнее варианта на C, при временах ~13 секунд. Если использовать для перемножения встроенную функцию matmult, то ответ выдаёт через ~3 секунды.

Если хочется распаралеллить, то внутри реализации процедуры subroutine matrix_prod(A, B, C, n) вместо do j = 1,n пишешь do concurrent (j = 1:n), а при сборке вместо флага -std=2003 указываешь флаг -std=2008 -ftree-parallelize-loops=4, где 4 - колличество потоков. Будет выполняться секунд за 9.

grem ★★★★★
()
Последнее исправление: grem (всего исправлений: 1)
Ответ на: комментарий от grem

Только надо проверить на матрицах с ненулевой диагональю. Кажется, учитывая, как заведены массивы, вместо sum( A(:, i) * T(:, j) ) всё-таки должно быть sum( A(:, j) * T(:, i) ).

grem ★★★★★
()

Что напрограммировать зависит от постановки задачи.

Предположим, цель соответствует тому, что мы на экран выводим. Т.е. значение ячейки в результате. Тогда нафейхоа весь этот огород? Заполнять матрицу целиком - зачем? Чтобы потом работать с произвольным значением в результирующей матрице? С каждым значением? С определенными значениями? Нам нужна результирующая матрица целиком, апатамушта? Короче подробности в студию.

olelookoe ★★★
()
Ответ на: комментарий от grem

Исправление для приведённой реализации:

--- a/fortran.f03	2019-10-04 00:00:00.000000000 +0300
+++ b/fortran.f03	2019-10-04 00:00:00.000000000 +0300
@@ -58,7 +58,7 @@
 
             do j = 0, n-1
                 do i = 0, n-1
-                    A(i+1, j+1) = tmp * (j - i) * (j + i)
+                    A(i+1, j+1) = tmp * (i - j) * (i + j)
                 end do
             end do
 
@@ -72,11 +72,11 @@
             real(dp), dimension(:, :), intent(in) :: A
             integer, intent(in) :: n
 
-            integer :: j
+            integer :: i
 
             if ( n .le. 10 ) then
-                do j = 1, n
-                    write(*, '(*(f12.6))') A( : , j )
+                do i = 1, n
+                    write(*, '(*(f12.6))') A( i , : )
                 end do
             else
                 print '("Matrix is too large to print, n = ", (i4))', n
@@ -97,12 +97,14 @@
             integer :: i, j
 
             allocate(T(n, n))
-            T = TRANSPOSE(B)
+            do j = 1, n
+                T( :, j) = A(j, :)
+            end do
 
             do j = 1, n
             !do concurrent (j = 1:n)
                 do i = 1, n
-                    C(i, j) = sum( A(:, i) * T(:, j) )
+                    C(i, j) = sum( T(:, i) * B(:, j) )
                 end do
             end do
 

Также дополнительно заменил встроенную функцию TRANSPOSE на реализацию в цикле.

Перетестировал заново в другом месте для gfortran-8.3.0 (запускал в цикле 10 раз, привожу худшее и лучшее время; дополнительный вывод убран):

$ gfortran -std=f2003 -Ofast -march=native -o a_f
$ for ((i=0;i<10;i++)); do ./a_f 3000; done
...
worst time: 16.820 sec
best time: 16.663 sec

Собранный там же gcc-8.3.0 вариант:

gcc -std=c11 -Ofast -march=native matr_mult.c -o a_c
...
worst time: 16.054532 sec
best time: 14.896423 sec

Время от запуска к запуску цикла почему-то держится либо около 16, либо всё время около 15-15.5 секунд.

Если взять компилятор PGI community edition 19.4 (полн. уст. ~ 8 gb) pgfortran-nollvm:

$ /opt/pgi/linux86-64-nollvm/19.4/bin/pgfortran -fast -tp=haswell matr_mult_test.f95 -o a_f_pgf
...
worst time: 13.493 sec
best time: 13.432 sec

Вариант pgfortran-llvm (что бы это значило?):

$ /opt/pgi/linux86-64-llvm/19.4/bin/pgfortran-llvm -fast -tp=haswell matr_mult_test.f95 -o a_f_pgf-llvm
...
worst time: 14.479 sec
best time: 14.317 sec
grem ★★★★★
()
Ответ на: комментарий от olelookoe

Да, лучше в конец добавить вывод хотя бы суммы всех элементов результирующей матрицы, а то мало ли что компилятор выкидывает при оптимизации.

grem ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.