Неожиданный segfault с __gnu_parallel::accumulate

Это действительно сбивает меня с толку, я был бы признателен, если бы кто-нибудь мог мне помочь.

(EDIT: думал, что это шаблонная проблема, я ошибся с этим)

Я хочу добавить несколько копий следующего класса с алгоритмом параллелизованного накопления gnu (хранится в #include <parallel/numeric> )

Класс намеренно мало что делает, я не думаю, что это проблема коллизии потоков?

template<class T>
class NaturalParameters
{
public:
  typedef typename std::vector<T>::iterator iterator;

  NaturalParameters()
    :    m_data(2) //vector with two zeros
  {  }
      
  typename std::vector<T>::const_iterator
  begin() const
  {
    return m_data.begin();
  }

  typename std::vector<T>::const_iterator
  end() const
  {
    return m_data.end();
  }

  NaturalParameters<T>& 
  operator+=(const NaturalParameters<T>& other)
  {
    //do something
    return *this;
  }
      
private:
  std::vector<T> m_data;
};

template<class T>
inline
NaturalParameters<T>
operator+(const NaturalParameters<T>& a, const NaturalParameters<T>& b)
{
  NaturalParameters<T> tmp = a;
  return tmp+=b;
}  

затем я запускаю его

int
main  (int ac, char **av)
{
  std::vector<NaturalParameters<double> > NP(1000);
  NaturalParameters<double> init;
  //the following segfaults
  NaturalParameters<double> NP2 = __gnu_parallel::accumulate(NP.begin(), NP.end(), init ); 
  //The following runs fine
  //NaturalParameters<double> NP2 = std::accumulate(NP.begin(), NP.end(), init ); 
}

Это действительно смущает меня - я понятия не имею, в чем проблема. Я использую g++ 4.4.5 и компилирую с помощью g++ gnu_parallel.cpp -g -fopenmp

ИЗМЕНИТЬ:

Обратите внимание, что это работает: (999 элементов вместо 1000)

 for(size_t i=0;i<1000;++i){

  std::vector<NaturalParameters> ChildrenNP(999);
  NaturalParameters<double> init;
  NaturalParameters<double> NP = __gnu_parallel::accumulate(ChildrenNP.begin(), ChildrenNP.end(), init ); 
  //NaturalParameters<double> NP = std::accumulate(ChildrenNP.begin(), ChildrenNP.end(), init ); 
   }

Обратная трасса:

    Program received signal SIGSEGV, Segmentation fault.
__libc_free (mem=0x12af1) at malloc.c:3709
3709    malloc.c: No such file or directory.
    in malloc.c
(gdb) backtrace
#0  __libc_free (mem=0x12af1) at malloc.c:3709
#1  0x00000000004024f8 in __gnu_cxx::new_allocator<double>::deallocate (this=0x614518, __p=0x12af1) at /usr/include/c++/4.4/ext/new_allocator.h:95
#2  0x0000000000401f0a in std::_Vector_base<double, std::allocator<double> >::_M_deallocate (this=0x614518, __p=0x12af1, __n=18446744073709542049) at /usr/include/c++/4.4/bits/stl_vector.h:146
#3  0x00000000004017b9 in std::_Vector_base<double, std::allocator<double> >::~_Vector_base (this=0x614518, __in_chrg=<value optimized out>) at /usr/include/c++/4.4/bits/stl_vector.h:132
#4  0x00000000004013b9 in std::vector<double, std::allocator<double> >::~vector (this=0x614518, __in_chrg=<value optimized out>) at /usr/include/c++/4.4/bits/stl_vector.h:313
#5  0x00000000004012b8 in NaturalParameters<double>::~NaturalParameters (this=0x614518, __in_chrg=<value optimized out>) at gnu_parallel.cpp:10
#6  0x00000000004023e7 in __gnu_parallel::for_each_template_random_access_ed<__gnu_cxx::__normal_iterator<NaturalParameters<double>*, std::vector<NaturalParameters<double>, std::allocator<NaturalParameters<double> > > >, __gnu_parallel::nothing, __gnu_parallel::accumulate_selector<__gnu_cxx::__normal_iterator<NaturalParameters<double>*, std::vector<NaturalParameters<double>, std::allocator<NaturalParameters<double> > > > >, __gnu_parallel::accumulate_binop_reduct<__gnu_parallel::plus<NaturalParameters<double>, NaturalParameters<double> > >, NaturalParameters<double> > (begin=..., end=..., o=..., f=..., r=..., 
    base=..., output=..., bound=-1) at /usr/include/c++/4.4/parallel/par_loop.h:127
#7  0x0000000000401d70 in std::__parallel::accumulate_switch<__gnu_cxx::__normal_iterator<NaturalParameters<double>*, std::vector<NaturalParameters<double>, std::allocator<NaturalParameters<double> > > >, NaturalParameters<double>, __gnu_parallel::plus<NaturalParameters<double>, NaturalParameters<double> > > (begin=..., end=..., init=..., binary_op=..., parallelism_tag=__gnu_parallel::parallel_unbalanced)
    at /usr/include/c++/4.4/parallel/numeric:99
