2015-12-25 2 views
0

У меня есть программа (полный код here), который покидает вокруг 46000th итерации:Программа на C++ выходит без ошибок. Как отлаживать?

{ 
    PROCESSER<MONO_CONT> processer; 
    c_start = std::clock(); 
    for (unsigned long long i = 0; i < iterations; i++) { 
     BloombergLP::bdlma::BufferedSequentialAllocatoralloc(pool, sizeof(pool)); 
     MONO_CONT* container = new(alloc) MONO_CONT(&alloc); 
     container->reserve(elements); 
     processer(container, elements); 
    } 
    c_end = std::clock(); 
    std::cout << (c_end - c_start) * 1.0/CLOCKS_PER_SEC << " "; 
} 

В этом случае MONO_CONT является vector<string, scoped_allocator_adaptor<alloc_adaptor<BloombergLP::bdlma::BufferedSequentialAllocator>>>. Я понимаю, что scoped_allocator_adaptor будет следить за тем, чтобы поставляемый распределитель использовался для распределения для передаваемых строк, тем самым гарантируя, что строки будут освобождены в конце каждой итерации цикла (избегая предложения 1201ProgramAlarm для проблемы). alloc_adapter - это всего лишь оболочка, чтобы распределители Bloomberg соответствовали правильному интерфейсу.

PROCESSER является следующим шаблонным функтором, который просто выполняет основные операции шаблонного контейнера, MONO_CONT:

template<typename DS2> 
struct process_DS2 { 
    void operator() (DS2 *ds2, size_t elements) { 
     escape(ds2); 
     for (size_t i = 0; i < elements; i++) { 
      ds2->emplace_back(&random_data[random_positions[i]], random_lengths[i]); 
     } 
     clobber(); 
    } 
}; 

Обратите внимание, что escape и clobber просто какой-то магия, что не делать ничего другого, кроме поражения оптимизатора (см this talk если вы заинтересованы). random_data - это всего лишь массив из char с мусором. random_positions определяет действительные индексы на random_data. random_lengths определяет допустимую длину строки (не выходит из конца данных мусора), начиная с соответствующей позиции в random_positions.

У меня есть подобный код, который работает точно такое же число итераций, и не подведет:

{ 
    PROCESSER<MONO_CONT> processer; 
    c_start = std::clock(); 
    for (unsigned long long i = 0; i < iterations; i++) { 
     BloombergLP::bdlma::BufferedSequentialAllocator alloc(pool, sizeof(pool)); 
     MONO_CONT container(&alloc); 
     container.reserve(elements); 
     processer(&container, elements); 
    } 
    c_end = std::clock(); 
    std::cout << (c_end - c_start) * 1.0/CLOCKS_PER_SEC << " "; 
} 

Основное различие между этими двумя отрывками является то, что в первом, я new ИНГ контейнер в распределитель, а затем передать распределитель в контейнер, опираясь на уничтожение распределителя для освобождения всей памяти контейнера (без фактического вызова деструктора самого контейнера). Во втором фрагменте я разрешаю более естественное уничтожение контейнера, выходя из области видимости в конце каждой итерации цикла.

Я строю это с Clang, работая в контейнере Docker на Debian. Предложения о том, что может быть проблемой, и как я могу начать отлаживать это?

+1

В первом примере используется 'placement-new'. Итак, где явный вызов деструктора? https://isocpp.org/wiki/faq/dtors#placement-new – PaulMcKenzie

+0

@PaulMcKenzie Насколько я знаю, определено, что он не вызывает деструктор, учитывая, что деструктор распределителя очищает память, и я никогда не получаю доступ к контейнер после того, как его память удалена.Взгляните на последние 3 параграфа главы 3 для более подробной информации о том, почему я это делаю: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0089r0.pdf – GBleaney

+0

Если вы обеспокоены эффективностью распределителя, подумайте о создании объекта «MONO_CONT» за пределами цикла, «clear» («) после каждой итерации, а затем« reserve() »достигли отметки о высокой воде и редко нуждаются в каком-либо распределении , –

ответ

1

В то время как вы полагаетесь на уничтожении распределителя, чтобы освободить память, выделенную для container, это не освободит память, используемую содержавшиеся string с, что не будет использовать аллокатор для vector, но будет использовать глобальный куча (new). Когда в программе заканчивается память, она выходит, ничего не сообщая, возможно, потому, что у нее недостаточно памяти для этого.

В вашей второй версии container уничтожен, что освободит выделенные строки при уничтожении vector.

Что касается того, как отлаживать что-то подобное, обычный совет «попробуйте выполнить его в отладчике» - это начало. Если вы запустили приложение в отладчике, оно может сломаться при создании или броске исключения std::bad_alloc. И вы можете контролировать использование памяти процесса.

+0

. Я добавил дополнительную статью на мой пост, объяснив мое использование 'scoped_allocator_adaptor', которое, как мне кажется, должно было охватывать здесь – GBleaney

+0

После некоторого дополнительного копания, кажется, вы правы. Я забыл шаблон строки, чтобы принять пользовательский распределитель из 'scoped_allocator_adaptor', поэтому он вместо этого переходил в кучу – GBleaney

Смежные вопросы