2015-07-15 5 views
5

Это своего рода вопрос, связанный с этим оригинальным вопросом с добавлением новой информации. См. Здесь для первой части, если вам интересно: Struct of arrays, arrays of structs and memory usage patternСтруктура массивов и шаблоны доступа к памяти

Кажется, что с моей первой попыткой настройки структуры массивов для простого класса существует немало проблем. В основном это избыточное выделение памяти для указателей и возможные утечки памяти из выделения этих указателей из vec3_b в предыдущем вопросе.

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

const size_t batch_size = 100; 
    struct vec3_c 
    { 
    size_t x[batch_size]; 
    size_t y[batch_size]; 
    size_t z[batch_size]; 
    }; 

    struct vec3_c vec3_c(size_t x, size_t y, size_t z, size_t index) 
    { 
     struct vec3_c v; 
     v.x[index] = x; 
     v.y[index] = y; 
     v.z[index] = z; 
     return v; 
    } 

     struct vec3_c vc3;   
     for(int i = 0; i < batch_size; i++) 
     { 
      vc3 = vec3_c(i+1, i*i, i*10, i); 
      //printf("vec3c x:%zu, y:%zu, z:%zu\n",vc3.x[i], vc3.y[i], vc3.z[i]); 
      printf("vec3c x:%p, y:%p, z:%p\n",(void*)&vc3.x[i], (void*)&vc3.y[i], (void*)&vc3.z[i]); 
     } 

     ---------------x-----------------|----------------y-----------------|----------------z-----------------| 

0|  0x7fff57489f40 : 140734657765184 | 0x7fff5748a260 : 140734657765984 | 0x7fff5748a580 : 140734657766784 
1|  0x7fff57489f48 : 140734657765192 | 0x7fff5748a268 : 140734657765992 | 0x7fff5748a588 : 140734657766792 
2|  0x7fff57489f50 : 140734657765200 | 0x7fff5748a270 : 140734657766000 | 0x7fff5748a590 : 140734657766800 

с этим обновленным кодом, я должен иметь фиксированный размер ведра, так что я установить его на batch_size 100 только для простых чисел. Заполните vec3c некоторыми данными и выполните аналогичный тест, на этот раз кажется, что каждое значение выровнено в 8 байтовых кусках.

например:

size of vec3  : 24 bytes 
size of vec3a  : 24 bytes 
size of vec3b  : 24 bytes 
size of vec3c  : 2400 bytes 
size of size_t : 8 bytes 
size of int  : 4 bytes 
size of 16 int : 64 bytes 
vec3c x:0x7fff592d2f40, y:0x7fff592d3260, z:0x7fff592d3580 
vec3c x:0x7fff592d2f48, y:0x7fff592d3268, z:0x7fff592d3588 
vec3c x:0x7fff592d2f50, y:0x7fff592d3270, z:0x7fff592d3590 
vec3c x:0x7fff592d2f58, y:0x7fff592d3278, z:0x7fff592d3598 
vec3c x:0x7fff592d2f60, y:0x7fff592d3280, z:0x7fff592d35a0 
vec3c x:0x7fff592d2f68, y:0x7fff592d3288, z:0x7fff592d35a8 
vec3c x:0x7fff592d2f70, y:0x7fff592d3290, z:0x7fff592d35b0 
vec3c x:0x7fff592d2f78, y:0x7fff592d3298, z:0x7fff592d35b8 
vec3c x:0x7fff592d2f80, y:0x7fff592d32a0, z:0x7fff592d35c0 
vec3c x:0x7fff592d2f88, y:0x7fff592d32a8, z:0x7fff592d35c8 

все разделены на 8 байт.

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

с этим новым расположением что-то вроде sizeof (vc3 [0] .x) вернет 8 байтов.

обратно в исходные вопросы:

  1. Является ли моя реализация struct vec3_c правильный способ установки на структуру массива?

  2. с размером партии vec_3c, равным 100, он показывает 2400 байт, но каждый отдельный элемент имеет всего 8 байтов и правильно выровнен, поэтому я мог бы теперь поместить 8 элементов в 1 линию современного кэша процессора?

  3. будет преобразовывать данные, переданные мне, например, типичный формат только массивов структур перевешивает преимущества производительности в состоянии, совместимом с кэшем, и способен работать с несколькими точками данных на вызов команды? Это связано с тем, что обе точки 1 и 2 являются правильными.

ех делает скалярное произведение двух векторов: , что означает, что я мог бы получить скалярное произведение 2 vec3_c за один цикл инструкции?

редактировать еще один вопрос, было бы лучше, чтобы добавить дополнительные 8 байт данных, чтобы сделать эту структуру кратны 32 байта и, возможно, использовать дополнительные 8 байты как рабочее пространство или просто оставить его пустым?

Редактировать Было указано, что моя первоначальная функция инициализации просто путала вещи.Я обновил его к этой форме:

