Performance Julia中使用匿名函数的性能惩罚

Performance Julia中使用匿名函数的性能惩罚,performance,anonymous-function,julia,Performance,Anonymous Function,Julia,我注意到在Julia中使用匿名函数会带来性能损失。为了说明这一点,我有两个quicksort的实现(取自Julia发行版中的微性能基准)。第一个按升序排序 function qsort!(a,lo,hi) i, j = lo, hi while i < hi pivot = a[(lo+hi)>>>1] while i <= j while a[i] < pivot; i += 1; en

我注意到在Julia中使用匿名函数会带来性能损失。为了说明这一点,我有两个quicksort的实现(取自Julia发行版中的微性能基准)。第一个按升序排序

function qsort!(a,lo,hi)
    i, j = lo, hi
    while i < hi
        pivot = a[(lo+hi)>>>1]
        while i <= j
            while a[i] < pivot; i += 1; end
            while pivot < a[j]; j -= 1; end
            if i <= j
                a[i], a[j] = a[j], a[i]
                i, j = i+1, j-1
            end
        end
        if lo < j; qsort!(a,lo,j); end
        lo, j = i, hi
    end
    return a
end
问题是:这是因为编译器中的限制会及时解决,还是有一种惯用的方法来传递函子/匿名函数,在这种情况下应该使用

更新从答案来看,这似乎是将在编译器中解决的问题

同时,有两个建议的解决办法。这两种方法都相当简单,尽管它们开始感觉像是在C++中使用的那种类型的伪造(虽然不是在同样的尴尬程度上)。 第一个是@Toivo Henningsson建议的FastAnon方案。我没有尝试这种方法,但它看起来不错

我尝试了@simonstar建议的第二种方法,它给了我与非泛型qsort同等的性能!实施:

abstract OrderingOp
immutable AscendingOp<:OrderingOp end
immutable DescendingOp<:OrderingOp end
evaluate(::AscendingOp, x, y) = x<y
evaluate(::DescendingOp, x, y) = x>y

function qsort_generic!(a,lo,hi,op=AscendingOp())
    i, j = lo, hi
    while i < hi
        pivot = a[(lo+hi)>>>1]
        while i <= j
            while evaluate(op, a[i], pivot); i += 1; end
            while evaluate(op, pivot, a[j]); j -= 1; end
            if i <= j
                a[i], a[j] = a[j], a[i]
                i, j = i+1, j-1
            end
        end
        if lo < j; qsort_generic!(a,lo,j,op); end
        lo, j = i, hi
    end
    return a
end
abstract OrderingOp
不可变上升P>1]

而我这是一个问题,将在即将到来的类型系统大修中修复


更新:Julia的0.5版本中已经修复了此问题。

是的,这是由于编译器的限制,并且有计划修复此问题,请参见示例。同时,该包可能会提供一种变通方法


您所使用的方式看起来非常地道,不幸的是,您没有遗漏任何魔术(FastAnonymous软件包除外)。

正如其他人所指出的,您所编写的代码是地道的Julia,并且有一天会很快,但编译器还没有完全做到。除了使用FastAnonymous之外,另一个选项是传递类型而不是匿名函数。对于这个模式,您定义了一个不带字段的不可变项和一个方法(我们称之为
evaluate
),该方法接受该类型的实例和一些参数。然后,排序函数将接受
op
对象而不是函数,并调用
evaluate(op,x,y)
而不是
op(x,y)
。因为函数是专门针对其输入类型的,所以抽象没有运行时开销。这是标准库和中的基础,以及

例如:

immutable AscendingSort; end
evaluate(::AscendingSort, x, y) = x < y

function qsort_generic!(a,lo,hi,op=AscendingSort())
    i, j = lo, hi
    while i < hi
        pivot = a[(lo+hi)>>>1]
        while i <= j
            while evaluate(op, a[i], pivot); i += 1; end
            while evaluate(op, pivot, a[j]); j -= 1; end
            if i <= j
                a[i], a[j] = a[j], a[i]
                i, j = i+1, j-1
            end
        end
        if lo < j; qsort_generic!(a,lo,j,op); end
        lo, j = i, hi
    end
    return a
end
不可变递增排序;结束
计算(::递增排序,x,y)=x>>1]

虽然我感谢@Toivo,但很高兴知道这是一个将被修复的问题。我一直在想什么样的元编程魔法可以用来解决这个问题,看来FastAnonymous就是这个问题的答案。谢谢Stefan。很高兴知道,没有理由像这样的匿名函数不借助“小技巧”就不能快速运行。
abstract OrderingOp
immutable AscendingOp<:OrderingOp end
immutable DescendingOp<:OrderingOp end
evaluate(::AscendingOp, x, y) = x<y
evaluate(::DescendingOp, x, y) = x>y

function qsort_generic!(a,lo,hi,op=AscendingOp())
    i, j = lo, hi
    while i < hi
        pivot = a[(lo+hi)>>>1]
        while i <= j
            while evaluate(op, a[i], pivot); i += 1; end
            while evaluate(op, pivot, a[j]); j -= 1; end
            if i <= j
                a[i], a[j] = a[j], a[i]
                i, j = i+1, j-1
            end
        end
        if lo < j; qsort_generic!(a,lo,j,op); end
        lo, j = i, hi
    end
    return a
end
immutable AscendingSort; end
evaluate(::AscendingSort, x, y) = x < y

function qsort_generic!(a,lo,hi,op=AscendingSort())
    i, j = lo, hi
    while i < hi
        pivot = a[(lo+hi)>>>1]
        while i <= j
            while evaluate(op, a[i], pivot); i += 1; end
            while evaluate(op, pivot, a[j]); j -= 1; end
            if i <= j
                a[i], a[j] = a[j], a[i]
                i, j = i+1, j-1
            end
        end
        if lo < j; qsort_generic!(a,lo,j,op); end
        lo, j = i, hi
    end
    return a
end