Julia: Segfault in factorizations inside threaded loops

Created on 24 Jan 2020  路  11Comments  路  Source: JuliaLang/julia

As the tittle says, I am getting segfaults in Julia 1.3.1 (linux) when trying to factorize matrices inside threaded loops, i.e.

using LinearAlgebra, SparseArrays

function test(n::Integer)::Float64
    A = sprand(n, n, .2)
    x = qr(A) \ rand(n)
    return norm(x)
end

Threads.@threads for i = 1:100
  test(i+100)
end

results in a segfault whenever JULIA_NUM_THREADS>1. I am using

julia> versioninfo()
Julia Version 1.3.1
Commit 2d5741174c (2019-12-30 21:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Xeon(R) CPU E5-2695 v4 @ 2.10GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, broadwell)
Environment:
  JULIA_NUM_THREADS = 36

Same problem is observed with LU and other factorizations.

bug linear algebra multithreading sparse

Most helpful comment

SuiteSparse is designed to handle this; all its functions are all thread-safe. There is a SuiteSparse_config global struct but it is not meant to be modified by multiple threads. It is meant to be initialized once (say when Julia starts) and then it is never modified after that. It has library-wide pointers to malloc, free, etc. So if there's a segfault, it's because Julia is using my routines incorrectly.

I have a few globals that exist if UMFPACK is compiled in debug mode, just for testing and diagnostics, but that requires editing the source code to enable. It can't be enabled with compile time flags, so it's not easy to turn on accidentally.

So this is a bug in Julia's interface. Can you point me to where you access the SuiteSparse_config global struct? I could then suggest a fix.

All 11 comments

The sparse factorizations are probably not thread safe. They use a common C struct for all kinds of settings and allocations.

@andreasnoack It seems so. At least, there are no segfaults with dense matrices. Is there any plan to fix this behavior in the future or is this something inside LAPACK and out of the control of Julia?

The sparse factorizations are handled by SuiteSparse which generally uses a global struct. LAPACK is structured very differently and is less prone to these kinds of issues. I do think we can fix this in Julia but I'm not sure what the right solution is. Maybe @vtjnash can provide some hints to what a good solution would be.

IIRC, LAPACK used to have many of issues like that too (since Fortran encourages usage of global/static variables), but they got shaken out a few years ago. Maybe just needs someone to do the same for SuiteSparse?

I assume making this code be native Julia and as fast as lapack won't be our approach? I understand it would be rediculously hard, but otoh, it would be great for programs using linear algebra on non blas types

For non-blas types you often do not need to put in the same level of effort to get good performance. Getting. A KLU equivalent in Julia would be a good start.

We probably should file these issues upstream. It helps that suitesparse is now on GitHub.

The sparse factorizations are probably not thread safe. They use a common C struct for all kinds of settings and allocations.

Is there a way to get these into thread local storage?

Maybe we can ask @DrTimothyAldenDavis if there are any good resources for how to use SuiteSparse in multithreaded code.

SuiteSparse is designed to handle this; all its functions are all thread-safe. There is a SuiteSparse_config global struct but it is not meant to be modified by multiple threads. It is meant to be initialized once (say when Julia starts) and then it is never modified after that. It has library-wide pointers to malloc, free, etc. So if there's a segfault, it's because Julia is using my routines incorrectly.

I have a few globals that exist if UMFPACK is compiled in debug mode, just for testing and diagnostics, but that requires editing the source code to enable. It can't be enabled with compile time flags, so it's not easy to turn on accidentally.

So this is a bug in Julia's interface. Can you point me to where you access the SuiteSparse_config global struct? I could then suggest a fix.

I also never do any memory allocations that get placed in a global struct. You might be refering to the Cholmod_Common object, created here: https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/79f25b523ae0fd81abe1ffb0efd9005f2e6eef33/CHOLMOD/Core/cholmod_common.c#L55

But that is not a global extern. It is not meant for multiple user threads to use. Each thread that calls CHOLMOD (and thus the SuiteSparseQR) need their own Common object.

I do allocate memory and put it there, but it is thread-local, not global extern. If Julia is attempting to use the Common object for multiple threads, it will segfault. You have to give each thread its own.

Thanks for the explanation. It's indeed cholmod_common and not SuiteSparse_config that is causing the issue here. In the current wrappers, we only have a single global instance of cholmod_common. Our SuiteSparse wrappers predate multithreading in Julia which is why the issue shows up now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dpsanders picture dpsanders  路  3Comments

musm picture musm  路  3Comments

omus picture omus  路  3Comments

sbromberger picture sbromberger  路  3Comments

StefanKarpinski picture StefanKarpinski  路  3Comments