为什么用户定义类型与Julia中的内置语言类型一样快速、紧凑?

为什么用户定义类型与Julia中的内置语言类型一样快速、紧凑?,julia,Julia,我已经查阅了很多文件,但我找不到一个具体的答案 Julia标准基元类型都是在语言本身中定义的,Julia允许您声明自己的基元类型,但我不知道这是否就是为什么程序员定义的类型与内置类型一样快速和紧凑的原因。更具体地说,我甚至检查了GitHub上的源代码 这就是数字类型int的定义,但我不敢进行推测。 有人能给我一个具体的答案吗?谢谢。这是一个很难准确回答的问题,涵盖了所有角落的情况 以下是我对一个简单的、希望不是过于简单的解释的看法: Julia代码被编译成本机汇编指令,然后执行。 如果Julia

我已经查阅了很多文件,但我找不到一个具体的答案

Julia标准基元类型都是在语言本身中定义的,Julia允许您声明自己的基元类型,但我不知道这是否就是为什么程序员定义的类型与内置类型一样快速和紧凑的原因。更具体地说,我甚至检查了GitHub上的源代码 这就是数字类型int的定义,但我不敢进行推测。
有人能给我一个具体的答案吗?谢谢。

这是一个很难准确回答的问题,涵盖了所有角落的情况

以下是我对一个简单的、希望不是过于简单的解释的看法:

Julia代码被编译成本机汇编指令,然后执行。 如果Julia编译器能够证明两个实现是等价的,那么您可以期望为它们生成相同的本机汇编指令。这不是100%正确,但根据我的经验,这是一个很好的近似值。 这意味着,在本机程序集级别,只要要执行的操作和编译器具有的类型信息相同,则无论您使用的是内置类型还是您自己的类型。 下面是一个简短的示例,我正在使用一个结构,但对于您自己的基本类型,情况也是如此:

struct A
    a::Int
end

function f(x::Int, n)
    s = Int[]
    for i in 1:n
        push!(s,x)
    end
    s
end

function f(x::A, n)
    s = Int[]
    for i in 1:n
        push!(s,x.a)
    end
    s
end

function f2(x::A, n)
    s = A[]
    for i in 1:n
        push!(s,x)
    end
    s
end
现在,如果运行@code_native f1、10^6、@code_native fA1、10^6和@code_native f2A1、10^6,您将看到生成的代码几乎相同

您可以在基准测试中看到这一点的影响:

julia> using BenchmarkTools

julia> @btime f(1, 10^6);
  8.567 ms (20 allocations: 9.00 MiB)

julia> @btime f(A(1), 10^6);
  8.528 ms (20 allocations: 9.00 MiB)

julia> @btime f2(A(1), 10^6);
  8.446 ms (20 allocations: 9.00 MiB)
您有相同的时间和相同的分配数量

但现在考虑以下定义:

struct C
    a
end

function f(x::C, n)
    s = Int[]
    for i in 1:n
        push!(s,x.a)
    end
    s
end
现在,对该功能进行基准测试可以得出:

julia> @btime f(C(1), 10^6);
  19.855 ms (21 allocations: 9.00 MiB)

在类型C字段中,a可以保存任何值,因此编译器无法证明x.a是Int,因此必须做一些额外的工作。您可以通过检查@code\u warntype fC1,10^6和@code\u warntype fA1,10^6来验证情况是否属实。

这是一个很难准确回答的问题,并且涵盖了所有情况

以下是我对一个简单的、希望不是过于简单的解释的看法:

Julia代码被编译成本机汇编指令,然后执行。 如果Julia编译器能够证明两个实现是等价的,那么您可以期望为它们生成相同的本机汇编指令。这不是100%正确,但根据我的经验,这是一个很好的近似值。 这意味着,在本机程序集级别,只要要执行的操作和编译器具有的类型信息相同,则无论您使用的是内置类型还是您自己的类型。 下面是一个简短的示例,我正在使用一个结构,但对于您自己的基本类型,情况也是如此:

struct A
    a::Int
end

function f(x::Int, n)
    s = Int[]
    for i in 1:n
        push!(s,x)
    end
    s
end

function f(x::A, n)
    s = Int[]
    for i in 1:n
        push!(s,x.a)
    end
    s
end

function f2(x::A, n)
    s = A[]
    for i in 1:n
        push!(s,x)
    end
    s
end
现在,如果运行@code_native f1、10^6、@code_native fA1、10^6和@code_native f2A1、10^6,您将看到生成的代码几乎相同

您可以在基准测试中看到这一点的影响:

julia> using BenchmarkTools

julia> @btime f(1, 10^6);
  8.567 ms (20 allocations: 9.00 MiB)

julia> @btime f(A(1), 10^6);
  8.528 ms (20 allocations: 9.00 MiB)

julia> @btime f2(A(1), 10^6);
  8.446 ms (20 allocations: 9.00 MiB)
您有相同的时间和相同的分配数量

但现在考虑以下定义:

struct C
    a
end

function f(x::C, n)
    s = Int[]
    for i in 1:n
        push!(s,x.a)
    end
    s
end
现在,对该功能进行基准测试可以得出:

julia> @btime f(C(1), 10^6);
  19.855 ms (21 allocations: 9.00 MiB)

在类型C字段中,a可以保存任何值,因此编译器无法证明x.a是Int,因此必须做一些额外的工作。您可以通过检查@code\u warntype fC1,10^6和@code\u warntype fA1,10^6来验证这种情况。

这是一个详细、有用答案的好例子!这是一个很好的例子,详细,有用的答案!