No problem with more than 500 @assert statements in v1.0.3?

I searched the Julia language repository (v1.0.3) with “@assert” and found more than 500 cases. It was surprising to me that the released code base has such a large number of @assert statements. According to my understanding, assertion should be carefully used to check only critical cases our programs cannot be recovered from. Otherwise, we need to raise exceptions so that other programs can hopefully handle the cases. Particularly, when a program is released officially after serious tests, all assertions should be passed and deactivated (or removed) to avoid any performance penalty with assertions.

I scanned several @assert statements in Julia and found some of them can be just removed (or deactivated) before a release or should be replaced by throw(SomeException).

Is there any particular reason I don’t know why the Julia language codebase still has more than 500 @assert statements in the official releases?

Assertions should never happen, assuming the code is correct. It is, however, generally preferable to know that an assertion has failed than to have a program continue incorrectly. Did you see any assertions that appeared to be in performance-critical locations? If not then it’s hard to see what the harm of leaving them is. We could introduce a flag to turn assertions into no ops, but there hasn’t been any demand for it so far.

This is more serious. Any place where @assert is being used in such a fashion should be changed as it is an incorrect usage. In correct code, assertions should never be triggered—they are not for error checking although they are often mistakenly used for such.

5 Likes

It would be great if you could be more specific, with a few examples. Also, if you think throwing a concrete error would be more appropriate, please report issues or make PRs.

I also sometimes use assertions for documentation. E.g. if in the middle of an algorithm it can be mathematically proven that a number x is positive I sometimes do @assert x > 0 "x must be positive since blabla". One could of course also use a comment here, but it is easy to forget updating a comment after each code change.

4 Likes

This. Assertions are like improved comments, since comments inevitably get outdated.

Most production code I’ve seen have tons of assertions, so I don’t consider it a problem as long as it’s used for conditions that should never occur.

That said, in most environments I’ve worked with you can disable assertions in release mode.

1 Like

I don’t have enough knowledge or experience about Julia to submit PRs directly for any fixes or changes to the internal code of the language, but here are a few examples I found so far. It would be great if I can have careful inspection on the many @assert statements in the repository from Julia core developers.

Example 1: julia/base/array.jl

function bitsunionsize(u::Union)
sz = Ref{Csize_t}(0)
algn = Ref{Csize_t}(0)
@assert ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), u, sz, algn) != Cint(0)
return sz
end

If @assert is deactivated, then the ccall() will be not executed and the function will return an incorrect value. The ccall() need to be called in a separate line.

Example 2: julia/base/util.jl L590

    succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool,
        (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}),
         CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize)
    @assert succeeded

Should the ccall() always return true? Otherwise, I think the @assert statement needs to be replaced with throw(SomeException).

3 Likes

Due to my limited knowledge, I am not sure which assertions are in performance-critical locations. However, I think we need to assume any assertions in the source files (except e.g. testing files) in julia/base/ and julia/stdlib/ can make major impact on the performance of other 3rd party programs using the core modules. Who knows if a small function with an assertion statement can be called within a massive iterative loop in an external program?

It is quite surprising to me that there is no strong demand for a flag to deactivate assertions. To help debugging without scarifying the performance of a production release, it is quite a standard feature for most of programming languages, isn’t it?

Yes, there are several examples of @assert that should rather throw exceptions. And also error(... that should rather throw a more specific exception. Mostly non-controversial, they are there for lack of developer time.

Your initial PR does not have to be perfect. In the case at hand, I think you’ll likely find someone to review and maybe provide guidance. Especially if you read this
https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md
carefully.

1 Like

That’s not a good assumption. Online, you’ll find anecdotes, informal reports of research, and published research showing that performance is often not adversely affected by assertions in production code. You’ll find a lot of discussion and opinions and references to literature on how to use assertions in various development and production situations. How this applies to Julia is an open problem.

I can speculate that @assert is not used much in performance critical situations precisely because people know it cannot be disabled. But, that’s just speculation.

1 Like

Wouldn’t it be fairly straightforward to introduce a skip_assert function, and modify the @assert macro to return something like

:(skip_assert($(Val(nameof(__module__)))) || $(esc(ex)) ? $(nothing) : throw(AssertionError($msg)))

(where the part after || is what the macro currently returns)

The skip_assert function would return false by default, but if I define

skip_assert(::Val{:MyModule}) = true

then the @asserts in MyModule would not run (be optimized out by the compiler).

Thanks to the world age mechanism it would be possible to turn assertions on or off in test scripts (or at the REPL), and functions would be recompiled as necessary.

I’ve opened an issue about auditing uses of @assert in Julia’s code base:

https://github.com/JuliaLang/julia/issues/30610

4 Likes

Great to see that @assert will be under scrutiny of Julia gurus. Thanks Stefan.

1 Like

It can be one solution but the first thing we can do is to introduce a simple command option that deactivates all @assert checks in Julia programs. For Jaya and Python, I remember the command options turns off assertion checks.

java -disableassertions 
python -O 

With this feature, Julia developers can insert @assert statements whenever they want for more effective debugging; they don’t have to worry about any performance penalty when their programs execute in a production mode (even though @assert still need to be used carefully).

Would it be difficult to introduce a global read-only flag like debug to Julia?

Following your advice, I dare to submit a PR directly for a fix. Yes, I will read the instructions carefully. Thanks.

https://github.com/JuliaLang/julia/pull/30615

3 Likes

There are two schools of programmatic thought at work: those who use (and like) exceptions and those who don’t. C is infamous for not having exceptions; Java and Python tend to overuse them as support is baked in and well designed.

Regardless of what one may think of exceptions in general, there is no consensus that exceptions are a superior mechanism into aiding error handling (a nice read for a colloquial argumentation in this sense) nor that these can be used extensively. A more balanced view on the matter can be found here.

In Julia, afaik assertion failures can be caught as they throw an AssertionError so I fail to see an actual argument (except a weak esthetic one).

I don’t think they are intended to be, violating an @assert is a bug that should be investigated.

1 Like

Perhaps even though to me it seems an elegant approach to the assertion-exception dichotomy. I do not have a strong opinion on this matter but tend to view simplicity as a virtue rather than a lack.