SIGSEGV using Eigen and std::vector
Asked Answered
E

1

7

I'm aware of the alignment issue arising when using Eigen's types in conjunction with dynamic memory. Thus, I've decided to disable vectorization with Eigen::DontAlign in accord with this page, yet the following code still raises SIGSEGVs consistently on execution. I'd be very happy if someone could shed some light upon why this happens. From my point of view, using Eigen::DontAlign should have gotten me rid of alignment intricacies.

#include <Eigen/Dense>
#include <vector>

int main()
{
    using vec_t = Eigen::Matrix< double, 4, 1, Eigen::DontAlign >;
    std::vector< vec_t > foo;
    foo.emplace_back( 0.0, 0.0, 0.0, 1.0 );
    vec_t vec{ -4.0, 1.0, 3.0, 1.0 };
    foo.push_back( vec );
}

GDB Output:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000408bed in Eigen::internal::evaluator<Eigen::PlainObjectBase<Eigen::Matrix<double, 4, 1, 2, 4, 1> > >::packet<0, double __vector(4)>(long long, long long) const (this=0x22fa90, row=0, col=0)
    at C:/Dev/Eigen/Eigen/src/Core/CoreEvaluators.h:197
197           return ploadt<PacketType, LoadMode>(m_data + row + col * m_outerStride.value());
(gdb) l
192       PacketType packet(Index row, Index col) const
193       {
194         if (IsRowMajor)
195           return ploadt<PacketType, LoadMode>(m_data + row * m_outerStride.value() + col);
196         else
197           return ploadt<PacketType, LoadMode>(m_data + row + col * m_outerStride.value());
198       }
199
200       template<int LoadMode, typename PacketType>
201       EIGEN_STRONG_INLINE
(gdb) bt
#0  0x0000000000408bed in Eigen::internal::evaluator<Eigen::PlainObjectBase<Eigen::Matrix<double, 4, 1, 2, 4, 1> > >::packet<0, double __vector(4)>(long long, long long) const (this=0x22fa90, row=0, col=0) at C:/Dev/Eigen/Eigen/src/Core/CoreEvaluators.h:197
#1  0x000000000040678a in Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>::assignPacket<0, 0, double __vector(4)>(long long, long long) (this=0x22fa60, row=0, col=0) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:652
#2  0x0000000000406863 in Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>::assignPacketByOuterInner<0, 0, double __vector(4)>(long long, long long) (this=0x22fa60, outer=0, inner=0) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:666
#3  0x00000000004068e0 in Eigen::internal::copy_using_evaluator_innervec_CompleteUnrolling<Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>, 0, 4>::run (kernel=...) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:274
#4  0x00000000004064d5 in Eigen::internal::dense_assignment_loop<Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 2, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>, 2, 2>::run (kernel=...) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:468
#5  0x0000000000406693 in Eigen::internal::call_dense_assignment_loop<Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::internal::assign_op<double, double> > (dst=..., src=..., func=...) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:724
#6  0x00000000004062ab in Eigen::internal::Assignment<Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::internal::assign_op<double, double>, Eigen::internal::Dense2Dense, void>::run (dst=..., src=..., func=...) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:862
#7  0x00000000004065a3 in Eigen::internal::call_assignment_no_alias<Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::internal::assign_op<double, double> > (dst=..., src=..., func=...) at C:/Dev/Eigen/Eigen/src/Core/AssignEvaluator.h:819
#8  0x0000000000405bdc in Eigen::PlainObjectBase<Eigen::Matrix<double, 4, 1, 2, 4, 1> >::_set_noalias<Eigen::Matrix<double, 4, 1, 2, 4, 1> > (this=0x3c2680, other=...) at C:/Dev/Eigen/Eigen/src/Core/PlainObjectBase.h:728
#9  0x0000000000406070 in Eigen::Matrix<double, 4, 1, 2, 4, 1>::Matrix(Eigen::Matrix<double, 4, 1, 2, 4, 1>&&) (this=0x3c2680, other=<unknown type in F:\GitHub\Radon\bin\Radon.exe, CU 0x0, DIE 0x1faef>) at C:/Dev/Eigen/Eigen/src/Core/Matrix.h:278
#10 0x000000000040c666 in std::_Construct<Eigen::Matrix<double, 4, 1, 2, 4, 1>, Eigen::Matrix<double, 4, 1, 2, 4, 1> >(Eigen::Matrix<double, 4, 1, 2, 4, 1>*, Eigen::Matrix<double, 4, 1, 2, 4, 1>&&) (__p=0x3c2680, __args#0=<unknown type in F:\GitHub\Radon\bin\Radon.exe, CU 0x0, DIE 0x1faef>) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/stl_construct.h:75
#11 0x000000000040ad69 in std::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<Eigen::Matrix<double, 4, 1, 2, 4, 1>*>, Eigen::Matrix<double, 4, 1, 2, 4, 1>*> (__first=..., __last=..., __result=0x3c2680) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/stl_uninitialized.h:75
#12 0x000000000040c8bf in std::uninitialized_copy<std::move_iterator<Eigen::Matrix<double, 4, 1, 2, 4, 1>*>, Eigen::Matrix<double, 4, 1, 2, 4, 1>*> (__first=..., __last=..., __result=0x3c2680) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/stl_uninitialized.h:126
#13 0x000000000040c9ff in std::__uninitialized_copy_a<std::move_iterator<Eigen::Matrix<double, 4, 1, 2, 4, 1>*>, Eigen::Matrix<double, 4, 1, 2, 4, 1>*, Eigen::Matrix<double, 4, 1, 2, 4, 1> > (__first=..., __last=..., __result=0x3c2680) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/stl_uninitialized.h:281
#14 0x000000000040ccaf in std::__uninitialized_move_if_noexcept_a<Eigen::Matrix<double, 4, 1, 2, 4, 1>*, Eigen::Matrix<double, 4, 1, 2, 4, 1>*, std::allocator<Eigen::Matrix<double, 4, 1, 2, 4, 1> > > (__first=0x3c2980, __last=0x3c29a0, __result=0x3c2680, __alloc=...) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/stl_uninitialized.h:304
#15 0x000000000040b648 in std::vector<Eigen::Matrix<double, 4, 1, 2, 4, 1>, std::allocator<Eigen::Matrix<double, 4, 1, 2, 4, 1> > >::_M_emplace_back_aux<Eigen::Matrix<double, 4, 1, 2, 4, 1> const&> (this=0x22fde0, __args#0=...) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/vector.tcc:420
#16 0x000000000040ba16 in std::vector<Eigen::Matrix<double, 4, 1, 2, 4, 1>, std::allocator<Eigen::Matrix<double, 4, 1, 2, 4, 1> > >::push_back (this=0x22fde0, __x=...) at C:/Dev/mingw-w64/x86_64-6.3.0-posix-seh-rt_v5-rev1/mingw64/lib/gcc/x86_64-w64-mingw32/6.3.0/include/c++/bits/stl_vector.h:924
#17 0x000000000040167f in main () at F:\GitHub\Radon\Radon.cxx:10
(gdb)

