If the zeros are known at compile time, LLVM may optimise away some 
unnecessary operations:

julia> foo(x) = x + 0*(x*x + 2)

foo (generic function with 1 method)


julia> @code_llvm foo(1)


define i64 @julia_foo_21664(i64) {

top:

  ret i64 %0

}

Unfortunately, this doesn't work as is with floating point numbers:

julia> @code_llvm foo(1.0)


define double @julia_foo_21665(double) {

top:

  %1 = fmul double %0, %0

  %2 = fadd double %1, 2.000000e+00

  %3 = fmul double %2, 0.000000e+00

  %4 = fadd double %3, %0

  ret double %4

}


The problem here is that there are a few "special" values that may not give 
the correct answer, e.g. Inf. However we can use the @fastmath macro to 
tell LLVM to ignore these cases:

julia> foo(x) = @fastmath x + 0*(x*x + 2)

foo (generic function with 1 method)


julia> @code_llvm foo(1.0)


define double @julia_foo_21656(double) {

top:

  ret double %0

}

Unfortunately, this doesn't work correctly for more general functions (such 
as exp), as LLVM doesn't know these don't have side-effects 
(see https://github.com/JuliaLang/julia/issues/414).

-Simon

On Friday, 3 July 2015 07:47:28 UTC+1, Jan Drugowitsch wrote:
>
> My aim is to write a Hamiltonian MCMC sampler that samples bound and drift 
> parameter posteriors for bounded diffusion models while assuming some 
> parametric form for how these bounds and the drift change over time. This 
> requires the first and second derivative of the first-passage densities 
> that are computed in 
> https://github.com/jdrugo/DiffModels.jl/blob/master/src/fpt.jl#L153. For 
> certain parametrization of drift and bound, partial derivatives will be one 
> or zero, which would make some parts of the code computing the full 
> derivatives drop out. To make this have an impact on performance, I thought 
> of two approaches:
>
>    - Write specialised functions for different parameterizations. This is 
>    the safe approach but would introduce many functions, as there are many 
>    possible combinations of drift and bound parameterizations. Also, it would 
>    not handle cases that I haven't thought of.
>    - Write a generic function that only evaluates the parts of the code 
>    that influence the final result. Currently, it seems that macros are the 
>    best way to achieve this. The advantages would be that it only requires me 
>    to write the function once, avoiding code replication and associated bugs. 
>    Also, it should be able to handle cases that I haven't thought of.
>
> Note that the code linked above currently does not compute the 
> derivatives. Due to several exp(.), the code that computes the derivatives 
> will be significantly longer, such that not evaluating parts of it should 
> lead to faster execution (not in the big-O sense, but reducing the 
> constant).
>
> Jan
>
> On Friday, 3 July 2015 01:51:19 UTC+2, Stefan Karpinski wrote:
>>
>> Can you elaborate on why that kind of code needs this unusual evaluation? 
>> It looks pretty reasonable as is to me.
>>
>> On Thu, Jul 2, 2015 at 5:20 PM, Jan Drugowitsch <[email protected]> wrote:
>>
>>> Is this a toy reduction of a concept that you want to apply in a much 
>>>> more complex way?
>>>>
>>>
>>> Yes, it was just meant to illustrate the concept.
>>>
>>> It should be applied to a function that has roughly the complexity of 
>>> https://github.com/jdrugo/DiffModels.jl/blob/master/src/fpt.jl#L153
>>> (in fact, it would be the first and second derivative of this function 
>>> with respect to parameters of drift and bounds).
>>>
>>> Jan 
>>>
>>> On Thu, Jul 2, 2015 at 11:09 AM, Jan Drugowitsch <[email protected]> 
>>>> wrote:
>>>>
>>>>> On Thursday, 2 July 2015 16:55:33 UTC+2, Yichao Yu wrote:
>>>>>>
>>>>>> On Thu, Jul 2, 2015 at 10:48 AM, Tom Breloff <[email protected]> 
>>>>>> wrote: 
>>>>>> > Just curious... is there a reason simply checking for non-zero 
>>>>>> isn't enough? 
>>>>>> > Readability? Performance? 
>>>>>> > 
>>>>>> > f(a,b,c) = (Bool(a) ? a * (b + c) : 0.0) 
>>>>>>
>>>>>> I'm guessing he want all code that gets his type automatically gets 
>>>>>> this behavior? If yes, I don't think there's anyway you can do that. 
>>>>>> If not, then just writing the branch or having a macro to rewrite 
>>>>>> that 
>>>>>> in your own code is probably the best solution. 
>>>>>>
>>>>>
>>>>> Indeed, the reason why I don't want to check for zeros and ones 
>>>>> explicitly is that some of these appear in inner loops and would reduce 
>>>>> performance.
>>>>>
>>>>> I already thought of macros as a possible solution, but I was 
>>>>> wondering if the same could be achieved in a more implicit/elegant way.
>>>>>
>>>>> Thanks,
>>>>> Jan
>>>>>  
>>>>>
>>>>>> > On Thursday, July 2, 2015 at 9:47:59 AM UTC-4, Jan Drugowitsch 
>>>>>> wrote: 
>>>>>> >> 
>>>>>> >> Dear Julia users, 
>>>>>> >> 
>>>>>> >> I am implementing an algorithm to solve a specific type of 
>>>>>> Volterra 
>>>>>> >> integral equation, and that simplifies significantly if some of 
>>>>>> its 
>>>>>> >> parameters are set to zero or one. The function implementing the 
>>>>>> algorithm 
>>>>>> >> takes quite a few arguments, such that writing specific versions 
>>>>>> for 
>>>>>> >> different arguments being zero/one would lead to too many 
>>>>>> different 
>>>>>> >> functions, which I would like to avoid. What I would rather like 
>>>>>> to do is to 
>>>>>> >> write one generic function and let the compiler prune different 
>>>>>> parts of the 
>>>>>> >> function, depending on the argument types. 
>>>>>> >> 
>>>>>> >> A minimal example of what I would like to do is 
>>>>>> >> 
>>>>>> >> immutable Zero <: Number; end 
>>>>>> >> 
>>>>>> >> const _zero = Zero() 
>>>>>> >> 
>>>>>> >> Base.promote_rule{T<:Number}(::Type{Zero}, ::Type{T}) = T 
>>>>>> >> Base.convert{T<:Number}(::Type{T}, ::Zero) = zero(T) 
>>>>>> >> 
>>>>>> >> *(::Zero, ::Zero) = _zero 
>>>>>> >> *(::Zero, ::Bool) = _zero 
>>>>>> >> *(::Bool, ::Zero) = _zero 
>>>>>> >> *(::Zero, ::Number) = _zero 
>>>>>> >> *(::Number, ::Zero) = _zero 
>>>>>> >> 
>>>>>> >> f(a, b, c) = a * (println("summing b + c"); b + c) 
>>>>>> >> 
>>>>>> >> println("Evaluating f(0, 1, 2)") 
>>>>>> >> f(0, 1, 2) 
>>>>>> >> println("Evaluating f(_zero, 1, 2)") 
>>>>>> >> f(_zero, 1, 2) 
>>>>>> >> 
>>>>>> >> (with Zero defined similar to 
>>>>>> >> https://groups.google.com/forum/#!topic/julia-users/0ab30bE8q6c) 
>>>>>> >> Running the above results in 
>>>>>> >> 
>>>>>> >> Evaluating f(0, 1, 2) 
>>>>>> >> summing b + c 
>>>>>> >> Evaluating f(_zero, 1, 2) 
>>>>>> >> summing b + c 
>>>>>> >> 
>>>>>> >> even though the result of the second "summing b + c" is discarded, 
>>>>>> and 
>>>>>> >> therefore wouldn't need to be evaluated. This is no surprise, as 
>>>>>> *(.,.) is a 
>>>>>> >> standard function that evaluates its operands before applying the 
>>>>>> function. 
>>>>>> >> Is there any way to change this behavior and turn *(.,.) into a 
>>>>>> function 
>>>>>> >> that performs short-circuit evaluation? If not, is there an 
>>>>>> alternative 
>>>>>> >> approach that achieves this without writing tons of specialized 
>>>>>> functions? 
>>>>>> >> 
>>>>>> >> Thanks, 
>>>>>> >> Jan 
>>>>>>
>>>>>
>>>>
>>

Reply via email to