Skip to content

Commit 1eea7c8

Browse files
committed
Lec6&lab6 polishing.
1 parent b4abe07 commit 1eea7c8

File tree

3 files changed

+103
-94
lines changed

3 files changed

+103
-94
lines changed

docs/src/lecture_06/hw.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ Put the code of the compulsory task inside `hw.jl`. Zip only this file (not its
99
<header class="admonition-header">Homework (2 points)</header>
1010
<div class="admonition-body">
1111
```
12-
Following the lab exercises, the task now is to find all variables in an expression, i.e. for example when given expression
13-
```math
12+
Your task is to find all single letter variables in an expression, i.e. for example when given expression
13+
```julia
1414
x + 2*y*z - c*x
1515
```
16-
return a tuple of *unique sorted symbols* representing variables in an expression.
16+
return an array of *unique alphabetically sorted symbols* representing variables in an expression.
1717
```julia
18-
(:x, :y, :z, :c)
18+
[:x, :y, :z, :c]
1919
```
2020
Implement this in a function called `find_variables`. Note that there may be some edge cases that you may have to handle in a special way, such as
21-
- variable assignments `r = x*x` should return `r` as well
22-
- ignoring symbols representing function calls such as `log`, `exp`, etc.
21+
- variable assignments `r = x*x` should return the variable on the left as well (`r` in this case)
22+
- ignoring symbols representing single letter function calls such as `f(x)`
2323

2424
```@raw html
2525
</div></div>

docs/src/lecture_06/lab.md

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,35 @@ Secondly we will start playing with the metaprogramming side of Julia, mainly co
1010

1111
These topics will be extended in the next [lecture](@ref macro_lecture)/[lab](@ref macro_lab), where we are going use metaprogramming to manipulate code with macros.
1212

13-
We will be again a little getting ahead of ourselves as we are going to use quite a few macros, which will be properly explained in the next lecture as well, however for now the important thing to know is that macro is just a special function, that accepts as an argument Julia code, which it modifies.
13+
We will be again a little getting ahead of ourselves as we are going to use quite a few macros, which will be properly explained in the next lecture as well, however for now the important thing to know is that a macro is just a special function, that accepts as an argument Julia code, which it can modify.
1414

15-
## Introspection intro
16-
Let's start with the topic of code inspection, e.g. we may ask the following: What happens when I execute this
17-
```julia
18-
[i for i in 1:10]
19-
```
20-
We may be tempted to think, that this is some function call, for which we can call the `@which` macro to infer what is being called under the hood, however when we do this Julia will warn us that this is something "more complex"
15+
## Quick reminder of introspection tooling
16+
Let's start with the topic of code inspection, e.g. we may ask the following: What happens when Julia evaluates `[i for i in 1:10]`?
17+
- parsing
2118
```@repl lab06_intro
2219
using InteractiveUtils #hide
23-
@which [i for i in 1:10]
20+
:([i for i in 1:10]) |> dump
2421
```
25-
and that we should rather call the `Meta.@lower` macro, which spits out the so called lowered code of the expression.
22+
- lowering
2623
```@repl lab06_intro
2724
Meta.@lower [i for i in 1:10]
2825
```
29-
Now we see that the result is constructed by first creating the range, then a generator, which is subsequently collected. That is the reason, why simple `@which` macro cannot help.
30-
31-
So why it is not a call, let's look at how the code is parsed
32-
```@example lab06_intro
33-
using GraphRecipes, Plots
34-
code = :([i for i in 1:10])
35-
plot(code, fontsize=12, shorten=0.01, axis_buffer=0.15, nodeshape=:rect)
26+
- typing
27+
```@repl lab06_intro
28+
f() = [i for i in 1:10]
29+
@code_typed f()
3630
```
37-
Comparing this to a simple call to collect
38-
```@example lab06_intro
39-
code = :(collect(1:10))
40-
plot(code, fontsize=12, shorten=0.01, axis_buffer=0.15, nodeshape=:rect)
31+
- LLVM code generation
32+
```@repl lab06_intro
33+
@code_llvm f()
34+
```
35+
- native code generation
36+
```@repl lab06_intro
37+
@code_native f()
4138
```
42-
43-
introduce `@code_llvm`, `@code_lowered` macros ???
4439

4540
Let's see how these tools can help us understand some of Julia's internals on examples from previous labs and lectures.
4641

47-
48-
4942
### Understanding the runtime dispatch and type instabilities
5043
We will start with a question: Can we spot internally some difference between type stable/unstable code?
5144

@@ -55,7 +48,7 @@ We will start with a question: Can we spot internally some difference between ty
5548
<div class="admonition-body">
5649
```
5750
Inspect the following two functions using `@code_lowered`, `@code_typed`, `@code_llvm` and `@code_native`.
58-
```@example
51+
```@example lab06_intro
5952
x = rand(10^5)
6053
function explicit_len(x)
6154
length(x)
@@ -115,14 +108,15 @@ In some cases the compiler uses loop unrolling[^1] optimization to speed up loop
115108
<div class="admonition-body">
116109
```
117110
Inspect under what conditions does the compiler unroll the for loop in the `polynomial` function from the last [lab](@ref horner).
118-
```julia
111+
```@example lab06_intro
119112
function polynomial(a, x)
120113
accumulator = a[end] * one(x)
121114
for i in length(a)-1:-1:1
122115
accumulator = accumulator * x + a[i]
123116
end
124117
accumulator
125118
end
119+
nothing #hide
126120
```
127121

128122
Compare the speed of execution with and without loop unrolling.
@@ -136,24 +130,25 @@ Compare the speed of execution with and without loop unrolling.
136130
<details class = "solution-body">
137131
<summary class = "solution-header">Solution:</summary><p>
138132
```
139-
```julia
133+
```@example lab06_intro
140134
using Test #hide
141135
using BenchmarkTools
142136
a = Tuple(ones(20)) # tuple has known size
143137
ac = collect(a)
144138
x = 2.0
145139
146-
@test polynomial(a,x) == evalpoly(x,a) # compare with built-in function
147-
148140
@code_lowered polynomial(a,x) # cannot be seen here as optimizations are not applied
149141
@code_typed polynomial(a,x) # loop unrolling is not part of type inference optimization
142+
nothing #hide
143+
```
150144

145+
```@repl lab06_intro
151146
@code_llvm polynomial(a,x)
152147
@code_llvm polynomial(ac,x)
153148
```
154149

155150
More than 2x speedup
156-
```julia
151+
```@repl lab06_intro
157152
@btime polynomial($a,$x)
158153
@btime polynomial($ac,$x)
159154
```
@@ -172,7 +167,7 @@ Inlining[^2] is another compiler optimization that allows us to speed up the cod
172167
<header class="admonition-header">Exercise</header>
173168
<div class="admonition-body">
174169
```
175-
Rewrite the `polynomial` function from the last [lab](@ref horner) using recursion and find the length of the coefficients, at which inlining of the recursive calls stops occuring.
170+
Rewrite the `polynomial` function from the last [lab](@ref horner) using recursion and find the length of the coefficients, at which inlining of the recursive calls stops occurring.
176171

177172
```julia
178173
function polynomial(a, x)
@@ -195,7 +190,7 @@ end
195190
<summary class = "solution-header">Solution:</summary><p>
196191
```
197192

198-
```julia
193+
```@example lab06_intro
199194
_polynomial!(ac, x, a...) = _polynomial!(x * ac + a[end], x, a[1:end-1]...)
200195
_polynomial!(ac, x, a) = x * ac + a
201196
polynomial(a, x) = _polynomial!(a[end] * one(x), x, a[1:end-1]...)
@@ -205,20 +200,23 @@ a = Tuple(ones(Int, 21)) # everything less than 22 gets inlined
205200
x = 2
206201
polynomial(a,x) == evalpoly(x,a) # compare with built-in function
207202
208-
209203
@code_lowered polynomial(a,x) # cannot be seen here as optimizations are not applied
210-
@code_typed polynomial(a,x)
211204
@code_llvm polynomial(a,x) # seen here too, but code_typed is a better option
205+
nothing #hide
206+
```
207+
208+
```@repl lab06_intro
209+
@code_typed polynomial(a,x)
212210
```
213211

214212
```@raw html
215213
</p></details>
216214
```
217215

218-
## Let's do some metaprogramming
216+
## AST manipulation: The first steps to metaprogramming
219217
Julia is so called homoiconic language, as it allows the language to reason about its code. This capability is inspired by years of development in other languages such as Lisp, Clojure or Prolog.
220218

221-
There are two easy ways to extract/construct the code structure [^2]
219+
There are two easy ways to extract/construct the code structure [^3]
222220
- parsing code stored in string with internal `Meta.parse`
223221
```@repl lab06_meta
224222
code_parse = Meta.parse("x = 2") # for single line expressions (additional spaces are ignored)
@@ -230,7 +228,7 @@ begin
230228
end
231229
""") # for multiline expressions
232230
```
233-
- constructing an expression using `quote ... end` or simple `:()` syntax
231+
- constructing an expression using `quote ... end` or simple `:()` syntax
234232
```@repl lab06_meta
235233
code_expr = :(x = 2) # for single line expressions (additional spaces are ignored)
236234
code_expr_block = quote
@@ -326,7 +324,7 @@ s = string(ex)
326324
Think of some corner cases, that the method may not handle properly.
327325

328326
**HINTS**:
329-
- Use `Meta.parse` in combination with `replace_i` **only** for checking of correctness.
327+
- Use `Meta.parse` in combination with `replace_i` **ONLY** for checking of correctness.
330328
- You can use the `replace` function.
331329

332330
```@raw html
@@ -345,7 +343,7 @@ does not work in this simple case, because it will replace "i" inside the `sin(z
345343
</p></details>
346344
```
347345

348-
If the exercises so far did not feel very useful let's focus on one, that is actually useful as a part of the [`IntervalArithmetics.jl`](https://github.com/JuliaIntervals/IntervalArithmetic.jl) pkg.
346+
If the exercises so far did not feel very useful let's focus on one, that is similar to a part of the [`IntervalArithmetics.jl`](https://github.com/JuliaIntervals/IntervalArithmetic.jl) pkg.
349347
```@raw html
350348
<div class="admonition is-category-exercise">
351349
<header class="admonition-header">Exercise</header>
@@ -397,7 +395,13 @@ eval(ex)
397395
This kind of manipulation is at the core of some pkgs, such as aforementioned [`IntervalArithmetics.jl`](https://github.com/JuliaIntervals/IntervalArithmetic.jl) where every number is replaced with a narrow interval in order to find some bounds on the result of a computation.
398396

399397

400-
[^2]: Once you understand the recursive structure of expressions, the AST can be constructed manually like any other type.
398+
[^3]: Once you understand the recursive structure of expressions, the AST can be constructed manually like any other type.
401399
---
402400

403401
## Resources
402+
- Julia's manual on [metaprogramming](https://docs.julialang.org/en/v1/manual/metaprogramming/)
403+
- David P. Sanders' [workshop @ JuliaCon 2021](https://www.youtube.com/watch?v=2QLhw6LVaq0)
404+
- Steven Johnson's [keynote talk @ JuliaCon 2019](https://www.youtube.com/watch?v=mSgXWpvQEHE)
405+
- Andy Ferris's [workshop @ JuliaCon 2018](https://www.youtube.com/watch?v=SeqAQHKLNj4)
406+
- [From Macros to DSL](https://github.com/johnmyleswhite/julia_tutorials) by John Myles White
407+
- Notes on [JuliaCompilerPlugin](https://hackmd.io/bVhb97Q4QTWeBQw8Rq4IFw?both#Julia-Compiler-Plugin-Project)

0 commit comments

Comments
 (0)