By: Staging Team
Re-posted from: https://sciml.ai/news/2021/05/24/QNDF/index.html
SciML Ecosystem Update: Improved QNDF Outperforms CVODE On SciMLBenchmarks
By: Staging Team
Re-posted from: https://sciml.ai/news/2021/05/24/QNDF/index.html
SciML Ecosystem Update: Improved QNDF Outperforms CVODE On SciMLBenchmarks
By: Josh Day
Re-posted from: https://www.juliafordatascience.com/performance-tips/
The Julia Language Docs have a great page on performance tips. It's worth a read, but the writing is aimed more towards a computer scientist than a data scientist. Here we'll cover some easy performance wins.
We find the best way to code in Julia is to:
Speeding up bottlenecks is usually straightforward in Julia, often involving drop-in replacements (like using StaticArrays for small, fixed arrays). There are, however, some performance gotchas you should avoid from the start so that you don't need to refactor code later on.
Any code that is performance critical or being benchmarked should be inside a function. - Julia Docs
"Globals" are essentially any variable that isn't inside a function.
# not great
x = 1
f(y) = x + y
# much better
f(x, y) = x + y
const. However, you won't be able to change its value later on.const x = 1
f(y) = x + y
Type stability is the idea that the return type of a function only depends on the types of the inputs, not the input values. Here is an example of a type-unstable function:
function f(x)
x < 10 ? "string" : 1
end
f(0) == "string"
f(100) == 1
The issue with the function above is that Julia's compiler needs to handle multiple cases (output could be either a String or an Int).
Try not to initialize a variable with a type that will change later because of some computation. For example, starting with x = 1 but then performing division x = x/2 will change x from Int to Float64.
When you use abstract types (e.g. Real) for things like arrays and struct fields, Julia must reason about a set of types rather than a specific type.
Side note: Here is an short snippet for printing the subtypes of an abstract type using AbstractTrees:
julia> using AbstractTrees
julia> AbstractTrees.children(x::Type) = subtypes(x)
julia> print_tree(Real)
Real
├─ AbstractFloat
│ ├─ BigFloat
│ ├─ Float16
│ ├─ Float32
│ └─ Float64
├─ AbstractIrrational
│ └─ Irrational
├─ Integer
│ ├─ Bool
│ ├─ Signed
│ │ ├─ BigInt
│ │ ├─ Int128
│ │ ├─ Int16
│ │ ├─ Int32
│ │ ├─ Int64
│ │ └─ Int8
│ └─ Unsigned
│ ├─ UInt128
│ ├─ UInt16
│ ├─ UInt32
│ ├─ UInt64
│ └─ UInt8
└─ RationalReal type tree.x = [] # eltype(x) == Any
x = Float64[] # eltype(x) == Float64
# Ambiguous field type!
struct A
thing::Real
end
# Unambiguous field type!
struct B{T <: Real}
thing::T
end
isconcretetype. You can think about abstract types as things that "don't exist", but instead they define a set of things that do exist.@time and watch your allocations. One of the easiest speed gains is to remove temporary variables from a computation since Julia needs to spend time cleaning these up (garbage collection).
@time do_something() will also include some overhead from the JIT (Just-in-time) compiler. You'll need to run it a second time for the most accurate results. For the most accurate measurement, see the fantastic BenchmarkTools package and its @btime macro.Take a look at this poorly written loop to add 100 to each element of an array:
julia> function add100!(x)
for i in 1:100
x[:] = x .+ 1
end
return x
end
add100 (generic function with 1 method)
julia> data = randn(10^6);
julia> @time add100!(data);
1.115484 seconds (295.91 k allocations: 780.613 MiB, 58.96% gc time, 18.67% compilation time)
julia> @time add100!(data);
0.386620 seconds (200 allocations: 762.947 MiB, 21.76% gc time)There are number of red flags in the last line above:
Let's try again and see that these metrics can be improved by a lot.
julia> function add100!(x)
x .+= 100
end
add100 (generic function with 1 method)
julia> data = randn(10^6);
julia> @time add100!(data);
0.040681 seconds (140.36 k allocations: 8.008 MiB, 98.36% compilation time)
julia> @time add100!(data);
0.001023 secondsIf you are seeing unexpectedly poor @time results, try asking yourself some of the following questions:
Are you creating temporary arrays as an intermediate step in a computation? In Julia, you can fuse multiple element-wise computations with the dot syntax:
x = randn(100)
# No intermediate vectors here! Thanks broadcast fusion!
y = sin.(abs.(x)) .+ 1 ./ (1:100)
!) version of a function I can use?For example, sort!(x) will sort the items of an array x in-place whereas sort(x) will return a sorted copy of x.
Can I use views?
x[:, 1:2], creates a copy! Try using a view of the data instead, which does not copy. You can do this with the view function or @views macro.x = randn(3, 3)
# The view equivalent of x[:, 1:2] (all rows, first 2 cols)
view(x, :, 1:2)
# These are all views
@views begin
x[1:2, 1]
x[:, 1:2]
endf(x::AbstractVector{<:Integer}) vs. f(x::Vector{<:Integer}) (there's no performance penalty because of Julia's JIT). This means views can be used as easy drop-in replacements for array copies!Am I accessing elements in order?
Julia's arrays are stored in column-major order. If you are, for example, iterating through the elements of a Matrix, make sure the inner loop is iterating over rows, since column elements are stored next to each other in your computer's memory:
x = rand(3,3)
for j in 1:3 # column j
for i in 1:3 # row i
x_ij = x[i, j]
perform_calculation_on_element(x_ij)
end
end
You now know how to achieve some easy performance wins in Julia. The Julia community is very concerned (some might say obsessed) with performance, so there is a long rabbit hole to go down if this has piqued your interest. If you want to know more details and even more performance tips, see the link below to the Julia manual's Performance Tips section.
Enjoying Julia For Data Science? Please share us with a friend and follow us on Twitter at @JuliaForDataSci.
Re-posted from: https://bkamins.github.io/julialang/2021/05/20/odsc.html
This time my post is going to be shorter than usual (but hopefully as exciting :)).
I am going to give a tutorial on DataFrames.jl during
ODSC Europe 2021 conference.
You can find the link to the outline of my workshop
“DataFrames.jl: a Perfect Sidekick for Your Next Data Science Project”
here.
The materials that are going to be presented during the tutorial can be found in
this GitHub repository. This is a first workshop where I decided not to
use Jupyter Notebook, but just classic Julia REPL so I hope participants will
also get convinced by it. Personally, I prefer using Julia REPL and plain old
text editor combo in all my work over any other currently available option.
Finally, I have written a small pre-conference post, that was published on ODSC
blog here. It was meant to attract the attention of people who do not
use Julia yet. And to be clear: the benchmarks shown there were not something I
spent days to hand pick a favorable scenario for DataFrames.jl. It was the first
thing that came to my mind to check. Actually, initially I did not even consider
benchmarking against Polars as it is not that popular yet. However, later
I wanted to check for myself how it compares. And indeed Polars turns out to
have a really decent implementation.
I hope you will find the shared materials useful!