You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: lectures/software_engineering/need_for_speed.md
+37-33Lines changed: 37 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -535,9 +535,10 @@ To illustrate, consider this code, where `b` is global
535
535
b = 1.0
536
536
function g(a)
537
537
global b
538
-
for i ∈ 1:1_000_000
538
+
for i in 1:1_000_000
539
539
tmp = a + b
540
540
end
541
+
return tmp
541
542
end
542
543
```
543
544
@@ -559,9 +560,10 @@ If we eliminate the global variable like so
559
560
560
561
```{code-cell} julia
561
562
function g(a, b)
562
-
for i ∈ 1:1_000_000
563
+
for i in 1:1_000_000
563
564
tmp = a + b
564
565
end
566
+
return tmp
565
567
end
566
568
```
567
569
@@ -573,7 +575,7 @@ then execution speed improves dramatically
573
575
574
576
Note that the second run was dramatically faster than the first.
575
577
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.
577
579
578
580
Notice also how small the memory footprint of the execution is.
579
581
@@ -595,15 +597,16 @@ prepend it with `const`
595
597
const b_const = 1.0
596
598
function g(a)
597
599
global b_const
598
-
for i ∈ 1:1_000_000
600
+
for i in 1:1_000_000
599
601
tmp = a + b_const
600
602
end
603
+
return tmp
601
604
end
602
605
```
603
606
604
607
Now the compiler can again generate efficient machine code.
605
608
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.
607
610
608
611
### Composite Types with Abstract Field Types
609
612
@@ -666,9 +669,10 @@ Here's a function that uses the field `a` of our objects
666
669
667
670
```{code-cell} julia
668
671
function f(foo)
669
-
for i ∈ 1:1_000_000
672
+
for i in 1:1_000_000
670
673
tmp = i + foo.a
671
674
end
675
+
return tmp
672
676
end
673
677
```
674
678
@@ -710,8 +714,31 @@ Here's the corresponding machine code
710
714
@code_native f(fc)
711
715
```
712
716
713
-
Much nicer...
714
717
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
+
```
715
742
### Abstract Containers
716
743
717
744
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
721
748
```{code-cell} julia
722
749
function sum_float_array(x::AbstractVector{<:Number})
723
750
sum = 0.0
724
-
for i ∈ eachindex(x)
751
+
for i in eachindex(x)
725
752
sum += x[i]
726
753
end
727
754
return sum
@@ -755,7 +782,7 @@ Here's the same function minus the type annotation in the function signature
755
782
```{code-cell} julia
756
783
function sum_array(x)
757
784
sum = 0.0
758
-
for i ∈ eachindex(x)
785
+
for i in eachindex(x)
759
786
sum += x[i]
760
787
end
761
788
return sum
@@ -782,7 +809,7 @@ Things get tougher for the interpreter when the data type within the array is im
782
809
For example, the following snippet creates an array where the element type is `Any`
783
810
784
811
```{code-cell} julia
785
-
x = Any[ 1/i for i ∈ 1:1e6 ];
812
+
x = Any[ 1/i for i in 1:1e6 ];
786
813
```
787
814
788
815
```{code-cell} julia
@@ -799,29 +826,6 @@ Now summation is much slower and memory management is less efficient.
799
826
800
827
Here are some final comments on performance.
801
828
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
-
825
829
### Summary and Tips
826
830
827
831
Use functions to segregate operations into logically distinct blocks.
0 commit comments