-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Hello.
I have noticed pretty significant performance regressions when partially applying data types, like convert $ Float64, round $ Int, parse $ Int etc. To reproduce, take this as an example:
using PartialFunctions, BenchmarkTools
data = rand(1:20, 1000) .|> string
@btime val_native = mapreduce( x -> parse(Int, x), +, data)
# -> 18.243 μs (1 allocation: 16 bytes)
@btime val_partial = mapreduce( parse $ Int, +, data)
# -> 129.363 μs (958 allocations: 14.98 KiB)
The reason for this can be found when inspecting the type:
typeof(parse $ Int)
# -> PartialFunctions.PartialFunction{nothing, nothing, typeof(parse), Tuple{DataType}, NamedTuple{(), Tuple{}}}
Notice the Tuple{DataType} in there. When compiling all the compiler sees is this signature: parse(::DataType, ::String), which is not enough information to compile type stable code.
The reason that happens is that
typeof(Int) # -> DataType
# same for all other data types
and that DataType is technically a concrete type but practically not really. Ideally this should be replaced with Type{Int}.
One way to fix this would be to supply a new specialized method for the $ function,
($)(f::Function, ::Type{T}) where T = ...
wherein the type parameter is manually set to Type{T}.
This problem has been solved before by the Fix1 type, see here.
Alternatively, I experimentally created my own partial application package. You can have a look how I solved the problem here.
I would offer the PR with a fix myself, but I have had very little time for hobby programming the past weeks, so I decided to just notify you about the problem instead 🤷♂️.