Introduction Julia features Concluding remarks

The Julia Programming Language: A fresh approach to technical computing

Tim Schoof

1/20 Introduction Julia features Concluding remarks

Sharelatex1

A I Online LTEX editor

A I Complete modern LTEX environment =⇒ no installation needed

I Collaborative real-time editing =⇒ no version conflicts

I Eternal history =⇒ Go back to any point in time

I Bibliography search, comments, chat, github integration, and more

1 sharelatex.com 2/20 Introduction Julia features Concluding remarks

Zotero2

I Organize your bibliography

I Share downloaded papers with the group

I Download pdf and meta-data with a single click

I Full text search in meta-data and pdfs

2 https://www.zotero.org/ 3/20 Introduction Julia features Concluding remarks

History

I Work started 2009 at UC Santa Barbara and MIT (by Stefan Karpinski, Viral Shah, Jeff Bezanson, Alan Edelman)

I First public release 2012

I Julia Computing founded in 2015 (by inventors + Deepak Vinchhi and Keno Fischer)

I 2017 Julia Computing raises $4.6M in seed funding

4/20 Introduction Julia features Concluding remarks

Example 1: Calculate π with Monte Carlo

I Choose M random points within a square (uniformly distributed)

I Count points within enclosed circle, e.g., N

I It follows

N A π(d/2)2 ≈ = M A d2

N =⇒ π ≈ 4 M

I In picture π ≈ 3.15

5/20 Introduction Julia features Concluding remarks

Example 1: Calculate π in C++

1 #include #include 3 std::mt19937_64 rng; 5 std::uniform_real_distribution distUReal;

7 using namespace std;

9 double calc_pi(int M) { int sum = 0; 11 double x; double y; 13 for (int i = 0; i < M; ++i) { 15 x = distUReal(rng); y = distUReal(rng); 17 sum += x*x + y*y < 1; } 19 return 4.0 * sum / M; } 21 int main() 23 { int M = pow(10,9); 25 cout << calc_pi(M) << endl; }

6/20 Introduction Julia features Concluding remarks

Example 1: Calculate π in C++

#include 2 #include

4 std::mt19937_64 rng; std::uniform_real_distribution distUReal; 6 using namespace std; 8 double calc_pi(int M) { 10 int sum = 0;

I characterdouble count:x; 432 12 double y; I run time:for 19(int s i = 0; i < M; ++i) 14 { x = distUReal(rng); 16 y = distUReal(rng); sum += x*x + y*y < 1; 18 } return 4.0 * sum / M; 20 }

22 int main() { 24 int M = pow(10,9); cout << calc_pi(M) << endl; 26 }

6/20 Introduction Julia features Concluding remarks

Example 1: Calculate π in Python

from random import random 2 def calc_pi(M): 4 return 4*sum(random()**2 + random()**2 < 1 for i in range(M)) / M

6 print(calc_pi(10**9))

Much shorter due to high level code:

I no type declarations necessary

I predefined functions: sum,…

I range based for loops: for elem in

I generator expressions: (f(elem) for elem in )

7/20 Introduction Julia features Concluding remarks

Example 1: Calculate π in Python

from random import random 2 def calc_pi(M): 4 return 4*sum(random()**2 + random()**2 < 1 for i in range(M)) / M

6 print(calc_pi(10**9)) I character count: 135

I run time: 280 s Much shorter due to high level code:

I no type declarations necessary

I predefined functions: sum,…

I range based for loops: for elem in

I generator expressions: (f(elem) for elem in )

7/20 Introduction Julia features Concluding remarks

Example 1: Calculate π in Julia

calc_pi(M) = 4*sum(rand()^2 + rand()^2 < 1 for i=1:M) / M 2 println(calc_pi(10^9))

Even shorter syntax:

I many important functions imported by default

I inline function definitions: f(x) = 2x

I ranges: start:stop and start:step:stop

8/20 Introduction Julia features Concluding remarks

Example 1: Calculate π in Julia

calc_pi(M) = 4*sum(rand()^2 + rand()^2 < 1 for i=1:M) / M 2 println(calc_pi(10^9))

I character count: 83

EvenI run shorter time: 7 syntax: s

I many important functions imported by default

I inline function definitions: f(x) = 2x

I ranges: start:stop and start:step:stop

8/20 Introduction Julia features Concluding remarks

