# Try-catch statement really slow even if catch is never executed

**URL:** <https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744>\
**Category:** Performance\
**Tags:** performance\
**Created:** [December 16, 2022, 3:20pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744 "2022-12-16T15:20:39Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![HashBrown](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hashbrown/32/43601_2.png) [@HashBrown](https://discourse.julialang.org/u/HashBrown)\
**Post date:** [December 16, 2022, 3:20pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/1 "2022-12-16T15:20:39Z")

</div>

Consider the following functions that add the elements of a vector:

```julia
slow(v) = begin
    s = v |> eltype |> zero
    for ele in v
        try 
            s += ele
        catch
            rethrow()
        end
    end
    s
end

fast(v) = begin
    s = v |> eltype |> zero
    for ele in v
        s += ele
    end
    s
end

```

We can time these functions with:

```julia
using BenchmarkTools

const v = rand(Int64, 1_000_000)

@btime slow(v) # 14.6 milliseconds
@btime fast(v) # 155.9 microseconds

```

`slow` is 100 times slower even though the `catch` clause is never executed!

Looking on the internet other languages like C# and Java don’t have this problem (I did not check for myself so take with a grain of salt). Is there any optimization to be done here to fix this? Otherwise `try-catch` clauses become almost impossible to use for a very high performance applications that may very rarely need to execute the slow `catch` clause.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [December 16, 2022, 3:25pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/2 "2022-12-16T15:25:18Z")

</div>

> [@Are exceptions in Julia "Zero Cost"](https://discourse.julialang.org/t/are-exceptions-in-julia-zero-cost/38405/8):
>
> It would be interesting to see what you tried. The test is, of course, not zero-cost: you still have to execute the condition to see if the exception should be triggered. I’m not seeing any difference, although there is some inconsistency in the @btime results: function f(n::Int) x = rand(Int) t = 0 try for i = 1:n i == x && error("bad i value") t += i end catch end return t end function g(n::Int) x = rand(Int) t = 0 f…

I wonder if this is a regression?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 16, 2022, 3:32pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/3 "2022-12-16T15:32:14Z")

</div>

the equivalent of that example is putting the `try` outside the loop:

```julia
slow(v) = begin
    s = v |> eltype |> zero
    try
        for ele in v
            s += ele
        end
    catch
        rethrow()
    end
    s
end

```

---

<div class="post-metadata">

**Author:** ![HashBrown](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hashbrown/32/43601_2.png) [@HashBrown](https://discourse.julialang.org/u/HashBrown)\
**Post date:** [December 16, 2022, 3:35pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/5 "2022-12-16T15:35:33Z")

</div>

For my use case the try would need to be in an inner loop. Actually the `try` is inside a function that is called while processing streams of data. In the case of an error that function resets any state that was altered to provide a transaction like interface.

So I guess we should limit our discussion to poor performance of `try-catch` inside loops.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 16, 2022, 3:42pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/6 "2022-12-16T15:42:06Z")

</div>

From your benchmark it seems that the `try/catch` has an overhead of 14.6 nanoseconds per try\*. Are you sure that really is important in your case? I have the impression that that cannot get any much faster.

- `14.6 milliseconds = 14.6e6 nanoseconds -> 14.6 nanoseconds per try`.

---

<div class="post-metadata">

**Author:** ![HashBrown](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hashbrown/32/43601_2.png) [@HashBrown](https://discourse.julialang.org/u/HashBrown)\
**Post date:** [December 16, 2022, 3:51pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/7 "2022-12-16T15:51:04Z")

</div>

I am trying to build a DAG based workflow abstraction that is nearly as performant as native code.

Example use might look like:

```julia
# Control
control_sum_benchmark(items) = begin
    v = Vector{Float64}(undef, length(items))
    for (i, item) in enumerate(items)
        v[i] = item[1] + item[2]
    end
    v
end

# Experiment
fs = FlowSource(Tuple{Int64, Float64})
x, y = Split(fs)
s = Sum(x, y)
materialize(s)

flow_all_items!(fs, items) = begin
    for item in items
        flow!(fs, item)
    end
end

# Setup
N = 1_000_000
items1 = rand(Int64, N)
items2 = rand(Float64, N)
items = [i for i in zip(items1, items2)]

# Benchmark
@btime flow_all_items!(fs, items) 
@btime control_sum_benchmark(items); 

```

Obviously real use cases would use nodes more complicated than a simple `Sum`. Great care has been taken to achieve such performance. The transaction like feature was a nice addition, but would destroy the native performance promise. (Slows this code down about 50 times over)

So basically if flow! takes about 15ns anyways per item (possible with very large and complex workflows) then the try statement won’t matter. But otherwise all the time is wasted on building a `try-catch` which seems unreasonably more expensive than a comparable `if-else`

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 16, 2022, 3:57pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/8 "2022-12-16T15:57:32Z")

</div>

There seems to be a significant overhead on `catch` statements on C++, from this discussion: [https://pspdfkit.com/blog/2020/performance-overhead-of-exceptions-in-cpp/](https://pspdfkit.com/blog/2020/performance-overhead-of-exceptions-in-cpp/)

I guess one has to write custom exception handling functions using as much information as possible from the type of data one has, to overcome this.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [December 16, 2022, 3:59pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/9 "2022-12-16T15:59:52Z")

</div>

> [@HashBrown](#):
>
> `slow` is 100 times slower even though the `catch` clause is never executed!

I haven’t checked, but I’d be willing to bet that this is due to a difference in vectorization. I’d assume the `try/catch` doesn’t allow the loop to be vectorized, while the “naive” version does.

> [@HashBrown](#):
>
> Looking on the internet other languages like C# and Java don’t have this problem (I did not check for myself so take with a grain of salt)

> [@HashBrown](#):
>
> Otherwise `try-catch` clauses become almost impossible to use for a very high performance applications that may very rarely need to execute the slow `catch` clause.

The usual recommendation, irrespective of language, is “don’t do exception management in a hot loop” - be aware that both C# and Java are (for this purpose) static languages, so they can get a lot more guarantees out of code that doesn’t throw. Very high performance algorithms tend to check the necessary conditions before entering the hot loop, instead of relying on a compiler optimization to do that for them.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 16, 2022, 4:05pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/10 "2022-12-16T16:05:35Z")

</div>

> [@HashBrown](#):
>
> So basically if flow! takes about 15ns anyways per item (possible with very large and complex workflows) then the try statement won’t matter. But otherwise all the time is wasted on building a `try-catch` which seems unreasonably more expensive than a comparable `if-else`

If it takes less than that we are talking about a few CPU operations, where alignment and vectorization may take place. In that case, as pointed above, one would really like to avoid testing inside the loop.

---

<div class="post-metadata">

**Author:** ![HashBrown](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hashbrown/32/43601_2.png) [@HashBrown](https://discourse.julialang.org/u/HashBrown)\
**Post date:** [December 16, 2022, 4:08pm UTC](https://discourse.julialang.org/t/try-catch-statement-really-slow-even-if-catch-is-never-executed/91744/11 "2022-12-16T16:08:09Z")

</div>

Yes. Perhaps it is just best to forget about performance on such a trivial example and focus on more practical use cases where workflows would be larger and more complex (where performance would remain high even with the try-catch).