#8  0x0000000000401655 in std::__parallel::accumulate<__gnu_cxx::__normal_iterator<NaturalParameters<double>*, std::vector<NaturalParameters<double>, std::allocator<NaturalParameters<double> > > >, NaturalParameters<double> > (begin=..., end=..., init=...) at /usr/include/c++/4.4/parallel/numeric:139
#9  0x0000000000400e2c in main (ac=1, av=0x7fffffffe188) at gnu_parallel.cpp:59

person Tom    schedule 24.04.2011    source источник
comment
Я также могу воспроизводить segfaults с некоторыми частями параллельного расширения GNU. Я подозреваю, что на самом деле в реализации есть ошибка.   -  person Konrad Rudolph    schedule 24.04.2011
comment
@sehe, это не отсылка? Это копия, нет?   -  person Tom    schedule 24.04.2011
comment
Вам нужно опубликовать DoSomething в operator+=.   -  person Puppy    schedule 24.04.2011
comment
@DeadMG: почему? делайте травку, чтобы он больше крашился :)   -  person sehe    schedule 24.04.2011
comment
@DeadMG: я компилирую, когда даю (я убрал полезные вещи в operator+=() ), чтобы я мог быть уверен, что это не моя проблема? (Раньше был мьютекс и поэлементное дополнение.)   -  person Tom    schedule 24.04.2011
comment
FWIW: удаление m_data устраняет сбой для меня; g++-4.6 показывает те же проблемы; добавление -D_GLIBCXX_PARALLEL -O0 ничего не решает   -  person sehe    schedule 24.04.2011
comment
Мне кажется, что ваши malloc и free не являются потокобезопасными, или ваша куча повреждена. Стек вызовов показывает segfault в деструкторе m_data.   -  person Collin Dauphinee    schedule 24.04.2011
comment
@dauphic, ммм, не знаю, что с этим делать. Я думаю, что что-то меняется в алгоритме, когда он перебирает более 1000 элементов. 999 элементов - это нормально, так что, может быть, это проблема с реализацией для больших накоплений? (или, может быть, только начинает поток, когда 1000 элементов?)   -  person Tom    schedule 24.04.2011
comment
Я посмотрел на параллельную реализацию accumulate, и, похоже, она не делает ничего другого в зависимости от количества элементов. Вы пробовали тестировать что-то вроде вызова accumulate для вектора из 800 элементов 100 раз?   -  person Collin Dauphinee    schedule 24.04.2011
comment
@dauphic, да, отредактировал мой пост   -  person Tom    schedule 24.04.2011
comment
1001 терпит неудачу, как и все выше 999, которые я пытаюсь   -  person Tom    schedule 24.04.2011
comment
Об ошибке сообщается здесь gcc.gnu.org/bugzilla/show_bug.cgi?id=48750   -  person sehe    schedule 24.04.2011


Ответы (2)


Это определенно похоже на ошибку в libstdc++:

/usr/include/С++/4.4/параллельный/par_loop.h:87

#   pragma omp single
      {
        num_threads = omp_get_num_threads();
        thread_results = static_cast<Result*>(
                            ::operator new(num_threads * sizeof(Result)));
        constructed = new bool[num_threads];
      }

Но строка 127 удаляет его с помощью

delete[] thread_results;

_Очевидно, конструкция thread_results была оптимизирована на одном этапе, но оператор удаления никогда не обновлялся, чтобы отразить это. Оптимизация имеет смысл по сравнению с простым обновлением массива (new Result[num_threads]), поскольку она позволяет избежать создания элементов._

Исправление этого

delete thread_results;

убирает баг. Вы захотите сообщить об этом разработчикам gnu.


Возможны остаточные проблемы с потокобезопасностью std::__cxx1998::vector::operator=. Вы можете понять, что я имею в виду, используя valgrind. Однако вполне возможно, что valgrind сообщает о ложном положительном результате.

Я только что проверил наоборот: при использовании new Result[num_threads] с delete[] (вместо оптимизированной версии в исходном коде GNU) вы получите чистый запуск valgrind. Я почти уверен, что это будет ложное срабатывание, но я обязательно упомяну об этом разработчикам GNU, когда вы сообщите об ошибке.

person sehe    schedule 24.04.2011
comment
Сузили список оставшихся проблем с valgrind. - person sehe; 24.04.2011
comment
+1 Как ты это нашел? Я очень впечатлен - сейчас заполняю отчет об ошибке. - person Tom; 24.04.2011

Ну, это резьба. Резьба тяжелая. Даже с расширениями gomp/parallel. Попробуйте хелгринд (valgrind --tool=helgrind ./t). Вывод такой большой... SO не позволит мне вставить его сюда :)

person sehe    schedule 24.04.2011
comment
Это отличный инструмент, я раньше не встречал опции Helgrind. Спасибо! - person Tom; 24.04.2011