Lately I was interested in the switch from *O(n) *to *O(log n)* algorithm 
in *dequeue!(pq, key) *(PriorityQueue).
Issue: #8011

What I encountered there is some piece of code, which I see very often. 
Here it is 
the definition of an immutable type *Pair*:

    import Base.hash

    immutable Pair{A, B}
           a::A
           b::B
    end

    function hash(p::Pair, h::Uint)
           h = hash(p.a, h)
           hash(p.b, h)
    end

    hash(p::Pair) = hash(p, zero(Uint))

Now this is very obvious what it does define. So I asked myself why write 
it like that?
Why not write a generic dispatch hash function for type Pair?

    function hash{A,B}(p::Pair{A,B}, h::Uint=zero(Uint))
           h = hash(p.a, h)
           hash(p.b, h)
    end

This does exactly the same. About its readability is on others to decide. 
This is not my point.
My point is. I was interested in the generated code. The first common 
implementation yields 
this on my machine:

@code_llvm hash(p)


define %jl_value_t* @"julia_hash;98714"(%jl_value_t*, %jl_value_t**, i32) {
top:
  %3 = alloca [4 x %jl_value_t*], align 8
  %.sub = getelementptr inbounds [4 x %jl_value_t*]* %3, i64 0, i64 0
  %4 = getelementptr [4 x %jl_value_t*]* %3, i64 0, i64 2, !dbg !1353
  store %jl_value_t* inttoptr (i64 4 to %jl_value_t*), %jl_value_t** %.sub, 
align 8
  %5 = load %jl_value_t*** @jl_pgcstack, align 8, !dbg !1353
  %6 = getelementptr [4 x %jl_value_t*]* %3, i64 0, i64 1, !dbg !1353
  %.c = bitcast %jl_value_t** %5 to %jl_value_t*, !dbg !1353
  store %jl_value_t* %.c, %jl_value_t** %6, align 8, !dbg !1353
  store %jl_value_t** %.sub, %jl_value_t*** @jl_pgcstack, align 8, !dbg 
!1353
  store %jl_value_t* null, %jl_value_t** %4, align 8
  %7 = getelementptr [4 x %jl_value_t*]* %3, i64 0, i64 3
  store %jl_value_t* null, %jl_value_t** %7, align 8
  %8 = load %jl_value_t** %1, align 8, !dbg !1353
  store %jl_value_t* %8, %jl_value_t** %4, align 8, !dbg !1354
  %9 = call %jl_value_t* @jl_box_uint64(i64 0), !dbg !1354
  store %jl_value_t* %9, %jl_value_t** %7, align 8, !dbg !1354
  %10 = call %jl_value_t* @jl_apply_generic(%jl_value_t* inttoptr (i64 
140372045145152 to %jl_value_t*), %jl_value_t** %4, i32 2), !dbg !1354
  %11 = load %jl_value_t** %6, align 8, !dbg !1354
  %12 = getelementptr inbounds %jl_value_t* %11, i64 0, i32 0, !dbg !1354
  store %jl_value_t** %12, %jl_value_t*** @jl_pgcstack, align 8, !dbg !1354
  ret %jl_value_t* %10, !dbg !1354
}


(p here is Pair("Hello", 1))

Now the second implementation yields this:

@code_llvm hash(p)

define i64 @"julia_hash;98673"(%jl_value_t*) {
top:
  %1 = call i64 @"julia_hash;98654"(%jl_value_t* %0, i64 0), !dbg !1231, 
!julia_type !1233
  ret i64 %1, !dbg !1231
} 


The first one may be considered as quite a mouth full.

Now more code is not necessarily slow code, but this result came at a 
rather surprise to me.
It is obvious that the dispatching happens quite directly.

Why is this?

Here's my versioninfo:

Julia Version 0.4.0-dev+410
Commit 0e55472* (2014-08-28 03:59 UTC)
Platform Info:
  System: Darwin (x86_64-apple-darwin13.3.0)
  CPU: Intel(R) Core(TM) i7-2635QM CPU @ 2.00GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Sandybridge)
  LAPACK: libopenblas
  LIBM: libopenlibm
  LLVM: libLLVM-3.3


(I deleted my bank-account information, because it's of no interest here ;) 
)

Greetings

Stefan 

Reply via email to