diff --git a/Day_4/Day_4.jl b/Day_4/Day_4.jl index 664eb80..a4281e6 100644 --- a/Day_4/Day_4.jl +++ b/Day_4/Day_4.jl @@ -4,6 +4,12 @@ using Markdown using InteractiveUtils +# ╔═╡ 7a9ccfbc-bd2e-41d0-be5d-dea04b90d397 +using BenchmarkTools + +# ╔═╡ 8b1dfee2-bd8b-4d23-b9f8-9406002e0eaa +using Random + # ╔═╡ 1fb7d9af-333e-44f2-b693-09ff97937d4c # Oh, no, you found my secret! 😱 # Don't change this hidden cell! @@ -135,8 +141,164 @@ If you want syntax highlighting in your REPL, add the package `OhMyREPL` to your https://kristofferc.github.io/OhMyREPL.jl/latest/installation/ """ -# ╔═╡ d21771de-272e-4d57-8c76-c75be709ad0a +# ╔═╡ 0e53e4ee-16a7-47ef-9992-77cbfd1ed258 +md""" +# Benchmarking +""" +# ╔═╡ d86e4a2f-e737-49a4-bc16-a149e81785bd +function normal_for_loop(N) + factorials = zeros(BigInt, N) + + for i in 1:N + factorials[i] = factorial(big(i)) + end + + return +end + +# ╔═╡ bc777c73-9573-41c3-8ab5-843335539f96 +N = 5000 + +# ╔═╡ 55d1d616-b6ee-40dd-a0ed-6274a98b1e73 +@benchmark normal_for_loop(N) + +# ╔═╡ 00c27927-9c72-417a-862f-9b66318d9751 +@btime normal_for_loop(N) + +# ╔═╡ d21771de-272e-4d57-8c76-c75be709ad0a +md""" +# Multithreading +Julia is built as a modern language with parallel and asynchronous programming in mind. + +You can use run Julia code on the GPU, on multiple CPU threads (multithreading) and with multiple processes (multiprocessing). + +In this course, only multithreading will be presented. + +The easiest way to use multithreading in Julia is by letting a `for` loop run on multiple threads. This can be achieved by only addy the macro `@threads` before the `for` loop. + +Before trying to use multithreading, make sure that you launch Julia with multiple threads. To do so, run the following to launch Julia: + + julia -t auto + +You can append `--project=.` and other arguments to it. + +`-t` is an alias to `--threads`. `auto` automatically determins how many threads your PC supports. You can also replace `auto` by the number of threads that you want to use. This number can not be bigger than the number of threads that your computer supports. +""" + +# ╔═╡ 2cf5f891-8d8f-477d-8ef1-9b4b9480d477 +# Show available number of threads +# If this is 1, then you can not use multithreading! +# If your PC has only one core and one thread, then you can do nothing about it. +# Otherwise, you have to tell Julia how many threads to use with the `-t` argument. +Threads.nthreads() + +# ╔═╡ 76866972-0290-4e33-93de-b6128ba11994 +md""" +## `@threads` +""" + +# ╔═╡ cacc94b4-21e0-410e-acaa-80e37b447f94 +function multithreaded_for_loop(N) + factorials = zeros(BigInt, N) + + Threads.@threads for i in 1:N + factorials[i] = factorial(big(i)) + end + + return +end + +# ╔═╡ a3029480-bcdc-44f3-b504-8bd3bf3aa14d +@btime multithreaded_for_loop(N) + +# ╔═╡ f141dbb4-bdc5-4f16-8d97-fc0a3d5981f2 +function shuffle_multithreaded_for_loop(N) + factorials = zeros(BigInt, N) + + Threads.@threads for i in shuffle(1:N) + factorials[i] = factorial(big(i)) + end + + return +end + +# ╔═╡ e9dcda88-1eef-4c0a-99b2-12eaec56186b +@btime shuffle_multithreaded_for_loop(N) + +# ╔═╡ 009bb2e8-f03e-40f7-a66b-166dc6a1962d +md""" +## Thread safety +""" + +# ╔═╡ dd5e5073-be29-47e7-91c5-9e47c35f905c +function thread_unsafe() + N = 100000 + var = 0 + + Threads.@threads for i in 1:N + var += 1 + end + + return var +end + +# ╔═╡ 4554cbf0-36f5-45c6-a966-ad18b1592a60 +# Run this cell multiple times. +# The output is random 🤯 +thread_unsafe() + +# ╔═╡ ec08a80c-8886-4312-9481-5c89951681e1 +function thread_unsafe_sum(N) + sum_of_sums = 0 + + Threads.@threads for i in 1:N + sum_of_sums = sum(1:i) + end + + return sum_of_sums +end + +# ╔═╡ 3b5d9f7c-1fc9-4e85-8c07-8b5709895a10 +N2 = 1000000 + +# ╔═╡ fbd2423b-aaea-47a7-a3cf-537860e11a93 +thread_unsafe_sum(N2) + +# ╔═╡ e65ad214-33ba-4d08-81f0-5f98022a9f78 +function thread_safe_sum(N) + sums = zeros(Int64, N) + + Threads.@threads for i in 1:N + sums[i] = sum(1:i) + end + + return sum(sums) +end + +# ╔═╡ 8ad3daa6-d221-4ff7-9bc2-8e8a66bdd8c7 +@btime thread_safe_sum(N2) + +# ╔═╡ 95dffc7f-3393-487e-8521-c96291cdc7bf +typemax(Int64) + +# ╔═╡ ebd3a9d9-7a12-4001-9b53-913f664fb1c8 +function shuffle_safe_thread_sum(N) + sums = zeros(Int64, N) + + Threads.@threads for i in shuffle(1:N) + sums[i] = sum(1:i) + end + + return sum(sums) +end + +# ╔═╡ ddd2409e-de34-4eb9-a0b7-e10cc6c0ce9f +# This is worse than the version without shuffeling. +# In this case, shuffling is too expensive compared with its benefit +# Especially for multithreading, there is no silver bullet. +# Always benchmark! This is the only method to make sure that an "optimization" indeed an optimization is +@btime shuffle_safe_thread_sum(N2) # ╔═╡ 09f71a9e-6798-492f-98df-45087d0c4c8b md""" @@ -146,15 +308,94 @@ Julia has a focus on high performance. But if you don't pay attention, you might In this section, tools and tips for performance optimization in Julia are presented. """ -# ╔═╡ 2a24aebc-0654-4d00-bdab-627a8e1a75f2 +# ╔═╡ 6509dddd-ff17-49db-8e5e-fcea1ef0026c +N3 = 1000000 +# ╔═╡ 2a24aebc-0654-4d00-bdab-627a8e1a75f2 +begin + sin_vals = [] + + function global_allocating_access(N) + for i in 1:N + push!(sin_vals, sin(i)) + end + + return + end + + @btime global_allocating_access(N3) +end + +# ╔═╡ 56058ab1-4ea2-479d-88f9-5da6ac8c39c2 +begin + typed_sin_vals = Float64[] + + function typed_global_allocating_access(N) + for i in 1:N + push!(typed_sin_vals, sin(i)) + end + + return + end + + @btime typed_global_allocating_access(N3) +end + +# ╔═╡ ef164e7c-668a-4312-83f1-687ca7d4c8f9 +begin + preallocated_sin_vals = zeros(Float64, N3) + + function global_preallocated_access(N) + for i in 1:N + preallocated_sin_vals[i] = sin(i) + end + + return + end + + @btime global_preallocated_access(N3) +end + +# ╔═╡ ebc621b5-3aa3-4cf7-bcdf-e4c5fbb79f50 +begin + passed_preallocated_sin_vals = zeros(Float64, N3) + + function local_preallocated_access(N, sin_vals) + for i in 1:N + sin_vals[i] = sin(i) + end + + return + end + + @btime local_preallocated_access(N3, passed_preallocated_sin_vals) +end + +# ╔═╡ afcc15de-81e0-484f-80cf-3d805517c6e8 +# Here, the difference of preallocation is clearer +begin + passed_sin_vals = Float64[] + + function local_access(N, sin_vals) + for i in 1:N + push!(sin_vals, sin(i)) + end + + return + end + + @btime local_access(N3, passed_sin_vals) +end # ╔═╡ 00000000-0000-0000-0000-000000000001 PLUTO_PROJECT_TOML_CONTENTS = """ [deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] +BenchmarkTools = "~1.3.1" PlutoUI = "~0.7.38" """ @@ -180,6 +421,12 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +[[deps.BenchmarkTools]] +deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93" +uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +version = "1.3.1" + [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" @@ -298,6 +545,10 @@ version = "0.7.38" deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +[[deps.Profile]] +deps = ["Printf"] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" + [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -369,9 +620,38 @@ uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" # ╟─7f45c502-0909-42df-b93d-384f743df6a9 # ╟─f23ad33d-af1d-40c2-9efc-17ef8c4d1fb8 # ╟─6340aec8-6f77-4a30-8815-ce76ddecd6e8 -# ╠═d21771de-272e-4d57-8c76-c75be709ad0a +# ╠═0e53e4ee-16a7-47ef-9992-77cbfd1ed258 +# ╠═7a9ccfbc-bd2e-41d0-be5d-dea04b90d397 +# ╠═d86e4a2f-e737-49a4-bc16-a149e81785bd +# ╠═bc777c73-9573-41c3-8ab5-843335539f96 +# ╠═55d1d616-b6ee-40dd-a0ed-6274a98b1e73 +# ╠═00c27927-9c72-417a-862f-9b66318d9751 +# ╟─d21771de-272e-4d57-8c76-c75be709ad0a +# ╠═2cf5f891-8d8f-477d-8ef1-9b4b9480d477 +# ╟─76866972-0290-4e33-93de-b6128ba11994 +# ╠═cacc94b4-21e0-410e-acaa-80e37b447f94 +# ╠═a3029480-bcdc-44f3-b504-8bd3bf3aa14d +# ╠═8b1dfee2-bd8b-4d23-b9f8-9406002e0eaa +# ╠═f141dbb4-bdc5-4f16-8d97-fc0a3d5981f2 +# ╠═e9dcda88-1eef-4c0a-99b2-12eaec56186b +# ╠═009bb2e8-f03e-40f7-a66b-166dc6a1962d +# ╠═dd5e5073-be29-47e7-91c5-9e47c35f905c +# ╠═4554cbf0-36f5-45c6-a966-ad18b1592a60 +# ╠═ec08a80c-8886-4312-9481-5c89951681e1 +# ╠═3b5d9f7c-1fc9-4e85-8c07-8b5709895a10 +# ╠═fbd2423b-aaea-47a7-a3cf-537860e11a93 +# ╠═e65ad214-33ba-4d08-81f0-5f98022a9f78 +# ╠═8ad3daa6-d221-4ff7-9bc2-8e8a66bdd8c7 +# ╠═95dffc7f-3393-487e-8521-c96291cdc7bf +# ╠═ebd3a9d9-7a12-4001-9b53-913f664fb1c8 +# ╠═ddd2409e-de34-4eb9-a0b7-e10cc6c0ce9f # ╟─09f71a9e-6798-492f-98df-45087d0c4c8b +# ╠═6509dddd-ff17-49db-8e5e-fcea1ef0026c # ╠═2a24aebc-0654-4d00-bdab-627a8e1a75f2 +# ╠═56058ab1-4ea2-479d-88f9-5da6ac8c39c2 +# ╠═ef164e7c-668a-4312-83f1-687ca7d4c8f9 +# ╠═ebc621b5-3aa3-4cf7-bcdf-e4c5fbb79f50 +# ╠═afcc15de-81e0-484f-80cf-3d805517c6e8 # ╟─1fb7d9af-333e-44f2-b693-09ff97937d4c # ╟─00000000-0000-0000-0000-000000000001 # ╟─00000000-0000-0000-0000-000000000002