Example 2: Micro benchmarks

9/20 Introduction Julia features Concluding remarks

Example 2: Micro benchmarks

10/20 Introduction Julia features Concluding remarks

Example 3: Celeste project

I Locate and characterize light sources in the universe

I Sloan Digital Sky Survey

I 178 TB image data I 188 million stars and galaxies

I 1.54 PFLOPS peak performance

I 9300 nodes I 1.3 million threads on 650,000 KNL cores I <15 min runtime

I Julia first language since C/C++/Fortran reaching >1PFLOPS

11/20 I only inferable Julia code is fast I type-unstable code has Python like performance (or even slower due to missing optimizations) I see also performance tips in manual! (https://docs.julialang.org/en/latest/manual/performance-tips/)

I Aggrassive specialization on argument types

I Language design focused on speed

I avoids hard to optimize language features e.g., dictionary semantics for objects and scopes I machine integers (overflow!) I driven by LLVM’s possibilities from the start

I “Type-stable” standard library

Introduction Julia features Concluding remarks

How is that possible?

I Just-In-Time (JIT) compilation to native machine code

12/20 4 julia> @time f(1) # first call with an Int 0.001466 seconds (244 allocations: 17.041 KiB) 6 2 julia> @time f(1) # second call with an Int 8 0.000003 seconds (4 allocations: 160 bytes) 2 10 julia> @time f(2) # Only the type matters, not the value 12 0.000003 seconds (4 allocations: 160 bytes) 4 14 julia> @time f(1.0) # First call with a Float64 16 0.004278 seconds (215 allocations: 13.104 KiB) 2.0 18 julia> @time f(1.0) 20 0.000003 seconds (5 allocations: 176 bytes) 2.0 22 julia> @time f(2.0) 24 0.000003 seconds (5 allocations: 176 bytes) 4.0

Introduction Julia features Concluding remarks

Just-in-time (JIT) Compilation

A new function is compiled on first use for each combination of argument types3:

julia> f(x) = 2x 2 f (generic function with 1 method)

3 except for functions and types by default 13/20 11 julia> @time f(2) # Only the type matters, not the value 0.000003 seconds (4 allocations: 160 bytes) 13 4

15 julia> @time f(1.0) # First call with a Float64 0.004278 seconds (215 allocations: 13.104 KiB) 17 2.0

19 julia> @time f(1.0) 0.000003 seconds (5 allocations: 176 bytes) 21 2.0

23 julia> @time f(2.0) 0.000003 seconds (5 allocations: 176 bytes) 25 4.0

Introduction Julia features Concluding remarks

Just-in-time (JIT) Compilation

A new function is compiled on first use for each combination of argument types3:

1 julia> f(x) = 2x f (generic function with 1 method) 3 julia> @time f(1) # first call with an Int 5 0.001466 seconds (244 allocations: 17.041 KiB) 2 7 julia> @time f(1) # second call with an Int 0.000003 seconds (4 allocations: 160 bytes) 9 2

3 except for functions and types by default 13/20 15 julia> @time f(1.0) # First call with a Float64 0.004278 seconds (215 allocations: 13.104 KiB) 17 2.0

19 julia> @time f(1.0) 0.000003 seconds (5 allocations: 176 bytes) 21 2.0

23 julia> @time f(2.0) 0.000003 seconds (5 allocations: 176 bytes) 25 4.0

Introduction Julia features Concluding remarks

Just-in-time (JIT) Compilation

A new function is compiled on first use for each combination of argument types3:

1 julia> f(x) = 2x f (generic function with 1 method) 3 julia> @time f(1) # first call with an Int 5 0.001466 seconds (244 allocations: 17.041 KiB) 2 7 julia> @time f(1) # second call with an Int 0.000003 seconds (4 allocations: 160 bytes) 9 2

11 julia> @time f(2) # Only the type matters, not the value 0.000003 seconds (4 allocations: 160 bytes) 13 4

3 except for functions and types by default 13/20 Introduction Julia features Concluding remarks

Just-in-time (JIT) Compilation

A new function is compiled on first use for each combination of argument types3:

1 julia> f(x) = 2x f (generic function with 1 method) 3 julia> @time f(1) # first call with an Int 5 0.001466 seconds (244 allocations: 17.041 KiB) 2 7 julia> @time f(1) # second call with an Int 0.000003 seconds (4 allocations: 160 bytes) 9 2

11 julia> @time f(2) # Only the type matters, not the value 0.000003 seconds (4 allocations: 160 bytes) 13 4

15 julia> @time f(1.0) # First call with a Float64 0.004278 seconds (215 allocations: 13.104 KiB) 17 2.0

19 julia> @time f(1.0) 0.000003 seconds (5 allocations: 176 bytes) 21 2.0

23 julia> @time f(2.0) 0.000003 seconds (5 allocations: 176 bytes) 25 4.0

3 except for functions and types by default 13/20 Introduction Julia features Concluding remarks

Machine code

Julia compiles to native machine code very similar to C++

1 julia> @code_native f(1) .text 3 Filename: REPL[1] pushq %rbp 5 movq %rsp, %rbp Source line: 1 7 leaq (%rdi,%rdi), %rax popq %rbp 9 retq nopw (%rax,%rax) 11 julia> @code_native f(1.0) 13 .text Filename: REPL[1] 15 pushq %rbp movq %rsp, %rbp 17 Source line: 1 vaddsd %xmm0, %xmm0, %xmm0 19 popq %rbp retq 21 nopw (%rax,%rax)

14/20 I only inferable Julia code is fast I type-unstable code has Python like performance (or even slower due to missing optimizations) I see also performance tips in manual! (https://docs.julialang.org/en/latest/manual/performance-tips/)

I Language design focused on speed

I avoids hard to optimize language features e.g., dictionary semantics for objects and scopes I machine integers (overflow!) I driven by LLVM’s possibilities from the start

I “Type-stable” standard library

Introduction Julia features Concluding remarks

How is that possible?

I Just-In-Time (JIT) compilation to native machine code

I Aggrassive specialization on argument types

15/20 I only inferable Julia code is fast I type-unstable code has Python like performance (or even slower due to missing optimizations) I see also performance tips in manual! (https://docs.julialang.org/en/latest/manual/performance-tips/)

I “Type-stable” standard library

Introduction Julia features Concluding remarks

How is that possible?

I Just-In-Time (JIT) compilation to native machine code

I Aggrassive specialization on argument types

I Language design focused on speed

I avoids hard to optimize language features e.g., dictionary semantics for objects and scopes I machine integers (overflow!) I driven by LLVM’s possibilities from the start

15/20 I only inferable Julia code is fast I type-unstable code has Python like performance (or even slower due to missing optimizations) I see also performance tips in manual! (https://docs.julialang.org/en/latest/manual/performance-tips/)

Introduction Julia features Concluding remarks

How is that possible?

I Just-In-Time (JIT) compilation to native machine code

I Aggrassive specialization on argument types

I Language design focused on speed

I avoids hard to optimize language features e.g., dictionary semantics for objects and scopes I machine integers (overflow!) I driven by LLVM’s possibilities from the start

I “Type-stable” standard library

15/20 I Fixed by: f(x) = x > 0 ? x : zero(x)

Introduction Julia features Concluding remarks

Type stability for functions

I A function’s return type depends solely on its input types (not the values) I Example: f(x) = x > 0 ? x : 0

16/20 I Fixed by: f(x) = x > 0 ? x : zero(x)

Introduction Julia features Concluding remarks

Type stability for functions

I A function’s return type depends solely on its input types (not the values) I Example: f(x) = x > 0 ? x : 0

julia> @code_warntype f(1.0) 2 Variables: #self#::#f 4 x::Float64 fx::Float64 6 Body: 8 begin $(Expr(:inbounds, false)) 10 # meta: location operators.jl > 216 # meta: location float.jl < 482 12 fx::Float64 = (Base.sitofp)(Float64, 0)::Float64 # meta: pop location 14 # meta: pop location $(Expr(:inbounds, :pop)) 16 unless (Base.or_int)((Base.lt_float)(fx::Float64, x::Float64)::Bool, (Base.and_int)((Base.eq_float)(fx::Float64, x::Float64)::Bool, (Base.or_int)((Base.eq_float)(fx::Float64, 9.223372036854776e18)::Bool, (Base.slt_int)(0, (Base.fptosi)(Int64, fx::Float64)::Int64)::Bool)::Bool)::Bool)::Bool goto 10 return x::Float64 18 10: return 0 20 end::Union{Float64, Int64}

22 julia> Test.@inferred f(1.0) ERROR: return type Float64 does not match inferred return type 24 Union{Float64, Int64}

16/20 Introduction Julia features Concluding remarks

Type stability for functions

I A function’s return type depends solely on its input types (not the values) I Example: f(x) = x > 0 ? x : 0 I Fixed by: f(x) = x > 0 ? x : zero(x)

julia> @code_warntype f(1.0) 2 Variables: #self#::#f 4 x::Float64 fx::Float64 6 Body: 8 begin $(Expr(:inbounds, false)) 10 # meta: location operators.jl > 216 # meta: location float.jl < 482 12 fx::Float64 = (Base.sitofp)(Float64, 0)::Float64 # meta: pop location 14 # meta: pop location $(Expr(:inbounds, :pop)) 16 unless (Base.or_int)((Base.lt_float)(fx::Float64, x::Float64)::Bool, (Base.and_int)((Base.eq_float)(fx::Float64, x::Float64)::Bool, (Base.or_int)((Base.eq_float)(fx::Float64, 9.223372036854776e18)::Bool, (Base.slt_int)(0, (Base.fptosi)(Int64, fx::Float64)::Int64)::Bool)::Bool)::Bool)::Bool goto 10 return x::Float64 18 10: return (Base.sitofp)(Float64, 0)::Float64 20 end::Float64

16/20 I Fixed by: s = float(one(x))

julia> @code_warntype myexp(1.0f0, 10) 2 Variables: #self# 4 x::Float32 n::Int64 6 k::Int64 #temp#@_5::Int64 8 s::Float32 #temp#@_7::Float32 10 Body: 12 ...

Introduction Julia features Concluding remarks

Type stability for variables

I A variable only holds values of the same type I Example:

julia> function myexp(x, n=10) 2 s = 1 for k = 1:n 4 s += x^k/factorial(k) end 6 return s end

17/20 I Fixed by: s = float(one(x))

julia> @code_warntype myexp(1.0f0, 10) 2 Variables: #self# 4 x::Float32 n::Int64 6 k::Int64 #temp#@_5::Int64 8 s::Float32 #temp#@_7::Float32 10 Body: 12 ...

Introduction Julia features Concluding remarks

Type stability for variables

I A variable only holds values of the same type I Example:

julia> function myexp(x, n=10) 2 s = 1 for k = 1:n 4 s += x^k/factorial(k) end 6 return s end

1 julia> @code_warntype myexp(1, 10) Variables: 3 #self# x::Int64 5 n::Int64 k::Int64 7 #temp#@_5::Int64 s::Union{Float64, Int64} 9 #temp#@_7::Core.MethodInstance #temp#@_8::Float64 11 Body: 13 ...

17/20 Introduction Julia features Concluding remarks

Type stability for variables

I A variable only holds values of the same type I Example:

julia> function myexp(x, n=10) 2 s = 1 for k = 1:n 4 s += x^k/factorial(k) end 6 return s end

I Fixed by: s = float(one(x))

julia> @code_warntype myexp(1.0f0, 10) 2 Variables: #self# 4 x::Float32 n::Int64 6 k::Int64 #temp#@_5::Int64 8 s::Float32 #temp#@_7::Float32 10 Body: 12 ...

17/20 Introduction Julia features Concluding remarks

How is that possible?

I Just-In-Time (JIT) compilation to native machine code

I Aggrassive specialization on argument types

I Language design focused on speed

I avoids hard to optimize language features e.g., dictionary semantics for objects and scopes I machine integers (overflow!) I driven by LLVM’s possibilities from the start

I “Type-stable” standard library

I only inferable Julia code is fast I type-unstable code has Python like performance (or even slower due to missing optimizations) I see also performance tips in manual! (https://docs.julialang.org/en/latest/manual/performance-tips/)

18/20 Introduction Julia features Concluding remarks

Julia Features

I

I

I Dot broadcasting

I Meta programming

I Calling other languages

I GPU programming

19/20 I Compare with single dispatch: o.f(x)

I special syntax for first “argument” o I runtime type of o completely determines which function is called I fields and methods are defined within classes I classes often misused as namespaces

I Multiple dispatch: f(o, x)

I uniform syntax for all function calls I dispatch is determined by the types of all arguments I all arguments are equal I Only fields are defined in structs (abstract types cannot have fields) I methods can be defined everywhere I leads to very modular designs and higher code reuse I use modules to group methods

Introduction Julia features Concluding remarks

Multiple Dispatch

I Julias central programming paradigm

I Post-OOP

I Characteristic look and feel of the language

20/20 I Multiple dispatch: f(o, x)

I uniform syntax for all function calls I dispatch is determined by the types of all arguments I all arguments are equal I Only fields are defined in structs (abstract types cannot have fields) I methods can be defined everywhere I leads to very modular designs and higher code reuse I use modules to group methods

Introduction Julia features Concluding remarks

Multiple Dispatch

I Julias central programming paradigm

I Post-OOP

I Characteristic look and feel of the language

I Compare with single dispatch: o.f(x)

I special syntax for first “argument” o I runtime type of o completely determines which function is called I fields and methods are defined within classes I classes often misused as namespaces

20/20 Introduction Julia features Concluding remarks

Multiple Dispatch

I Julias central programming paradigm

I Post-OOP

I Characteristic look and feel of the language

I Compare with single dispatch: o.f(x)

I special syntax for first “argument” o I runtime type of o completely determines which function is called I fields and methods are defined within classes I classes often misused as namespaces

I Multiple dispatch: f(o, x)

I uniform syntax for all function calls I dispatch is determined by the types of all arguments I all arguments are equal I Only fields are defined in structs (abstract types cannot have fields) I methods can be defined everywhere I leads to very modular designs and higher code reuse I use modules to group methods

20/20 Introduction Julia features Concluding remarks

Multiple Dispatch example

I Multiple dispatch like C++ with runtime types

abstract type A end 2 foo(x::A, y::A) = 1

4 struct C1 <: A end foo(x::C1, y::A) = 2 6 foo(x::C1, y::C1) = 3

8 struct C2 <: A end

function static() 1 function dynamic() 2 c1 = C1() v = [C1(), C2()] c2 = C2() 3 for e1 in v 4 foo(c1, c1) for e2 in v foo(c1, c2) 5 println(foo(e1,e2)) 6 foo(c2, c1) end foo(c2, c2) 7 end 8 nothing end end static dynamic

Julia C++ Julia C++ C++ code foo(c1, c1) 3 3 3 2 foo(c1, c2) 2 2 2 2 foo(c2, c1) 1 1 1 1 foo(c2, c2) 1 1 1 1 21/20 Introduction Julia features Concluding remarks

Generic programming

I Julia functions are generic by default

function mynorm(v) v u N 2 s = abs(v[1])^2 uX kvk = t |v |2 for i in 2:length(v) i i=1 4 s += abs(v[i])^2 end 6 sqrt(s) end

22/20 Introduction Julia features Concluding remarks

Generic programming

I Julia functions are generic by default

function mynorm(v) v u N 2 s = abs(v[1])^2 uX kvk = t |v |2 for i in 2:length(v) i i=1 4 s += abs(v[i])^2 end 6 sqrt(s) end

1 julia> mynorm([1.0,2.0,3.0]) 3.7416573867739413

22/20 Introduction Julia features Concluding remarks

Generic programming

I Julia functions are generic by default

function mynorm(v) v u N 2 s = abs(v[1])^2 uX kvk = t |v |2 for i in 2:length(v) i i=1 4 s += abs(v[i])^2 end 6 sqrt(s) end

julia> mynorm([1.0,2.0,3.0]) 2 3.7416573867739413

4 julia> mynorm([1.0f0,2.0f0,3.0f0]) 3.7416575f0 6 julia> mynorm([big(1),big(2),big(3)]) 8 3.741657386773941385583748732316549301756019807778726946303745467320035156306928

10 julia> mynorm([1.0 + 1.0im, 2.0 + 2.0im, 3.0 + 3.0im]) 5.291502622129181

22/20 Introduction Julia features Concluding remarks

Generic programming

I Julia functions are generic by default

function mynorm(v) v u N 2 s = abs(v[1])^2 uX kvk = t |v |2 for i in 2:length(v) i i=1 4 s += abs(v[i])^2 end 6 sqrt(s) end

1 julia> using StaticArrays, Unitful.DefaultSymbols

3 julia> mynorm(SVector(1m, 2dm, 3cm)) 1.0202450685987166 m

22/20 Introduction Julia features Concluding remarks

Generic programming

I Julia functions are generic by default

function mynorm(v) v u N 2 s = abs(v[1])^2 uX kvk = t |v |2 for i in 2:length(v) i i=1 4 s += abs(v[i])^2 end 6 sqrt(s) end

julia> using SymPy 2 julia> @syms x positive=true y real=true z 4 (x, y, z)

6 julia> mynorm([sqrt(x), y, z]) ______8 / 2 2 \/ x + y + |z|

22/20 Introduction Julia features Concluding remarks

Generic programming

I Julia functions are generic by default more generic implementation

function mynorm(v) v u N 2 s = abs(v[1])^2 uX kvk = t |v |2 for i in 2:length(v) i i=1 4 s += abs(v[i])^2 end 6 sqrt(s) end

julia> using SymPy 2 julia> @syms x positive=true y real=true z 4 (x, y, z)

6 julia> mynorm([sqrt(x), y, z]) ______8 / 2 2 \/ x + y + |z|

22/20 Introduction Julia features Concluding remarks

Dot broadcasting

I Element-wise operations require a dot, e.g., v .* w v .+ 1 (A*B is matrix multiplication, v + 1 will be an error)

I Every scalar function f(x) can be applied element-wise with f.(v) → read as dotted call operator .(

I Examples:

v.^2 2 exp.(M) f(x) = x > 0 ? x : zero(x) 4 f.(v) strip.(split("a, b, c", ",")) 6 # or: split("a, b, c", ",") .|> strip

I Warning: M^2 / exp(M) might be matrix potentiation/exponentiation in future version

23/20 Introduction Julia features Concluding remarks

Dot fusion

I Multiple vectorized operations are fused into a single loop

I Loop fusion is a syntactic guarantee (might change in the future)

I Example

v = rand(10^6) 2 w = similar(v) f(x) = 3x^2 + 5x + 2 4 w .= f.(2 .* v.^2 .+ 6 .* v.^3 .- sqrt.(v))

I The last statement is allocation free!

I It can also be written as

@. w = f(2v^2 + 6v^3 - sqrt(v))

24/20 Introduction Julia features Concluding remarks

Meta programming

I Macros take an expression as input and replace it with a new expression

I Macro execution happens at compile time

I The new expression is then evaluated as normal

I Simple example

1 julia> @macroexpand @show f(x) quote 3 (Base.println)("f(x) = ", (Base.repr)(begin # show.jl, line 243: #1395#value = f(x) 5 end)) #1395#value 7 end

9 julia> @show f(x) f(x) = 10 11 10

I Useful to create Domain Specific Languages

1 @tensor D[a,b,c] = A[a,e,f,c,f,g]*B[g,b,e] + α*C[c,a,b]

25/20 Introduction Julia features Concluding remarks

Meta programming — generated functions

4 I The following function adds arrays of arbitrary dimensions

1 using Base.Cartesian

3 @generated function add!(out, a, b) N = ndims(out) 5 quote @nloops $N i out begin 7 @nref($N, out, i) = @nref($N, a, i) + @nref($N, b, i) end 9 end end

I Different version is compiled for each combination of input types I Seven dimensional input produces code like

for i_7 = indices(out, 7) 2 for i_6 = indices(out, 6) for i_5 = indices(out, 5) 4 for i_4 = indices(out, 4) for i_3 = indices(out, 3) 6 for i_2 = indices(out, 2) for i_1 = indices(out, 1) 8 out[i_1, i_2, i_3, i_4, i_5, i_6, i_7] = a[i_1, i_2, i_3, i_4, i_5, i_6, i_7] + b[i_1, i_2, i_3, i_4, i_5, i_6, i_7] 10 # Some output omitted

4 https://mikeinnes.github.io/2017/08/24/cudanative.html 26/20 Introduction Julia features Concluding remarks

Calling other languages

I C and Fortran can be called without glue code

I Example

I C function

int main(int argc, char **argv);

I Callable from Julia as

1 argv = [ "a.out", "arg1", "arg2" ] ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv)

I Interfaces for Python, Matlab, , …

27/20 Introduction Julia features Concluding remarks

Calling other languages

I C and Fortran can be called without glue code

I Example

I C function

int main(int argc, char **argv);

I Callable from Julia as

1 argv = [ "a.out", "arg1", "arg2" ] ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv)

I Interfaces for Python, Matlab, R, …

27/20 Introduction Julia features Concluding remarks

GPU programming

I Currently requires self compiled version of Julia

I Custom kernels with CUDAnative

using CuArrays, CUDAnative 2 xs, ys, zs = CuArray(rand(1024)), CuArray(rand(1024)), CuArray(zeros(1024)) 4 function kernel_vadd(out, a, b) 6 i = (blockIdx().x-1) * blockDim().x + threadIdx().x out[i] = a[i] + b[i] 8 return end 10 @cuda (1, length(xs)) kernel_vadd(zs, xs, ys)

I High level syntax with GPUArrays and dot broadcasting

1 zs .= xs.^2 .+ ys .* 2 sum(zs)

28/20 Introduction Julia features Concluding remarks

GPU programming — benchmark

I Speed comparable with CUDA C++ 5 I Partial port the Rodinia benchmark suite :

5 https://devblogs.nvidia.com/parallelforall/gpu-computing-julia-programming-language/ 29/20 Introduction Julia features Concluding remarks

When Julia shouldn’t be used

When one needs

I Language stability

I Stable release 1.0 “soon” (∼ until summer) I Breaking release 2.0 planned in a few years I Packages will need some time after 1.0 to stabilize I Deprecation warnings and FemtoCleaner ease transitions

I Fast startup times

I Adapt workflow: use REPL/Juno/VSCode and Revise.jl to keep Julia running I Might be improved with interpreter and delayed compilation in the future

I Real-time computing

I Garbage collector optimized for throughput instead of latency

30/20 Introduction Julia features Concluding remarks

The future

I Julia is rapidly growing

I 1.2 million downloads per month I Already more contributers (636 since 2012) than python (409 since 1990), numpy (573 since 2002), or scipy (550 since 2001)

I Julia blurs the distinction between users and developers

I Most code of the standard library and packages is written in Julia I @edit func(args) opens source code in editor I Pkg.checkout, Pkg.test, and PkgDev.submit help to create pull request to existing package I PkgDev.generate and PkgDev.register help to create new packages

I Compiler optimizations “barely scratched the surface”

I More optimizations are planned for 1.x versions

I Better tooling

I Improved package manager I Debugger

31/20 Introduction Julia features Concluding remarks

Summary

I Julia combines C-like speed with Python-like convenience

I Powerful features

I Multiple dispatch I Generic programming I Dot broadcasting I Meta programming I Calling other languages I GPU programming

I Try it yourself

I Completely open source (MIT license) I Single binary, no installation required

32/20 Introduction Julia features Concluding remarks

Thank you for your attention!

33/20 More generic implementation of norm Return

I The simple mynorm function …

I …works only for indexable collections I …assumes 1 is the first index I …fails on empty collections

I Use the iterator protocol to allow general iterable arguments

julia> function mynorm(it) 2 state = start(it) done(it, state) && return float(real(zero(eltype(it)))) 4 elem, state = next(it, state) sum = abs2(elem) 6 while !done(it, state) elem, state = next(it, state) 8 sum += abs2(elem) end 10 return sqrt(sum) end 12 julia> mynorm(i^2 for i=1:3) 14 9.899494936611665

34/20 Dispatch in C++ Return

#include 2 #include

4 // abstract base class 1 int main() { class A{ 6 public: 3 std::cout << "Static:" << std::endl; virtual int foo(A* a) { C1 c1 = C1(); 8 return 1; 5 C2 c2 = C2(); } std::cout << c1.foo(&c1) << std::endl; 10 }; 7 std::cout << c1.foo(&c2) << std::endl; std::cout << c2.foo(&c1) << std::endl; 12 // a concrete class with special behavior 9 std::cout << c2.foo(&c2) << std::endl; class C1 : public A{ 14 public: 11 std::cout << "Dynamic::" << std::endl; virtual int foo(A* a) { std::vector v {new C1(), new C2()}; 16 return 2; 13 for (auto e1: v) { } for (auto e2: v) { 18 virtual int foo(C1* a) { 15 std::cout << e1->foo(e2) << std::endl; return 3; } 20 } 17 } }; } 22 // a concrete class with default behavior 24 class C2 : public A{ };

35/20