为什么我的Julia实现比3D实现更快地计算3D中的欧几里德距离

时间:2017-07-16 06:35:27

标签: c compilation runtime julia

我正在比较Julia计算3D空间中两组点之间的欧氏距离与C中的等效实现所花费的时间。我非常惊讶地发现(对于这个特殊情况和我的特定实现)Julia比C快22%。当我在Julia版本中包含@fastmath时,它甚至比C快83%。

这引出了我的问题:为什么?朱莉娅比我最初认为的更令人惊讶我在做一些非常低效的事情。我把钱押在后者身上。

有关实施的一些细节:

  • 在朱莉娅,我使用Float64的二维数组。
  • 在C中,我使用动态分配的double的一维数组。
  • 在C语言中,我使用sqrt中的math.h函数。
  • 计算速度非常快,因此我计算它们1000次以避免在微/毫秒级别进行比较。

关于汇编的一些细节:

  • 编译器:gcc 5.4.0
  • 优化标记:-O3 -ffast-math

时序:

  • 朱莉娅(没有@fastmath):90秒
  • 朱莉娅(@fastmath):20 s
  • C:116 s
  • 我使用bash命令time作为计时
    • $ time ./particleDistance.jl(文件中有shebang)
    • $ time ./particleDistance

particleDistance.jl

#!/usr/local/bin/julia

function distance!(x::Array{Float64, 2}, y::Array{Float64, 2}, r::Array{Float64, 2})
    nx = size(x, 1)
    ny = size(y, 1)

    for k = 1:1000

        for j = 1:ny

            @fastmath for i = 1:nx
                @inbounds dx = y[j, 1] - x[i, 1]
                @inbounds dy = y[j, 2] - x[i, 2]
                @inbounds dz = y[j, 3] - x[i, 3]

                rSq = dx*dx + dy*dy + dz*dz

                @inbounds r[i, j] = sqrt(rSq)
            end

        end

    end

end

function main()
    n = 4096
    m = 4096

    x = rand(n, 3)
    y = rand(m, 3)
    r = zeros(n, m)

    distance!(x, y, r)

    println("r[n, m] = $(r[n, m])")
end

main()

particleDistance.c

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

void distance(int n, int m, double* x, double* y, double* r)
{
    int i, j, I, J;
    double dx, dy, dz, rSq;

    for (int k = 0; k < 1000; k++)
    {
        for (j = 0; j < m; j++)
        {
            J = 3*j;

            for (i = 0; i < n; i++)
            {
                I = 3*i;

                dx = y[J] - x[I];
                dy = y[J+1] - x[I+1];
                dz = y[J+2] - x[I+2];

                rSq = dx*dx + dy*dy + dz*dz;

                r[j*n+i] = sqrt(rSq);
            }
        }
    }
}

int main()
{
    int i;
    int n = 4096;
    int m = 4096;

    double *x, *y, *r;

    size_t xbytes = 3*n*sizeof(double);
    size_t ybytes = 3*m*sizeof(double);

    x = (double*) malloc(xbytes);
    y = (double*) malloc(ybytes);
    r = (double*) malloc(xbytes*ybytes/9);

    for (i = 0; i < 3*n; i++)
    {
        x[i] = (double) rand()/RAND_MAX*2.0-1.0;
    }

    for (i = 0; i < 3*m; i++)
    {
        y[i] = (double) rand()/RAND_MAX*2.0-1.0;
    }

    distance(n, m, x, y, r);

    printf("r[n*m-1] = %f\n", r[n*m-1]);

    free(x);
    free(y);
    free(r);

    return 0;
}

生成文件

all: particleDistance.c
    gcc -o particleDistance particleDistance.c -O3 -ffast-math -lm

2 个答案:

答案 0 :(得分:0)

也许它应该是一个评论,但关键是朱莉娅确实非常优化。在Julia网页中,你可以看到它在某些情况下可以击败C(曼德尔)。

我看到你在编译中使用了-ffast-math。但是,也许你可以在你的代码中做一些优化(虽然现在的编译器非常聪明,这可能无法解决问题)。

  1. 不要使用int作为索引,而是尝试使用unsigned int,这允许您尝试以下内容;
  2. 如果您使用未签名的,则可以进行移位并添加,而不是乘以3。这可以节省一些计算时间;
  3. 在访问像x [J]这样的元素时,可能会尝试直接使用指针并按顺序方式访问元素,如x + = 3(?);
  4. 尝试将它们设置为宏,而不是int n和int m。如果他们事先知道,你可以利用它。
  5. malloc在这种情况下有所不同吗?如果已知n和m,则固定大小的数组将减少操作系统分配内存所花费的时间。
  6. 可能还有其他一些东西,但是Julia已经通过实时编译进行了优化,因此所有不变且预先知道的东西都会被用来支持它。我没有后悔地试过朱莉娅。

答案 1 :(得分:0)

您在C中的指数计算相当缓慢

尝试类似下面的内容(我没有编译它,它可能仍然有错误,只是想象一下这个想法):

+---------------------+-----------+-----------+--------------+---------------------+----------------------+----------------------+
| Processing_Instance | Review_Id |   gmpi    |   Memberid   |     Review_Date     | attestation_fax_date | review_detail_status |
+---------------------+-----------+-----------+--------------+---------------------+----------------------+----------------------+
|               23760 |  11359973 | 650775278 | 300601690600 | 2017-03-30 00:00:00 | 2017-03-27 00:00:00  | Confirmed            |
|               23760 |  11359973 | 650775278 | 300601690600 | 2017-03-30 00:00:00 | 2017-03-27 00:00:00  | Confirmed            |
+---------------------+-----------+-----------+--------------+---------------------+----------------------+----------------------+

或者你可以尝试,它可能会更快一点(一个增量而不是3)

void distance(int n, int m, double* x, double* y, double* r)
{
int i, j;
double dx, dy, dz, rSq;
double* X, *Y, *R;


for (int k = 0; k < 1000; k++)
{
    R = r;
    Y = y;
    for (j = 0; j < m; j++)
    {
        X = x;

        for (i = 0; i < n; i++)
        {
            dx = Y[0] - *X++;
            dy = Y[1] - *X++;
            dz = Y[2] - *X++;

            rSq = dx*dx + dy*dy + dz*dz;

            *R++ = sqrt(rSq);
        }
        Y += 3;
    }
}
}

Y [x]与*(Y + x)相同。

祝你好运