{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m environment at `~/Dropbox/Presentations/2020-detroit-tech-watch-julia-intro/Project.toml`\n" ] } ], "source": [ "using Pkg\n", "Pkg.activate(\".\")\n", "Pkg.instantiate()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Intro to Julia\n", "\n", "## Robin Deits\n", "### Detroit Tech Watch\n", "### May 26, 2020\n", "\n", "Follow along at: https://tinyurl.com/tech-watch-julia [1]\n", "\n", "[1] https://github.com/rdeits/DetroitTechWatch2020.jl/blob/master/Intro%20to%20Julia.ipynb" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Who am I?\n", "\n", "* Former MIT PhD, now a robotics engineer at Boston Dynamics\n", " * The opinions expressed here are mine, not those of Boston Dynamics\n", "* Long-time Julia user:\n", " * Co-founder of JuliaRobotics: https://juliarobotics.org/\n", " * JuliaCon presenter: https://www.youtube.com/watch?v=dmWQtI3DFFo\n", " * Open-source contributor: https://github.com/rdeits/\n", " \n", "
\n", " \n", "
My day job
\n", "
\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Overview\n", "\n", "* What is Julia?\n", "* Tour of Julia\n", " * Functions, types, multiple dispatch\n", "* Julia is fast! \n", " * Benchmarking Julia vs. C and Python\n", "* Bonus Features of Julia\n", " * Async tasks, multiprocessing, and metaprogramming\n", "* What's hard to do in Julia?\n", "* Essential Julia packages and tools" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# What is Julia\n", "\n", "\n", "\n", "* Julia is a high-level language like Python with the performance of a fast language like C\n", "* Julia is a great choice for scientific computing, with:\n", " * Excellent performance\n", " * N-dimensional arrays\n", " * Parallel and distributed computing\n", "* And it's also a nice environment for general purpose programming, with:\n", " * An active ecosystem of packages and good tools for managing them\n", " * A rich type system\n", " * Iterators, asynchronous tasks, and coroutines\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "# Julia at a Glance\n", "\n", "* First public release in 2012, version 1.0 released in 2018\n", "* Free\n", " * Julia itself is BSD licensed\n", " * It bundles some GPLed libraries (which can be disabled if desired)\n", "* Built-in JIT compiler transforms Julia code to native assembly at run time\n", " * Uses LLVM under the hood\n", "* Garbage collected\n", "* Dynamically typed\n", "* Organized via multiple dispatch" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# A Tour of Julia" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## The Basics" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Arithmetic:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 + 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Strings:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello world\n" ] } ], "source": [ "# Strings\n", "println(\"hello world\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Arrays:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "x = [1, 2, 3, 4];" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Functions" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "say_hello (generic function with 1 method)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function say_hello(name)\n", " println(\"hello \", name)\n", "end" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello world\n" ] } ], "source": [ "say_hello(\"world\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "By default, a function is generic, so you can pass in any type you want:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello [1, 2, 3]\n" ] } ], "source": [ "say_hello([1, 2, 3])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Types\n", "\n", "Every value in Julia has a type:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Float64" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(1.0)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Int64" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(1)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Irrational{:π}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(π)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Array{Int64,1}" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof([1, 2, 3])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "You can create your own types to organize your data:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Person(\"Alice\")" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "struct Person\n", " name::String\n", "end\n", "\n", "alice = Person(\"Alice\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Julia's types are extremely lightweight, and user-defined types are *exactly* as performant as anything built-in:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sizeof(Person) == sizeof(Ptr{String})" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Multiple Dispatch\n", "\n", "Julia does not have classes like Java, Python, or C++. Instead, code is organized around *multiple dispatch*, where the compiler chooses the appropriate method of a given function based on the types of *all* of its input arguments. \n", "\n", "For more, see: [The Unreasonable Effectiveness of Multiple Dispatch (Stefan Karpinski, JuliaCon 2019)](https://www.youtube.com/watch?v=kc9HwsxE1OY)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "greet (generic function with 1 method)" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "greet(x, y) = println(\"$x greets $y\")" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Person(\"alice\") greets Person(\"bob\")\n" ] } ], "source": [ "alice = Person(\"alice\")\n", "bob = Person(\"bob\")\n", "\n", "greet(alice, bob)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Currently there is only one `greet()` function, and it will work on `x` and `y` of any type:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3] greets hello world\n" ] } ], "source": [ "greet([1, 2, 3], \"hello world\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can use abstract types to organize the behavior of related types:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "abstract type Animal end\n", "\n", "struct Cat <: Animal\n", " name::String\n", "end" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We've already defined `greet(x, y)` for any `x` and `y`, but we can add another definition for a more specific set of input types.\n", "\n", "We can be as specific or as general as we like with the argument types:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "greet (generic function with 2 methods)" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "greet(x::Person, y::Animal) = println(\"$x pats $y\")" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "greet (generic function with 3 methods)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "greet(x::Cat, y) = println(\"$x meows at $y\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Julia will always pick the *most specific* method that matches the provided function arguments." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Person(\"alice\") pats Cat(\"fluffy\")\n" ] } ], "source": [ "fluffy = Cat(\"fluffy\")\n", "\n", "greet(alice, fluffy)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cat(\"fluffy\") meows at Person(\"alice\")\n" ] } ], "source": [ "greet(fluffy, alice)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "greet (generic function with 6 methods)" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "struct Dog <: Animal\n", " name::String\n", "end\n", "\n", "greet(x::Dog, y) = println(\"$x barks at $y\")\n", "\n", "greet(x::Dog, y::Person) = println(\"$x licks $y's face\")\n", "\n", "greet(x::Dog, y::Dog) = println(\"$x sniffs $y's butt\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Person(\"alice\") pats Dog(\"fido\")\n" ] } ], "source": [ "fido = Dog(\"fido\")\n", "rex = Dog(\"rex\")\n", "\n", "greet(alice, fido)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dog(\"fido\") barks at Cat(\"fluffy\")\n" ] } ], "source": [ "greet(fido, fluffy)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dog(\"fido\") licks Person(\"bob\")'s face\n" ] } ], "source": [ "greet(fido, bob)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dog(\"fido\") sniffs Dog(\"rex\")'s butt\n" ] } ], "source": [ "greet(fido, rex)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "If you want to know which `greet` method will be called for a given set of arguments, you can use `@which` to check:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "greet(x::Person, y::Animal) in Main at In[18]:1" ], "text/plain": [ "greet(x::Person, y::Animal) in Main at In[18]:1" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@which greet(alice, fido)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "You can list all of the methods of a given function with `methods`:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "# 6 methods for generic function greet:" ], "text/plain": [ "# 6 methods for generic function \"greet\":\n", "[1] greet(x::Dog, y::Dog) in Main at In[22]:9\n", "[2] greet(x::Dog, y::Person) in Main at In[22]:7\n", "[3] greet(x::Person, y::Animal) in Main at In[18]:1\n", "[4] greet(x::Cat, y) in Main at In[19]:1\n", "[5] greet(x::Dog, y) in Main at In[22]:5\n", "[6] greet(x, y) in Main at In[14]:1" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "methods(greet)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Modules\n", "\n", "Modules in Julia are used to organize code into namespaces." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello world\n" ] } ], "source": [ "module MyUsefulModule\n", "\n", "export hello\n", "\n", "hello() = println(\"hello world\")\n", "goodbye() = println(\"goodbye world\")\n", "\n", "end\n", "\n", "MyUsefulModule.hello()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The `using` command brings any `export`ed symbols from a module into the current namespace:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello world\n" ] } ], "source": [ "using .MyUsefulModule\n", "hello()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Using Packages\n", "\n", "Julia has a built-in package manager called `Pkg`. It handles installing packages and managing all your package environments. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "A package *environment* represents a single set of installed packages. Let's activate the environment for this talk:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m environment at `~/Dropbox/Presentations/2020-detroit-tech-watch-julia-intro/Project.toml`\n" ] } ], "source": [ "using Pkg\n", "Pkg.activate(\".\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(this is similar to `source venv/bin/activate` in a Python virtual environment)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can install a package in our current environment. This will only affect that environment, so we can safely do this without breaking any other Julia projects we might be working on:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m registry at `~/.julia/registries/General`\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[?25l " ] }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m git-repo `git@github.com:JuliaRegistries/General.git`\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [> ] 0.0 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [======> ] 14.3 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [============> ] 28.6 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [==================> ] 42.9 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [=======================> ] 57.1 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [=============================> ] 71.4 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [===================================> ] 85.7 %\r", " \u001b[36m\u001b[1mFetching:\u001b[22m\u001b[39m [========================================>] 100.0 %\r", "\u001b[2K\u001b[?25h" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/Dropbox/Presentations/2020-detroit-tech-watch-julia-intro/Project.toml`\n", "\u001b[90m [no changes]\u001b[39m\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/Dropbox/Presentations/2020-detroit-tech-watch-julia-intro/Manifest.toml`\n", "\u001b[90m [no changes]\u001b[39m\n" ] } ], "source": [ "Pkg.add(\"Colors\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The `Project.toml` file gives a concise description of the packages we've added to this environment:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[deps]\n", "BenchmarkTools = \"6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf\"\n", "Colors = \"5ae59095-9a9b-59fe-a467-6f913c188581\"\n", "HTTP = \"cd3eb016-35fb-5094-929b-558a96fad6f3\"\n", "Plots = \"91a5bcdd-55d7-5caf-9e0b-520d859cae80\"\n", "ProgressMeter = \"92933f4c-e287-5a05-a399-4b506db050ca\"\n", "PyCall = \"438e738f-606a-5dbb-bf0a-cddfbfd45ab0\"\n" ] }, { "data": { "text/plain": [ "Process(`\u001b[4mcat\u001b[24m \u001b[4mProject.toml\u001b[24m`, ProcessExited(0))" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "run(`cat Project.toml`)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The package manager also generates a complete manifest of every package that is installed, including all the transitive dependencies and their versions. You can use this to reproduce a given package environment exactly:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "# This file is machine-generated - editing it directly is not advised\n", "\n", "[[Base64]]\n", "uuid = \"2a0f44e3-6c83-55bd-87e4-b1978d98bd5f\"\n", "\n", "[[BenchmarkTools]]\n", "deps = [\"JSON\", \"Logging\", \"Printf\", \"Statistics\", \"UUIDs\"]\n", "git-tree-sha1 = \"9e62e66db34540a0c919d72172cc2f642ac71260\"\n", "uuid = \"6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf\"\n", "version = \"0.5.0\"\n", "\n", "[[Bzip2_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"3663bfffede2ef41358b6fc2e1d8a6d50b3c3904\"\n", "uuid = \"6e34b625-4abd-537c-b88f-471c36dfa7a0\"\n", "version = \"1.0.6+2\"\n", "\n", "[[ColorSchemes]]\n", "deps = [\"ColorTypes\", \"Colors\", \"FixedPointNumbers\", \"Random\", \"StaticArrays\"]\n", "git-tree-sha1 = \"7a15e3690529fd1042f0ab954dff7445b1efc8a5\"\n", "uuid = \"35d6a980-a343-548e-a6ea-1d62b119f2f4\"\n", "version = \"3.9.0\"\n", "\n", "[[ColorTypes]]\n", "deps = [\"FixedPointNumbers\", \"Random\"]\n", "git-tree-sha1 = \"c73d9cfc2a9d8433dc77f5bff4bddf46b1d78c20\"\n", "uuid = \"3da002f7-5984-5a60-b8a6-cbb66c0b333f\"\n", "version = \"0.10.3\"\n", "\n", "[[Colors]]\n", "deps = [\"ColorTypes\", \"FixedPointNumbers\", \"InteractiveUtils\", \"Reexport\"]\n", "git-tree-sha1 = \"2fdeb981ebcf52cd800ddb6a0aa5eac34153552d\"\n", "uuid = \"5ae59095-9a9b-59fe-a467-6f913c188581\"\n", "version = \"0.12.0\"\n", "\n", "[[Conda]]\n", "deps = [\"JSON\", \"VersionParsing\"]\n", "git-tree-sha1 = \"7a58bb32ce5d85f8bf7559aa7c2842f9aecf52fc\"\n", "uuid = \"8f4d0f93-b110-5947-807f-2305c1781a2d\"\n", "version = \"1.4.1\"\n", "\n", "[[Contour]]\n", "deps = [\"StaticArrays\"]\n", "git-tree-sha1 = \"0b17db36e7e03f8437e0d1f55aea3e4a60c74353\"\n", "uuid = \"d38c429a-6771-53c6-b99e-75d170b6e991\"\n", "version = \"0.5.3\"\n", "\n", "[[DataAPI]]\n", "git-tree-sha1 = \"176e23402d80e7743fc26c19c681bfb11246af32\"\n", "uuid = \"9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a\"\n", "version = \"1.3.0\"\n", "\n", "[[DataStructures]]\n", "deps = [\"InteractiveUtils\", \"OrderedCollections\"]\n", "git-tree-sha1 = \"af6d9c86e191c917c2276fbede1137e8ea20157f\"\n", "uuid = \"864edb3b-99cc-5e75-8d2d-829cb0a9cfe8\"\n", "version = \"0.17.17\"\n", "\n", "[[Dates]]\n", "deps = [\"Printf\"]\n", "uuid = \"ade2ca70-3891-5945-98fb-dc099432e06a\"\n", "\n", "[[DelimitedFiles]]\n", "deps = [\"Mmap\"]\n", "uuid = \"8bb1440f-4735-579b-a4ab-409b98df4dab\"\n", "\n", "[[Distributed]]\n", "deps = [\"Random\", \"Serialization\", \"Sockets\"]\n", "uuid = \"8ba89e20-285c-5b6f-9357-94700520ee1b\"\n", "\n", "[[FFMPEG]]\n", "deps = [\"FFMPEG_jll\"]\n", "git-tree-sha1 = \"c82bef6fc01e30d500f588cd01d29bdd44f1924e\"\n", "uuid = \"c87230d0-a227-11e9-1b43-d7ebe4e7570a\"\n", "version = \"0.3.0\"\n", "\n", "[[FFMPEG_jll]]\n", "deps = [\"Bzip2_jll\", \"FreeType2_jll\", \"FriBidi_jll\", \"LAME_jll\", \"LibVPX_jll\", \"Libdl\", \"Ogg_jll\", \"OpenSSL_jll\", \"Opus_jll\", \"Pkg\", \"Zlib_jll\", \"libass_jll\", \"libfdk_aac_jll\", \"libvorbis_jll\", \"x264_jll\", \"x265_jll\"]\n", "git-tree-sha1 = \"0fa07f43e5609ea54848b82b4bb330b250e9645b\"\n", "uuid = \"b22a6f82-2f65-5046-a5b2-351ab43fb4e5\"\n", "version = \"4.1.0+3\"\n", "\n", "[[FixedPointNumbers]]\n", "git-tree-sha1 = \"3ba9ea634d4c8b289d590403b4a06f8e227a6238\"\n", "uuid = \"53c48c17-4a7d-5ca2-90c5-79b7896eea93\"\n", "version = \"0.8.0\"\n", "\n", "[[FreeType2_jll]]\n", "deps = [\"Bzip2_jll\", \"Libdl\", \"Pkg\", \"Zlib_jll\"]\n", "git-tree-sha1 = \"7d900f32a3788d4eacac2bfa3bf5c770179c8afd\"\n", "uuid = \"d7e528f0-a631-5988-bf34-fe36492bcfd7\"\n", "version = \"2.10.1+2\"\n", "\n", "[[FriBidi_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"2f56bee16bd0151de7b6a1eeea2ced190a2ad8d4\"\n", "uuid = \"559328eb-81f9-559d-9380-de523a88c83c\"\n", "version = \"1.0.5+3\"\n", "\n", "[[GR]]\n", "deps = [\"Base64\", \"DelimitedFiles\", \"LinearAlgebra\", \"Printf\", \"Random\", \"Serialization\", \"Sockets\", \"Test\", \"UUIDs\"]\n", "git-tree-sha1 = \"1185d50c5c90ec7c0784af7f8d0d1a600750dc4d\"\n", "uuid = \"28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71\"\n", "version = \"0.49.1\"\n", "\n", "[[GeometryTypes]]\n", "deps = [\"ColorTypes\", \"FixedPointNumbers\", \"LinearAlgebra\", \"StaticArrays\"]\n", "git-tree-sha1 = \"34bfa994967e893ab2f17b864eec221b3521ba4d\"\n", "uuid = \"4d00f742-c7ba-57c2-abde-4428a4b178cb\"\n", "version = \"0.8.3\"\n", "\n", "[[HTTP]]\n", "deps = [\"Base64\", \"Dates\", \"IniFile\", \"MbedTLS\", \"Sockets\"]\n", "git-tree-sha1 = \"fe31f4ff144392ad8176f5c7c03cca6ba320271c\"\n", "uuid = \"cd3eb016-35fb-5094-929b-558a96fad6f3\"\n", "version = \"0.8.14\"\n", "\n", "[[IniFile]]\n", "deps = [\"Test\"]\n", "git-tree-sha1 = \"098e4d2c533924c921f9f9847274f2ad89e018b8\"\n", "uuid = \"83e8ac13-25f8-5344-8a64-a9f2b223428f\"\n", "version = \"0.5.0\"\n", "\n", "[[InteractiveUtils]]\n", "deps = [\"Markdown\"]\n", "uuid = \"b77e0a4c-d291-57a0-90e8-8db25a27a240\"\n", "\n", "[[JSON]]\n", "deps = [\"Dates\", \"Mmap\", \"Parsers\", \"Unicode\"]\n", "git-tree-sha1 = \"b34d7cef7b337321e97d22242c3c2b91f476748e\"\n", "uuid = \"682c06a0-de6a-54ab-a142-c8b1cf79cde6\"\n", "version = \"0.21.0\"\n", "\n", "[[LAME_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"221cc8998b9060677448cbb6375f00032554c4fd\"\n", "uuid = \"c1c5ebd0-6772-5130-a774-d5fcae4a789d\"\n", "version = \"3.100.0+1\"\n", "\n", "[[LibGit2]]\n", "deps = [\"Printf\"]\n", "uuid = \"76f85450-5226-5b5a-8eaa-529ad045b433\"\n", "\n", "[[LibVPX_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"e3549ca9bf35feb9d9d954f4c6a9032e92f46e7c\"\n", "uuid = \"dd192d2f-8180-539f-9fb4-cc70b1dcf69a\"\n", "version = \"1.8.1+1\"\n", "\n", "[[Libdl]]\n", "uuid = \"8f399da3-3557-5675-b5ff-fb832c97cbdb\"\n", "\n", "[[LinearAlgebra]]\n", "deps = [\"Libdl\"]\n", "uuid = \"37e2e46d-f89d-539d-b4ee-838fcccc9c8e\"\n", "\n", "[[Logging]]\n", "uuid = \"56ddb016-857b-54e1-b83d-db4d58db5568\"\n", "\n", "[[MacroTools]]\n", "deps = [\"Markdown\", \"Random\"]\n", "git-tree-sha1 = \"f7d2e3f654af75f01ec49be82c231c382214223a\"\n", "uuid = \"1914dd2f-81c6-5fcd-8719-6d5c9610ff09\"\n", "version = \"0.5.5\"\n", "\n", "[[Markdown]]\n", "deps = [\"Base64\"]\n", "uuid = \"d6f4376e-aef5-505a-96c1-9c027394607a\"\n", "\n", "[[MbedTLS]]\n", "deps = [\"Dates\", \"MbedTLS_jll\", \"Random\", \"Sockets\"]\n", "git-tree-sha1 = \"426a6978b03a97ceb7ead77775a1da066343ec6e\"\n", "uuid = \"739be429-bea8-5141-9913-cc70e7f3736d\"\n", "version = \"1.0.2\"\n", "\n", "[[MbedTLS_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"c83f5a1d038f034ad0549f9ee4d5fac3fb429e33\"\n", "uuid = \"c8ffd9c3-330d-5841-b78e-0817d7145fa1\"\n", "version = \"2.16.0+2\"\n", "\n", "[[Measures]]\n", "git-tree-sha1 = \"e498ddeee6f9fdb4551ce855a46f54dbd900245f\"\n", "uuid = \"442fdcdd-2543-5da2-b0f3-8c86c306513e\"\n", "version = \"0.3.1\"\n", "\n", "[[Missings]]\n", "deps = [\"DataAPI\"]\n", "git-tree-sha1 = \"de0a5ce9e5289f27df672ffabef4d1e5861247d5\"\n", "uuid = \"e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28\"\n", "version = \"0.4.3\"\n", "\n", "[[Mmap]]\n", "uuid = \"a63ad114-7e13-5084-954f-fe012c677804\"\n", "\n", "[[NaNMath]]\n", "git-tree-sha1 = \"928b8ca9b2791081dc71a51c55347c27c618760f\"\n", "uuid = \"77ba4419-2d1f-58cd-9bb1-8ffee604a2e3\"\n", "version = \"0.3.3\"\n", "\n", "[[Ogg_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"59cf7a95bf5ac39feac80b796e0f39f9d69dc887\"\n", "uuid = \"e7412a2a-1a6e-54c0-be00-318e2571c051\"\n", "version = \"1.3.4+0\"\n", "\n", "[[OpenSSL_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"d2a6f25262d568b5a7e454cf7ff5066a79d16c7d\"\n", "uuid = \"458c3c95-2e84-50aa-8efc-19380b2a3a95\"\n", "version = \"1.1.1+2\"\n", "\n", "[[Opus_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"002c18f222a542907e16c83c64a1338992da7e2c\"\n", "uuid = \"91d4177d-7536-5919-b921-800302f37372\"\n", "version = \"1.3.1+1\"\n", "\n", "[[OrderedCollections]]\n", "git-tree-sha1 = \"12ce190210d278e12644bcadf5b21cbdcf225cd3\"\n", "uuid = \"bac558e1-5e72-5ebc-8fee-abe8a469f55d\"\n", "version = \"1.2.0\"\n", "\n", "[[Parsers]]\n", "deps = [\"Dates\", \"Test\"]\n", "git-tree-sha1 = \"f0abb338b4d00306500056a3fd44c221b8473ef2\"\n", "uuid = \"69de0a69-1ddd-5017-9359-2bf0b02dc9f0\"\n", "version = \"1.0.4\"\n", "\n", "[[Pkg]]\n", "deps = [\"Dates\", \"LibGit2\", \"Libdl\", \"Logging\", \"Markdown\", \"Printf\", \"REPL\", \"Random\", \"SHA\", \"UUIDs\"]\n", "uuid = \"44cfe95a-1eb2-52ea-b672-e2afdf69b78f\"\n", "\n", "[[PlotThemes]]\n", "deps = [\"PlotUtils\", \"Requires\", \"Statistics\"]\n", "git-tree-sha1 = \"c6f5ea535551b3b16835134697f0c65d06c94b91\"\n", "uuid = \"ccf2f8ad-2431-5c83-bf29-c5338b663b6a\"\n", "version = \"2.0.0\"\n", "\n", "[[PlotUtils]]\n", "deps = [\"ColorSchemes\", \"Colors\", \"Dates\", \"Printf\", \"Random\", \"Reexport\", \"Statistics\"]\n", "git-tree-sha1 = \"59ec24a0c96c513533e488dff1433df1bd3d6b9f\"\n", "uuid = \"995b91a9-d308-5afd-9ec6-746e21dbc043\"\n", "version = \"1.0.3\"\n", "\n", "[[Plots]]\n", "deps = [\"Base64\", \"Contour\", \"Dates\", \"FFMPEG\", \"FixedPointNumbers\", \"GR\", \"GeometryTypes\", \"JSON\", \"LinearAlgebra\", \"Measures\", \"NaNMath\", \"Pkg\", \"PlotThemes\", \"PlotUtils\", \"Printf\", \"REPL\", \"Random\", \"RecipesBase\", \"RecipesPipeline\", \"Reexport\", \"Requires\", \"Showoff\", \"SparseArrays\", \"Statistics\", \"StatsBase\", \"UUIDs\"]\n", "git-tree-sha1 = \"64e7405da4333ee6df59d7d0d88aade456341b3e\"\n", "uuid = \"91a5bcdd-55d7-5caf-9e0b-520d859cae80\"\n", "version = \"1.3.2\"\n", "\n", "[[Printf]]\n", "deps = [\"Unicode\"]\n", "uuid = \"de0858da-6303-5e67-8744-51eddeeeb8d7\"\n", "\n", "[[ProgressMeter]]\n", "deps = [\"Distributed\", \"Printf\"]\n", "git-tree-sha1 = \"b3cb8834eee5410c7246734cc6f4f586fe0dc50e\"\n", "uuid = \"92933f4c-e287-5a05-a399-4b506db050ca\"\n", "version = \"1.3.0\"\n", "\n", "[[PyCall]]\n", "deps = [\"Conda\", \"Dates\", \"Libdl\", \"LinearAlgebra\", \"MacroTools\", \"Serialization\", \"VersionParsing\"]\n", "git-tree-sha1 = \"3a3fdb9000d35958c9ba2323ca7c4958901f115d\"\n", "uuid = \"438e738f-606a-5dbb-bf0a-cddfbfd45ab0\"\n", "version = \"1.91.4\"\n", "\n", "[[REPL]]\n", "deps = [\"InteractiveUtils\", \"Markdown\", \"Sockets\"]\n", "uuid = \"3fa0cd96-eef1-5676-8a61-b3b8758bbffb\"\n", "\n", "[[Random]]\n", "deps = [\"Serialization\"]\n", "uuid = \"9a3f8284-a2c9-5f02-9a11-845980a1fd5c\"\n", "\n", "[[RecipesBase]]\n", "git-tree-sha1 = \"54f8ceb165a0f6d083f0d12cb4996f5367c6edbc\"\n", "uuid = \"3cdcf5f2-1ef4-517c-9805-6587b60abb01\"\n", "version = \"1.0.1\"\n", "\n", "[[RecipesPipeline]]\n", "deps = [\"Dates\", \"PlotUtils\", \"RecipesBase\"]\n", "git-tree-sha1 = \"9215637e28503ca85bef843a1fc02b2f76f1ba09\"\n", "uuid = \"01d81517-befc-4cb6-b9ec-a95719d0359c\"\n", "version = \"0.1.9\"\n", "\n", "[[Reexport]]\n", "deps = [\"Pkg\"]\n", "git-tree-sha1 = \"7b1d07f411bc8ddb7977ec7f377b97b158514fe0\"\n", "uuid = \"189a3867-3050-52da-a836-e630ba90ab69\"\n", "version = \"0.2.0\"\n", "\n", "[[Requires]]\n", "deps = [\"UUIDs\"]\n", "git-tree-sha1 = \"d37400976e98018ee840e0ca4f9d20baa231dc6b\"\n", "uuid = \"ae029012-a4dd-5104-9daa-d747884805df\"\n", "version = \"1.0.1\"\n", "\n", "[[SHA]]\n", "uuid = \"ea8e919c-243c-51af-8825-aaa63cd721ce\"\n", "\n", "[[Serialization]]\n", "uuid = \"9e88b42a-f829-5b0c-bbe9-9e923198166b\"\n", "\n", "[[Showoff]]\n", "deps = [\"Dates\"]\n", "git-tree-sha1 = \"e032c9df551fb23c9f98ae1064de074111b7bc39\"\n", "uuid = \"992d4aef-0814-514b-bc4d-f2e9a6c4116f\"\n", "version = \"0.3.1\"\n", "\n", "[[Sockets]]\n", "uuid = \"6462fe0b-24de-5631-8697-dd941f90decc\"\n", "\n", "[[SortingAlgorithms]]\n", "deps = [\"DataStructures\", \"Random\", \"Test\"]\n", "git-tree-sha1 = \"03f5898c9959f8115e30bc7226ada7d0df554ddd\"\n", "uuid = \"a2af1166-a08f-5f64-846c-94a0d3cef48c\"\n", "version = \"0.3.1\"\n", "\n", "[[SparseArrays]]\n", "deps = [\"LinearAlgebra\", \"Random\"]\n", "uuid = \"2f01184e-e22b-5df5-ae63-d93ebab69eaf\"\n", "\n", "[[StaticArrays]]\n", "deps = [\"LinearAlgebra\", \"Random\", \"Statistics\"]\n", "git-tree-sha1 = \"5c06c0aeb81bef54aed4b3f446847905eb6cbda0\"\n", "uuid = \"90137ffa-7385-5640-81b9-e52037218182\"\n", "version = \"0.12.3\"\n", "\n", "[[Statistics]]\n", "deps = [\"LinearAlgebra\", \"SparseArrays\"]\n", "uuid = \"10745b16-79ce-11e8-11f9-7d13ad32a3b2\"\n", "\n", "[[StatsBase]]\n", "deps = [\"DataAPI\", \"DataStructures\", \"LinearAlgebra\", \"Missings\", \"Printf\", \"Random\", \"SortingAlgorithms\", \"SparseArrays\", \"Statistics\"]\n", "git-tree-sha1 = \"a6102b1f364befdb05746f386b67c6b7e3262c45\"\n", "uuid = \"2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91\"\n", "version = \"0.33.0\"\n", "\n", "[[Test]]\n", "deps = [\"Distributed\", \"InteractiveUtils\", \"Logging\", \"Random\"]\n", "uuid = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\n", "\n", "[[UUIDs]]\n", "deps = [\"Random\", \"SHA\"]\n", "uuid = \"cf7118a7-6976-5b1a-9a39-7adc72f591a4\"\n", "\n", "[[Unicode]]\n", "uuid = \"4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5\"\n", "\n", "[[VersionParsing]]\n", "git-tree-sha1 = \"80229be1f670524750d905f8fc8148e5a8c4537f\"\n", "uuid = \"81def892-9a0e-5fdd-b105-ffc91e053289\"\n", "version = \"1.2.0\"\n", "\n", "[[Zlib_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"a2e0d558f6031002e380a90613b199e37a8565bf\"\n", "uuid = \"83775a58-1f1d-513f-b197-d71354ab007a\"\n", "version = \"1.2.11+10\"\n", "\n", "[[libass_jll]]\n", "deps = [\"Bzip2_jll\", \"FreeType2_jll\", \"FriBidi_jll\", \"Libdl\", \"Pkg\", \"Zlib_jll\"]\n", "git-tree-sha1 = \"027a304b2a90de84f690949a21f94e5ae0f92c73\"\n", "uuid = \"0ac62f75-1d6f-5e53-bd7c-93b484bb37c0\"\n", "version = \"0.14.0+2\"\n", "\n", "[[libfdk_aac_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"480c7ed04f68ea3edd4c757f5db5b6a0a4e0bd99\"\n", "uuid = \"f638f0a6-7fb0-5443-88ba-1cc74229b280\"\n", "version = \"0.1.6+2\"\n", "\n", "[[libvorbis_jll]]\n", "deps = [\"Libdl\", \"Ogg_jll\", \"Pkg\"]\n", "git-tree-sha1 = \"6a66f65b5275dfa799036c8a3a26616a0a271c4a\"\n", "uuid = \"f27f6e37-5d2b-51aa-960f-b287f2bc3b7a\"\n", "version = \"1.3.6+4\"\n", "\n", "[[x264_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"d89346fe63a6465a9f44e958ac0e3d366af90b74\"\n", "uuid = \"1270edf5-f2f9-52d2-97e9-ab00b5d0237a\"\n", "version = \"2019.5.25+2\"\n", "\n", "[[x265_jll]]\n", "deps = [\"Libdl\", \"Pkg\"]\n", "git-tree-sha1 = \"61324ad346b00a6e541896b94201c9426591e43a\"\n", "uuid = \"dfaa095f-4041-5dcd-9319-2fabd8486b76\"\n", "version = \"3.0.0+1\"\n" ] }, { "data": { "text/plain": [ "Process(`\u001b[4mcat\u001b[24m \u001b[4mManifest.toml\u001b[24m`, ProcessExited(0))" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "run(`cat Manifest.toml`)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Example: Representing Colors and Images with Colors.jl\n", "\n", "Let's take a tour of one of my favorite packages, Colors.jl, and show off some feature's of Julia's arrays along the way. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We can load a package from the current environment with `using`:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "using Colors: RGB # For now, just bring the `RGB` name into scope" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", "\n" ], "text/plain": [ "RGB{N0f8}(1.0,0.0,0.0)" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RGB(1, 0, 0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The `RGB` type from Colors.jl knows how to render itself as an actual colored `div` when running in Jupyter. We can also print its value as a string if we want:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RGB{N0f8}(1.0,0.0,0.0)" ] } ], "source": [ "print(RGB(1, 0, 0))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Julia arrays are fully generic, so we can create an array of colors:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "11×11 Array{RGB{Float64},2} with eltype RGB{Float64}:\n", " RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,1.0,0.0)\n", " RGB{Float64}(0.1,0.0,0.0) RGB{Float64}(0.1,1.0,0.0)\n", " RGB{Float64}(0.2,0.0,0.0) RGB{Float64}(0.2,1.0,0.0)\n", " RGB{Float64}(0.3,0.0,0.0) RGB{Float64}(0.3,1.0,0.0)\n", " RGB{Float64}(0.4,0.0,0.0) RGB{Float64}(0.4,1.0,0.0)\n", " RGB{Float64}(0.5,0.0,0.0) … RGB{Float64}(0.5,1.0,0.0)\n", " RGB{Float64}(0.6,0.0,0.0) RGB{Float64}(0.6,1.0,0.0)\n", " RGB{Float64}(0.7,0.0,0.0) RGB{Float64}(0.7,1.0,0.0)\n", " RGB{Float64}(0.8,0.0,0.0) RGB{Float64}(0.8,1.0,0.0)\n", " RGB{Float64}(0.9,0.0,0.0) RGB{Float64}(0.9,1.0,0.0)\n", " RGB{Float64}(1.0,0.0,0.0) … RGB{Float64}(1.0,1.0,0.0)" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C = [RGB(i, j, 0) for i in 0:0.1:1, j in 0:0.1:1]" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Array{RGB{Float64},2}" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(C)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "C is an array like any other, so we can index into it and slice it:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", "\n" ], "text/plain": [ "RGB{Float64}(0.7,0.1,0.0)" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C[8, 2]" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "11-element Array{RGB{Float64},1} with eltype RGB{Float64}:\n", " RGB{Float64}(0.0,0.0,0.0)\n", " RGB{Float64}(0.0,0.1,0.0)\n", " RGB{Float64}(0.0,0.2,0.0)\n", " RGB{Float64}(0.0,0.3,0.0)\n", " RGB{Float64}(0.0,0.4,0.0)\n", " RGB{Float64}(0.0,0.5,0.0)\n", " RGB{Float64}(0.0,0.6,0.0)\n", " RGB{Float64}(0.0,0.7,0.0)\n", " RGB{Float64}(0.0,0.8,0.0)\n", " RGB{Float64}(0.0,0.9,0.0)\n", " RGB{Float64}(0.0,1.0,0.0)" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C[1, :]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's pull out the red channel from our image `C`:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "using Colors: red, green, blue" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If you don't know what a function does, you can use the `?` operator to access its docstring:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1md\u001b[22muce \u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1md\u001b[22misplay \u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1md\u001b[22mirect_stdin \u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1md\u001b[22mirect_stdout \u001b[0m\u001b[1mr\u001b[22m\u001b[0m\u001b[1me\u001b[22m\u001b[0m\u001b[1md\u001b[22mirect_stderr\n", "\n" ] }, { "data": { "text/latex": [ "\\texttt{red(c)} returns the red component of an \\texttt{AbstractRGB} opaque or transparent color.\n", "\n" ], "text/markdown": [ "`red(c)` returns the red component of an `AbstractRGB` opaque or transparent color.\n" ], "text/plain": [ " \u001b[36mred(c)\u001b[39m returns the red component of an \u001b[36mAbstractRGB\u001b[39m opaque or transparent\n", " color." ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "?red" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "red(C[1, 1])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Broadcasting\n", "\n", "To get the red channel of each element of `C`, we can use *broadcasting*. The syntax `f.(x)` applies the function `f` to each element of `x`:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "11×11 Array{Float64,2}:\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1\n", " 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2\n", " 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3\n", " 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4\n", " 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5\n", " 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6\n", " 0.7 0.7 0.7 0.7 0.7 0.7 0.7 0.7 0.7 0.7 0.7\n", " 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8\n", " 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9\n", " 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "red.(C)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "That's not very visual. Let's render that red channel as a grayscale image:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "11×11 Array{Gray{Float64},2} with eltype Gray{Float64}:\n", " Gray{Float64}(0.0) Gray{Float64}(0.0) … Gray{Float64}(0.0)\n", " Gray{Float64}(0.1) Gray{Float64}(0.1) Gray{Float64}(0.1)\n", " Gray{Float64}(0.2) Gray{Float64}(0.2) Gray{Float64}(0.2)\n", " Gray{Float64}(0.3) Gray{Float64}(0.3) Gray{Float64}(0.3)\n", " Gray{Float64}(0.4) Gray{Float64}(0.4) Gray{Float64}(0.4)\n", " Gray{Float64}(0.5) Gray{Float64}(0.5) … Gray{Float64}(0.5)\n", " Gray{Float64}(0.6) Gray{Float64}(0.6) Gray{Float64}(0.6)\n", " Gray{Float64}(0.7) Gray{Float64}(0.7) Gray{Float64}(0.7)\n", " Gray{Float64}(0.8) Gray{Float64}(0.8) Gray{Float64}(0.8)\n", " Gray{Float64}(0.9) Gray{Float64}(0.9) Gray{Float64}(0.9)\n", " Gray{Float64}(1.0) Gray{Float64}(1.0) … Gray{Float64}(1.0)" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Colors: Gray\n", "\n", "Gray.(red.(C))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Julia's broadcasting provides guaranteed *loop fusion*. That means that if you do `Gray.(red.(x))`, the language guarantees that it will do only one loop over the elements of `x`, computing `Gray(red(x_i))` for each `x_i` in `x`. \n", "\n", "See https://julialang.org/blog/2017/01/moredots/ for more. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Julia is Fast\n", "\n", "* I claimed at the beginning of this talk that Julia has performance on par with C. Let's prove it!\n", "* To show this, I'll implement the basic `sum` function in Julia, C, and Python so we can compare them:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's start with Julia:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "my_sum" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", "Naive implementation of sum. Works for any iterable `x` with any element type.\n", "\"\"\"\n", "function my_sum(x)\n", " result = zero(eltype(x))\n", " for element in x\n", " result += element\n", " end\n", " return result\n", "end" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "And let's create some data to test with:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10000000-element Array{Float64,1}:\n", " 0.3250821881143635\n", " 0.5739150786758722\n", " 0.13383939143899348\n", " 0.4395993297446783\n", " 0.3295526596520042\n", " 0.02847425323560948\n", " 0.8487199564066177\n", " 0.7997505161145844\n", " 0.6245936015647562\n", " 0.5106550585869152\n", " 0.9238301866509853\n", " 0.49764889129935574\n", " 0.7401243526305121\n", " ⋮\n", " 0.6680583163473712\n", " 0.4697749293985751\n", " 0.8080600012381363\n", " 0.08649714310431955\n", " 0.2412430060343358\n", " 0.33645492148197076\n", " 0.138424161003317\n", " 0.27552196839070064\n", " 0.00438248390256013\n", " 0.7495825118700372\n", " 0.11786299612959139\n", " 0.9934003189851528" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data = rand(Float64, 10^7)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To measure the performance of `my_sum`, we'll use the BenchmarkTools.jl package. " ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "using BenchmarkTools" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "BenchmarkTools.Trial: \n", " memory estimate: 0 bytes\n", " allocs estimate: 0\n", " --------------\n", " minimum time: 11.994 ms (0.00% GC)\n", " median time: 12.054 ms (0.00% GC)\n", " mean time: 12.099 ms (0.00% GC)\n", " maximum time: 16.589 ms (0.00% GC)\n", " --------------\n", " samples: 414\n", " evals/sample: 1" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@benchmark my_sum($data)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In this case, we only care about the minimum time. The `@btime` macro is a shorthand to print just that minimum time:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 11.716 ms (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "4.998996907719521e6" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime my_sum($data)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's compare this with C. It's easy to call functions from C shared libraries in Julia:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "c_compare" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", "Call the `strcmp` function from `libc.so.6`\n", "\"\"\"\n", "function c_compare(x::String, y::String)\n", " # We have to tell the compiler that this C function returns an `int` and \n", " # expects two `char *` inputs. The `Cint` and `Cstring` types are convenient\n", " # shorthands for those:\n", " ccall((:strcmp, \"libc.so.6\"), Cint, (Cstring, Cstring), x, y)\n", "end" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c_compare(\"hello\", \"hello\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Calling C functions has very little overhead:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 8.760 ns (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "0" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime c_compare($(\"hello\"), $(\"hello\"))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's create a C implementation of `my_sum`. We can do that without leaving Julia by piping some code directly to GCC:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [], "source": [ "C_code = \"\"\"\n", "\n", "#include // For `size_t`\n", "\n", "// Note: our Julia code works for any type, but the C implementation \n", "// is only for `double`.\n", "\n", "double c_sum(size_t n, double *X) {\n", " double s = 0.0;\n", " size_t i;\n", " for (i = 0; i < n; ++i) {\n", " s += X[i];\n", " }\n", " return s;\n", "}\n", "\n", "\"\"\";" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Now let's generate a name for our shared library:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"/tmp/jl_FELUh5.so\"" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# dlext gives the correct file extension for a shared library on this platform\n", "using Libdl: dlext\n", "const Clib = tempname() * \".\" * dlext" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To send the code to GCC, we can use `open()` on a command to write directly to the `stdin` of that command as if it were any other file- or buffer-like object:" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [], "source": [ "open(`gcc -fPIC -O3 -msse3 -xc -shared -o $Clib -`, \"w\") do cmd\n", " print(cmd, C_code) \n", "end" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Now we can define a Julia function that calls the C function we just compiled:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "c_sum (generic function with 1 method)" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The return type and argument types must match the signature we declared above:\n", "# \n", "# double c_sum(size_t n, double *X) \n", "# \n", "c_sum(X::Array{Float64}) = ccall((\"c_sum\", Clib), Cdouble, (Csize_t, Ptr{Cdouble}), length(X), X)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Now let's measure the performance of the pure C function:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 11.360 ms (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "4.998996907719521e6" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime c_sum($data)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's plot the result using the Plots.jl package:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [], "source": [ "using Plots" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = [\n", " \"my_sum (Julia)\" => 11.7,\n", " \"c_sum (C)\" => 11.3\n", "]\n", "\n", "bar(first.(results), last.(results), xlabel=\"function\", ylabel=\"time (ms, shorter is better)\", legend=nothing)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Our naive Julia code is just as fast as our naive C code! \n", "\n", "Is that as fast as we can go? What about Julia's built-in `sum()` function:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 5.377 ms (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "4.998996907718867e6" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime sum($data)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = [\n", " \"my_sum (Julia)\" => 11.7,\n", " \"c_sum (C)\" => 11.3,\n", " \"sum (Julia)\" => 5.4,\n", "]\n", "\n", "bar(first.(results), last.(results), xlabel=\"function\", ylabel=\"time (ms, shorter is better)\", legend=nothing)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "What's going on? Is the `sum()` function using some built-in behavior we don't have access to?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Nope--we can achieve that result easily with a few modifications:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "my_fast_sum (generic function with 1 method)" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function my_fast_sum(x)\n", " result = zero(eltype(x))\n", " \n", " # `@inbounds` is a macro which disables all bounds checking within a given block. \n", " #\n", " # `@simd` enables additional vector operations by indicating that it is OK to potentially\n", " # evaluate the loop out-of-order. \n", " @inbounds @simd for element in x\n", " result += element\n", " end\n", " result\n", "end" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 5.332 ms (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "4.998996907718901e6" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime my_fast_sum($data)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = [\n", " \"my_sum (Julia)\" => 11.7,\n", " \"c_sum (C)\" => 11.3,\n", " \"sum (Julia)\" => 5.4,\n", " \"my_fast_sum (Julia)\" => 5.3,\n", "]\n", "\n", "bar(first.(results), last.(results), xlabel=\"function\", ylabel=\"time (ms, shorter is better)\", legend=nothing)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "With some pretty simple changes, we were able to create a pure-Julia function which is twice as fast as our naive C function while still being clear and completely generic:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "6.641592653589793" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_fast_sum([1, 2.5, π])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Just for reference, let's compare with Python. It's easy to call Python code from Julia too--we just need the `PyCall` package:" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "using PyCall" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.8414709848078965" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "py_math = pyimport(\"math\")\n", "py_math.sin(1.0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Just as we did with C, we can quickly define a Python sum function without leaving Julia:" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "PyObject " ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The PyCall package lets us define python functions directly from Julia:\n", "\n", "py\"\"\"\n", "def mysum(a):\n", " s = 0.0\n", " for x in a:\n", " s = s + x\n", " return s\n", "\"\"\"\n", "\n", "# mysum_py is a reference to the Python mysum function\n", "py_sum = py\"\"\"mysum\"\"\"o" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let's make sure we're getting similar answers everywhere:" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "py_sum(data) ≈ c_sum(data) ≈ sum(data) ≈ my_sum(data) ≈ my_fast_sum(data)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 724.217 ms (7 allocations: 352 bytes)\n" ] }, { "data": { "text/plain": [ "4.998996907719521e6" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime py_sum($data)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = [\n", " \"my_sum (Julia)\" => 11.7,\n", " \"c_sum (C)\" => 11.3,\n", " \"sum (Julia)\" => 5.4,\n", " \"my_fast_sum (Julia)\" => 5.3,\n", " \"py_sum (Python)\" => 724.2,\n", "]\n", "\n", "bar(first.(results), last.(results), xlabel=\"function\", ylabel=\"time (ms, shorter is better)\", legend=nothing)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### What about Numpy or Cython?\n", "\n", "* Of course, there are faster ways to sum a vector of `double`s in Python than a `for` loop. \n", "* `numpy.sum()` is just as fast as Julia's `sum()` for large vectors...\n", "* ...but there are some caveats:\n", " * NumPy is only efficient for a pre-determined set of numeric types. \n", " * NumPy cannot be extended without switching into an entirely different programming language, build system, and code environment. \n", " * So, if `numpy.sum()` happens to cover the cases you actually need, then go for it!\n", " * But if you want to be able to write efficient code that does not happen to cover the specific set of functions and types in NumPy, then you need Julia. " ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "struct Point{T}\n", " x::T\n", " y::T\n", "end\n", "\n", "function Base.zero(::Type{Point{T}}) where {T} \n", " Point{T}(zero(T), zero(T))\n", "end\n", " \n", "Base.:+(p1::Point, p2::Point) = Point(p1.x + p2.x, p1.y + p2.y)\n", "\n", "points = [Point(rand(), rand()) for _ in 1:10^7];" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 15.001 ms (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "Point{Float64}(5.000181181696696e6, 5.000916078448208e6)" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime my_fast_sum($points)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\t.text\n", "; ┌ @ In[64]:2 within `my_fast_sum'\n", "\tmovq\t%rdi, %rax\n", "; │ @ In[64]:8 within `my_fast_sum'\n", "; │┌ @ simdloop.jl:71 within `macro expansion'\n", "; ││┌ @ simdloop.jl:51 within `simd_inner_length'\n", "; │││┌ @ array.jl:221 within `length'\n", "\tmovq\t8(%rsi), %rcx\n", "; ││└└\n", "; ││ @ simdloop.jl:72 within `macro expansion'\n", "; ││┌ @ int.jl:49 within `<'\n", "\ttestq\t%rcx, %rcx\n", "; ││└\n", "\tjle\tL51\n", "\tmovq\t(%rsi), %rdx\n", "\tvxorpd\t%xmm0, %xmm0, %xmm0\n", "\tnopw\t%cs:(%rax,%rax)\n", "\tnopl\t(%rax)\n", "; ││ @ simdloop.jl:77 within `macro expansion' @ In[64]:9\n", "; ││┌ @ In[75]:10 within `+' @ float.jl:401\n", "L32:\n", "\tvaddpd\t(%rdx), %xmm0, %xmm0\n", "; ││└\n", "; ││ @ simdloop.jl:75 within `macro expansion'\n", "; ││┌ @ int.jl:49 within `<'\n", "\taddq\t$16, %rdx\n", "\taddq\t$-1, %rcx\n", "; ││└\n", "\tjne\tL32\n", "; │└\n", "; │ @ In[64]:11 within `my_fast_sum'\n", "\tvmovupd\t%xmm0, (%rax)\n", "\tretq\n", "L51:\n", "\tvxorps\t%xmm0, %xmm0, %xmm0\n", "; │ @ In[64]:11 within `my_fast_sum'\n", "\tvmovups\t%xmm0, (%rax)\n", "\tretq\n", "\tnopl\t(%rax)\n", "; └\n" ] } ], "source": [ "@code_native my_fast_sum(points)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Bonus Features of Julia" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Asynchronous Tasks\n", "\n", "Julia supports asynchronous cooperative tasks, with `libuv` providing the backend. These tasks are great for handling operations like IO or network requests:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "using HTTP: request" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "starting request 1\n", "starting request 2\n", "starting request 3\n", "starting request 4\n", "starting request 5\n", "got response 2 with status 200\n", "got response 4 with status 200\n", "got response 3 with status 200\n", "got response 1 with status 200\n", "got response 5 with status 200\n" ] } ], "source": [ "for i in 1:5\n", " @async begin\n", " println(\"starting request $i\")\n", " r = request(\"GET\", \"https://jsonplaceholder.typicode.com/posts/$i\")\n", " println(\"got response $i with status $(r.status)\")\n", " end\n", "end\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Multi-Threading\n", "\n", "\n", "Julia also supports parallel and distributed computing (see https://docs.julialang.org/en/v1/manual/parallel-computing/ for more). In addition, Julia 1.3 implemented a new feature, the Parallel Task Run-Time (PATR), which allows for *composable* multi-threading. It is now possible for a parallelized Julia function to call other parallelized code without over-subsubscribing the available processors. See \n", "https://julialang.org/blog/2019/07/multithreading/ for more. " ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "fib (generic function with 1 method)" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Base.Threads: @spawn\n", "\n", "\n", "function fib(n::Int)\n", " if n < 2\n", " return n\n", " end\n", " # `@spawn` creates a new parallel task. Tasks are lightweight and can be\n", " # created at will. The Julia Parallel Task Run-Time handles scheduling the\n", " # tasks to native threads in a depth first manner. That means that you can\n", " # write parallel code which calls other parallel code without over-subscribing\n", " # your available processors. \n", " t = @spawn fib(n - 2)\n", " return fib(n - 1) + fetch(t)\n", "end " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## No Implicit Copying\n", "\n", "Values are never copied unless you intentionally copy or convert them. That means that functions can mutate their input arguments to efficiently do work in-place:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "invert!" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", "Invert the sign of the vector `x`, operating in-place to avoid any memory allocation.\n", "\"\"\"\n", "function invert!(x::AbstractVector)\n", " for i in eachindex(x)\n", " x[i] = -x[i]\n", " end\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: the `!` in the function name is just a convention: it signals to readers of the code\n", "that the input argument `x` will be modified." ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " -1\n", " -2\n", " -3" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = [1, 2, 3]\n", "invert!(x)\n", "x" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 4.133 ns (0 allocations: 0 bytes)\n" ] } ], "source": [ "@btime invert!($x)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Anything Can Be a Value\n", "\n", "Julia has no special rules about what can or cannot be assigned to a variable or passed to a function. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Functions are Values\n", "\n", "A Julia function is a value like any other, so passing functions around and implementing higher-order functions is trivial:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "map_reduce" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", "map_reduce: apply `operator` to each element in `array` and reduce pairwise via `reduction`\n", "\"\"\"\n", "function map_reduce(operator, reduction, array, initial_value)\n", " result = initial_value\n", " for item in array\n", " result = reduction(result, operator(item))\n", " end\n", " result\n", "end" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "1.1350859243855171" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "map_reduce(sin, +, [1, 2, 3, 4], 0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can define `sum` in terms of `map_reduce`:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "fancy_sum (generic function with 1 method)" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fancy_sum(x) = map_reduce(identity, +, x, zero(eltype(x)))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The performance is just as good as our hand-written `sum` loop:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 11.762 ms (0 allocations: 0 bytes)\n" ] }, { "data": { "text/plain": [ "4.998996907719521e6" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@btime fancy_sum($data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get all the way down to 5ms, we'd need to apply the same `@inbounds` and `@simd` annotations. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Types are Values\n", "\n", "Types can also be passed around as values and bound to variables with no special rules. This makes implementing factories or constructors easy:" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "empty_matrix (generic function with 1 method)" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function empty_matrix(T::Type, rows::Integer, cols::Integer)\n", " zeros(T, rows, cols)\n", "end" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Int64,2}:\n", " 0 0 0\n", " 0 0 0\n", " 0 0 0" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty_matrix(Int, 3, 3)" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Point{Float64},2}:\n", " Point{Float64}(0.0, 0.0) Point{Float64}(0.0, 0.0) Point{Float64}(0.0, 0.0)\n", " Point{Float64}(0.0, 0.0) Point{Float64}(0.0, 0.0) Point{Float64}(0.0, 0.0)\n", " Point{Float64}(0.0, 0.0) Point{Float64}(0.0, 0.0) Point{Float64}(0.0, 0.0)" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty_matrix(Point{Float64}, 3, 3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Expressions are Values\n", "\n", "Even the expressions that representing Julia code are represented as values in Julia. You can create an expression with the `:()` operator, and you can inspect it just like any other object. " ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ ":(1 + 2)" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = :(1 + 2)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "An expression has a `head` indicating what type of expression it is and zero or more `args`:" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ ":call" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr.head" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "3-element Array{Any,1}:\n", " :+\n", " 1\n", " 2" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr.args" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Metaprogramming\n", "\n", "Since expressions are just values, we can easily write functions to manipulate them:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "switch_to_subtraction!" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "switch_to_subtraction!(x::Any) = nothing\n", "\n", "\"\"\"\n", "Change all `+` function calls to `-` function calls. \n", "\n", "\n", "Great for fixing sign errors in your code!\n", "\n", "\"\"\"\n", "function switch_to_subtraction!(ex::Expr)\n", " if ex.head == :call && ex.args[1] == :(+)\n", " ex.args[1] = :(-)\n", " end\n", " for i in 2:length(ex.args)\n", " switch_to_subtraction!(ex.args[i])\n", " end\n", "end" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ ":((1 + 2) * (3 + 4) * sqrt(2))" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr = :((1 + 2) * (3 + 4) * sqrt(2))" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ ":((1 - 2) * (3 - 4) * sqrt(2))" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "switch_to_subtraction!(expr)\n", "\n", "expr" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Macros\n", "\n", "A macro is written just like a normal Julia function. The difference is that a macro operates on the *expression* itself, not on its value:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "@more_cats" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", "Modify a given expression, replacing all string literals with \"cat\"\n", "\"\"\"\n", "macro more_cats(expr)\n", " for i in eachindex(expr.args)\n", " if expr.args[i] isa String\n", " expr.args[i] = \"cat\"\n", " end\n", " end\n", " return esc(expr)\n", "end" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Macros are always called with the `@` prefix in Julia:" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "cat\n" ] } ], "source": [ "@more_cats println(\"hello world\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "`@macroexpand` shows the code that another macro will generate:" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ ":(println(\"cat\"))" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@macroexpand @more_cats println(\"hello world\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Actually Useful Julia Macros" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "`@show` : print out the *name* of a variable and its value. Great for quick debugging:" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 5\n" ] }, { "data": { "text/plain": [ "5" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 5\n", "@show x" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "`@time` measure the elapsed time of an expression and return the result of that expression:" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.000074 seconds (6 allocations: 464 bytes)\n" ] }, { "data": { "text/plain": [ "1.772453850905516027298167483341145182797549456122387128213807789852911284591025" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@time sqrt(big(π))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "`@showprogress`: Time each iteration of a loop and estimate how much longer it will take to finish:" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [], "source": [ "using ProgressMeter: @showprogress" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32mProgress: 100%|█████████████████████████████████████████| Time: 0:00:04\u001b[39m\n" ] } ], "source": [ "@showprogress for i in 1:100\n", " sum(rand(10^7))\n", "end" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# What's Hard to Do in Julia?\n", "\n", "What is the compiler team working on making better? https://discourse.julialang.org/t/compiler-work-priorities/17623\n", "\n", "What are some subtle problems that the Julia team would like to fix?\n", "\n", "[![What's bad about Julia talk](img/bad_about_julia.png)](https://www.youtube.com/watch?v=TPuJsgyu87U)\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Compiler Latency\n", "\n", "* The JIT compiler runs each time it sees a function being called with a new input type. \n", "* That makes the first call to every function slow, since you have to wait for the JIT.\n", " * This makes Julia awkward to use for things like shell scripts or AWS lambda" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Static Compilation\n", "\n", "* To avoid the JIT lag, you can compile a Julia package to a standalone executable using [PackageCompiler.jl](https://github.com/JuliaLang/PackageCompiler.jl), but:\n", " * This workflow is pretty new, and you may run into interesting bugs\n", " * The resulting libraries tend to be quite large" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Embedded Computing\n", "\n", "* It can be hard to run Julia on memory-limited systems, since you need the compiler living alongside your code. \n", "* Static compilation can help, but this isn't a well-developed workflow yet." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Static Analysis\n", "\n", "* There are some linting tools for Julia (like the `vscode-julia` extension for Visual Studio Code), but they are not as mature as languages like Python, C, Java, etc.\n", "* Static analysis of Julia is harder, since the language itself is dynamically typed. \n", " * To be fair, static analysis of C++ is [undecidable](https://blog.reverberate.org/2013/08/parsing-c-is-literally-undecidable.html) but we still have tools that do a pretty good job most of the time. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Useful Julia Tools" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Juno IDE\n", "\n", "https://junolab.org/\n", "\n", "* Built-in debugger and profiler support\n", "* Inline evaluation of expressions\n", "* Docstring lookup and autocomplete\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Julia-VSCode\n", "\n", "https://github.com/julia-vscode/julia-vscode\n", "\n", "* Code highlighting, snippets, linting, and completions\n", "* Integrated plot and table viewers\n", "* General extension support via the VSCode language server\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Flux.jl\n", "\n", "https://fluxml.ai/Flux.jl/stable/\n", "\n", "* Flexible library for machine learning built entirely in Julia\n", "* Feed-forward and recurrent neural nets\n", "* Gradients via automatic differentiation\n", "* GPU support via CuArrays.jl\n", "\n", "```julia\n", "m = Chain(\n", " Dense(784, 32, σ),\n", " Dense(32, 10), softmax\n", ")\n", "\n", "loss(x, y) = Flux.mse(m(x), y)\n", "ps = Flux.params(m)\n", "\n", "for i in 1:num_training_iters\n", " Flux.train!(loss, ps, data, opt)\n", "end\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## DifferentialEquations.jl\n", "\n", "https://github.com/SciML/DifferentialEquations.jl\n", "\n", "* {stochastic | deterministic | ordinary | partial} differential equations\n", "* Automatic differentiation and sparsity detection\n", "* GPU support\n", "* Sensitivity analysis and parameter estimation\n", "* Access to pure-Julia solvers and existing C and Fortran solvers\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## DataFrames.jl\n", "\n", "https://github.com/JuliaData/DataFrames.jl\n", "\n", "* In-memory tabular data\n", "* Joining, indexing, grouping, and split-apply-combine\n", "\n", "```julia\n", "julia> using DataFrames\n", "\n", "julia> df = DataFrame(A = 1:4, B = [\"M\", \"F\", \"F\", \"M\"])\n", "4×2 DataFrame\n", "│ Row │ A │ B │\n", "│ │ Int64 │ String │\n", "├─────┼───────┼────────┤\n", "│ 1 │ 1 │ M │\n", "│ 2 │ 2 │ F │\n", "│ 3 │ 3 │ F │\n", "│ 4 │ 4 │ M │\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## JuMP.jl\n", "\n", "https://github.com/JuliaOpt/JuMP.jl\n", "\n", "* Continuous and discrete optimization\n", "* Support for a wide variety of free and commercial solvers\n", "* Efficient high-level language for mathematical programming\n", "\n", "\n", "Example: Solving a simple model-predictive control problem as a quadratic program ([source](https://github.com/rdeits/DynamicWalking2018.jl/blob/master/notebooks/6.%20Optimization%20with%20JuMP.ipynb)):\n", "\n", "[![demo of a simple model-predictive control problem](img/mpc.gif)](https://github.com/rdeits/DynamicWalking2018.jl/blob/master/notebooks/6.%20Optimization%20with%20JuMP.ipynb)\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Cassette.jl\n", "\n", "https://www.youtube.com/watch?v=_E2zEzNEy-8\n", "\n", "\"Overdub\" Julia code: \n", "\n", "* Hook into the compiler to dynamically modify the behavior of existing functions in a given *context*\n", "* Building block for debuggers, automatic differentation, and more.\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Where To Go Next?\n", "\n", "* Download Julia from https://julialang.org/\n", "* Check out the manual at https://docs.julialang.org/en/v1/\n", "* Ask questions on [discourse](https://discourse.julialang.org/) and [slack](https://slackinvite.julialang.org/)\n", "* Find interesting packages on [juliahub](https://juliahub.com/ui/Home)\n", "\n", "![triangulated background](img/julia-triangle-background.svg)\n", "\n", "[Logo by Cormullion and David P. Saunders](https://nbviewer.jupyter.org/github/dpsanders/JuliaCon2019_tshirt/blob/master/penroseiana.ipynb)" ] } ], "metadata": { "@webio": { "lastCommId": null, "lastKernelId": null }, "celltoolbar": "Slideshow", "kernelspec": { "display_name": "Julia 1.4.1", "language": "julia", "name": "julia-1.4" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.4.1" } }, "nbformat": 4, "nbformat_minor": 2 }