Environment: Windows 7 64-bit SP1

Hardware: i7-6800k (AVX2 support)

Compiler: MinGW-w64 (x86_64-6.3.0-posix-seh-rt_v5-rev1)

Flags -Wall -Wextra -pedantic-errors -Wno-deprecated -std=c++14 -march=native -g -ggdb -fno-omit-frame-pointer

Eigen version: 3.3.2

Enzymology answered 11/2, 2017 at 21:44 Comment(2)
Shouldn't you rather look at eigen.tuxfamily.org/dox/group__TopicStlContainers.html, section "The case of std::vector"?Kitchener
@Kitchener I think that advice only pertains to the aligned case; why else should I be forced to use an aligning allocator for unaligned classes? Anyway, I went ahead and tried it just to be sure but unfortunately it still SIGSEGVd.Enzymology
T
4

I don't have a solution per se, but more insight as to what happened. First off, I can reproduce with gcc 5.3.0 on MinGW as well, so it's not just you. Second of all, by running gcc -march=native -Q --help=target ... | grep enabled I obtained a list of the the flags enabled by -march=native on my (different) setup (I'm using an older i5, etc.). I divided those in a binary fashion until I came up with the list of two flags (in my case) needed to trigger the same error in a modified version of your MCVE (+1):

#include <Eigen/Core>
#include <Eigen/StdVector>
#include <iostream>
#include <vector>

using vec_t = Eigen::Matrix< double, 4, 1, Eigen::DontAlign >;
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(vec_t)

int main()
{
    //  , Eigen::aligned_allocator<vec_t> 
    std::vector< vec_t> foo;
    std::cout << "Before emplace_back\n";
    foo.emplace_back( 0.0, 0.0, 0.0, 1.0 );
    std::cout << "Before vec\n";
    vec_t vec{ -4.0, 1.0, 3.0, 1.0 };
    std::cout << "Before push_back\n";
    foo.push_back( vec );
    std::cout << "After push_back\n";
    return 0;
}

They were -mavx and -mf16c, two flags that enable AVX instructions. The gdb output is slightly different, as I did use the instructions here:

Program received signal SIGSEGV, Segmentation fault. 0x0000000000403ca4 in Eigen::internal::ploadu(Eigen::internal::unpacket_traits::type const*) ( from=0x312340) at C:/include/Eigen3.3.2/Eigen/src/Core/arch/AVX/PacketMath.h:218 218 template<> EIGEN_STRONG_INLINE Packet4d ploadu(const double* from) { EIGEN_DEBUG_UNALIGNED_LOAD return _mm256_loadu_pd(from); }

So we see that Eigen is still vectorizing with AVX using unaligned loads, as we only told Eigen not to align and we didn't specify -DEIGEN_DONT_VECTORIZE in the preprocessor (or as a #define before including Eigen). Adding that removes the segfault. So, the workaround is to disable vectorization (even unaligned). If that's enough, great. If not, wait for ggael & co. (or someone else) to figure out a better solution.

Tret answered 12/2, 2017 at 9:8 Comment(2)
Compiling with -DEIGEN_DONT_VECTORIZE seems to resolve the issue temporarily, thanks. However, I would really like to take advantage of SIMD. Eigen's documentation suggests that it supports SIMD on unaligned data by storing to / loading from the concerned registers when needed (when specifying Eigen::DontAlign). So I'm still under the impression that this is a bug on Eigen's part, either in the code or in the documentation.Enzymology
Hence my comment about waiting for ggael & co. (Eigen's devs).Tret

© 2022 - 2024 — McMap. All rights reserved.