почему параллельное чтение массива вызывает утечки памяти? - PullRequest
0 голосов
/ 30 сентября 2019

Я читаю массив параллельно, используя openmp. Ниже приведен минимальный воспроизводимый пример:

#include <cstdint>
#include <cstdlib>
#include <immintrin.h>
#include <iostream>
#include <memory>
#include <omp.h>

int main(int argc, char* argv[]){
  // align to cache line, which is 512 bits or 64 bytes
  size_t actualSize = 2048;

  uint8_t* array = static_cast<uint8_t *>(aligned_alloc(64, actualSize));
  for(size_t i = 0; i < actualSize; i++){
    // initialize values
    array[i] = rand() % 256;
  }

  __m256i sum_v = _mm256_setzero_si256 ();
  #pragma omp parallel for
  for (size_t i = 0; i < actualSize; i+=32){
    __m256i v1 = _mm256_load_si256((const __m256i *) array+i);
    // i understand that there is a race condition here, but I'm just
    // concerned with the memory leaks
    sum_v = _mm256_add_epi8 (sum_v, v1);
  }

  // just to keep compiler from optimizing out sum_v
  uint8_t result = _mm256_extract_epi8 (sum_v, 0);

  std::cout << "result: " << result << std::endl;

  free(array);
  return 0;
}

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

Я скомпилирую это с g++ -Wall -g -std=c++1y -march=native -mtune=native -fopenmp -O3 -g minimal-memleaks.cpp,Когда я запускаю эту программу, используя valgrind ./a.out, я получаю утечку памяти, часть которой копируется ниже

==7688== Thread 8:
==7688== Invalid read of size 32
==7688==    at 0x108D30: _mm256_add_epi8 (avx2intrin.h:107)
==7688==    by 0x108D30: main._omp_fn.0 (minimal-memleaks.cpp:25)
==7688==    by 0x51DB95D: ??? (in /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0)
==7688==    by 0x5FA66DA: start_thread (pthread_create.c:463)
==7688==    by 0x551588E: clone (clone.S:95)
==7688==  Address 0x61e1980 is 29,280 bytes inside an unallocated block of size 4,077,760 in arena "client"

полный вывод доступен здесь: https://pastebin.com/qr0W9FGD

Я могуКажется, не понимаю, почему. Сначала я думал, что цикл проходит за 2048 байтов, которые я выделил, но моя математика говорит, что это не должно. Я прочитал в блоках 32, и добавление 32 к i в конечном итоге будет равно 2048, когда цикл должен остановиться. Я также подумал, что, возможно, основной поток закрывался до дочерних потоков, но мои исследования показывают, что основной поток не будет закрываться, пока не будут созданы потоки, созданные в цикле #pragma omp parallel for. Это неправильно?

Спасибо за любую помощь, которую вы можете предоставить.

1 Ответ

6 голосов
/ 30 сентября 2019

Это не утечка памяти. Вы читаете и / или повреждаете память.

 for (size_t i = 0; i < actualSize; i+=32){
   __m256i v1 = _mm256_load_si256((const __m256i *) array+i);

Здесь вы запускаете конец массива. actualSize - это размер выделенного массива в байтах .

__m256i - это тип данных длиной 32 байта.

(const __m256i *) array

Это преобразует указатель в указатель на 32-байтовый объект.

Способ, которым добавление указателя работает в C ++, заключается в том, что добавление единицы к указателю продвигает указатель к следующему объекту. , поэтому

 array+1

- это то место, где находится следующий 32-байтовый объект. Это 32 байта после array.

Так что, если вы решите, какой адрес ваш цикл for заканчивает чтение, должно быть очевидно, что он уходит с конца вашего массива в никогда и никогда не приземлится,и Вальгринд лает на вас из-за этого.

Ваш for цикл, вероятно, должен быть:

for (size_t i = 0; i < actualSize/32; ++i){
  __m256i v1 = _mm256_load_si256((const __m256i *) array+i);
...