Skip to content

Commit 147d53f

Browse files
committed
A few more tweaks on the compiliation
1 parent c1bc971 commit 147d53f

File tree

1 file changed

+37
-33
lines changed

1 file changed

+37
-33
lines changed

lectures/software_engineering/need_for_speed.md

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,10 @@ To illustrate, consider this code, where `b` is global
535535
b = 1.0
536536
function g(a)
537537
global b
538-
for i 1:1_000_000
538+
for i in 1:1_000_000
539539
tmp = a + b
540540
end
541+
return tmp
541542
end
542543
```
543544

@@ -559,9 +560,10 @@ If we eliminate the global variable like so
559560

560561
```{code-cell} julia
561562
function g(a, b)
562-
for i 1:1_000_000
563+
for i in 1:1_000_000
563564
tmp = a + b
564565
end
566+
return tmp
565567
end
566568
```
567569

@@ -573,7 +575,7 @@ then execution speed improves dramatically
573575

574576
Note that the second run was dramatically faster than the first.
575577

576-
That's because the first call included the time for JIT compilaiton.
578+
That's because the first call included the time for JIT compilation.
577579

578580
Notice also how small the memory footprint of the execution is.
579581

@@ -595,15 +597,16 @@ prepend it with `const`
595597
const b_const = 1.0
596598
function g(a)
597599
global b_const
598-
for i 1:1_000_000
600+
for i in 1:1_000_000
599601
tmp = a + b_const
600602
end
603+
return tmp
601604
end
602605
```
603606

604607
Now the compiler can again generate efficient machine code.
605608

606-
We'll leave you to experiment with it.
609+
However, global variables within a function is almost always a bad idea. Instead, the `b_const` should be passed as a parameter to the function.
607610

608611
### Composite Types with Abstract Field Types
609612

@@ -666,9 +669,10 @@ Here's a function that uses the field `a` of our objects
666669

667670
```{code-cell} julia
668671
function f(foo)
669-
for i 1:1_000_000
672+
for i in 1:1_000_000
670673
tmp = i + foo.a
671674
end
675+
return tmp
672676
end
673677
```
674678

@@ -710,8 +714,31 @@ Here's the corresponding machine code
710714
@code_native f(fc)
711715
```
712716

713-
Much nicer...
714717

718+
Finally, note that if we compile a slightly different version of the function, which doesn't actually return the value
719+
```{code-cell} julia
720+
function f_no_return(foo)
721+
for i in 1:1_000_000
722+
tmp = i + foo.a
723+
end
724+
end
725+
```
726+
That
727+
```{code-cell} julia
728+
@btime f_no_return($fc)
729+
```
730+
Which seems improbably small. The machine code gives a hint,
731+
```{code-cell} julia
732+
@code_native f_no_return(fc)
733+
```
734+
735+
Note that in this case, the machine code is doing nothing. The compiler was able to prove that the function had no side effects and hence it could simply ignore the inputs.
736+
737+
This is not the case with the abstract case, because the compiler is unable to prove this invariant.
738+
739+
```{code-cell} julia
740+
@btime f_no_return($fa)
741+
```
715742
### Abstract Containers
716743

717744
Another way we can run into trouble is with abstract container types.
@@ -721,7 +748,7 @@ Consider the following function, which essentially does the same job as Julia's
721748
```{code-cell} julia
722749
function sum_float_array(x::AbstractVector{<:Number})
723750
sum = 0.0
724-
for i eachindex(x)
751+
for i in eachindex(x)
725752
sum += x[i]
726753
end
727754
return sum
@@ -755,7 +782,7 @@ Here's the same function minus the type annotation in the function signature
755782
```{code-cell} julia
756783
function sum_array(x)
757784
sum = 0.0
758-
for i eachindex(x)
785+
for i in eachindex(x)
759786
sum += x[i]
760787
end
761788
return sum
@@ -782,7 +809,7 @@ Things get tougher for the interpreter when the data type within the array is im
782809
For example, the following snippet creates an array where the element type is `Any`
783810

784811
```{code-cell} julia
785-
x = Any[ 1/i for i 1:1e6 ];
812+
x = Any[ 1/i for i in 1:1e6 ];
786813
```
787814

788815
```{code-cell} julia
@@ -799,29 +826,6 @@ Now summation is much slower and memory management is less efficient.
799826

800827
Here are some final comments on performance.
801828

802-
### Explicit Typing
803-
804-
Writing fast Julia code amounts to writing Julia from which the compiler can
805-
generate efficient machine code.
806-
807-
For this, Julia needs to know about the type of data it's processing as early as possible.
808-
809-
We could hard code the type of all variables and function arguments but this comes at a cost.
810-
811-
Our code becomes more cumbersome and less generic.
812-
813-
We are starting to loose the advantages that drew us to Julia in the first place.
814-
815-
Moreover, explicitly typing everything is not necessary for optimal performance.
816-
817-
The Julia compiler is smart and can often infer types perfectly well, without
818-
any performance cost.
819-
820-
What we really want to do is
821-
822-
* keep our code simple, elegant and generic
823-
* help the compiler out in situations where it's liable to get tripped up
824-
825829
### Summary and Tips
826830

827831
Use functions to segregate operations into logically distinct blocks.

0 commit comments

Comments
 (0)