struct vec3_c* vec3_c() 
{ 
    struct vec3_c *v = (struct vec3_c*)malloc(sizeof(struct vec3_c)); 
    v->index = 0; 
    return v; 
} 

struct vec3_c* v3 = vec3_c(); 
    for(size_t i = 0; i < batch_size; i++) 
    { 
     v3->x[i] = i + 1; 
     v3->y[i] = i * i; 
     v3->z[i] = i * 10; 
     printf("index:%d\tvec3c x:%zu, y:%zu, z:%zu\n",i,v3->x[i], v3->y[i], v3->z[i]); 
     printf("index:%zu\tvec3c x:%p, y:%p, z:%p\n",i,(void*)&v3->x[i], (void*)&v3->y[i], (void*)&v3->z[i]); 
    } 
+2

Извините, возможно, я неправильно истолковал всю проблему, но мне кажется, что вы используете vc3 v v неправильно: действительно, функция vec3_c выделяет локальный (неинициализированный) «v», затем устанавливает НЕКОТОРЫЕ поля и возвращает COPY OF v (поля которого содержат непредсказуемые значения, кроме «индексного»). Я вижу, что в вашем примере вас действительно не интересует содержимое vc3, так что же является целью функции vec3_c? –

+0

@GiuseppeGuerrini В моем примере исходного кода было несколько ошибок, которые я со временем выяснил. Я бы использовал эту структуру для пакетного процесса. Vec3 что-то значает как обработку потока или инструкции типа SIMD. – user1610950

ответ

3

Если вы собираетесь иметь много этих А точек, и вы хотите, чтобы иметь возможность выполнить действие на все иксы сразу, то это имеет смысл положить все иксы вместе:

struct PointBatch { 
    size_t x[batchsize]; 
    size_t y[batchsize]; 
    size_t z[batchsize]; 
} 

// More efficient for things like 
// - find the point with the largest X 
// - find the sum of all the points as [xsum, ysum, zsum] 

Если вы обычно работаете на й, у и г одиночных точек, то это имеет смысл ставить каждую точку вместе, как структуры.

struct Point { 
    size_t x; 
    size_t y; 
    size_t z; 
} 

struct Point pointBatch[batchsize]; 

// Better for things like 
// - plot all the points on a graph 
// - determine which points satisfy the equation: x^2 + y^2 < z^2 

N.B.
Обратите внимание, что когда производительность не вопрос, вы, вероятно, обнаружите, что Point/pointBatch подход делает код проще писать и более читаемым, как struct PointBatch не дает вам удобный способ обратиться или пройти вокруг одной точки.

+0

данные, скорее всего, будут переданы мне в виде структуры A, но для более эффективного ее обработки в моих алгоритмах мне придется преобразовывать из A в B один раз и хранить B для обработки их партиями. Я упомянул об этом в первоначальном вопросе, спрашивая о стоимости преобразования после аванса в отношении пакетной обработки для согласованности кеша и обработки стиля SIMD. – user1610950

+0

На самом деле, если вам нужно пробежать все точки в последовательности, структура массивов намного лучше для SIMD. Пример 'x^2 + y^2

+0

Благодарю вас, это направление, к которому я направляюсь, чтобы выполнять пакетную обработку данных SIMD. Сначала мне придется вращать их с AoS на SoA. – user1610950

2

Существует по крайней мере одна проблема с этой части кода:

struct vec3_c vec3_c(size_t x, size_t y, size_t z, size_t index) 
{ 
    struct vec3_c v; 
    v.x[index] = x; 
    v.y[index] = y; 
    v.z[index] = z; 
    return v; 
} 

Вы создаете новый vec3_v-структуру в стеке и возвращает его. Это означает, что каждый вызов будет возвращать новую структуру со всеми неинициализированными элементами, за исключением строки index.

И позже, когда вы делаете (100 раз) vc3 = vec3_c(i+1, i*i, i*10, i);, вы копируете 300 значений (размер структуры), 297 из которых не инициализированы: это действительно неэффективно и вызывает неопределенное поведение!

+0

На самом деле это вызывающий объект, который выделяет пространство для возвращаемого значения. В ABI x86 и amd64 вызывающий абонент передает указатель на это пространство. (И вызываемому придется запустить конструктор, если он еще не был конструктором, так что да, он должен был бы обнулить части, которые он не писал. Или это? Этот код не использует инициализатор, который требуют, чтобы нетронутые элементы были обнулены. Возвращенный 'vec3_c' неинициализирован, за исключением' v. * [index] ') Если вам повезет, компилятор может устранить большую часть этого после встраивания этого конструктора. –

+0

@PeterCordes: Конечно, ты прав! Это C, а не C++ ... И это явное в 6.7.9 10: * Если объект с автоматической продолжительностью хранения не инициализирован явно, его значение неопределенно * –

+0

Даже если это был C++, без нуля-arg конструктор, не будет ли он еще неинициализирован? –