Наилучшей производительности можно достичь, если структуры настолько малы, что некоторые из них могут поместиться в строку кэша, а распределение выполняется таким образом, чтобы обеспечить вероятность того, что структуры, к которым осуществляется доступ вскоре после друг друга, действительно будут помещаться в одну и ту же строку кэша.
Если структуры намного больше, чем строки кэша, наилучшая производительность может быть достигнута за счет обеспечения того, чтобы части структуры, к которым часто обращаются в тесной последовательности, находились рядом друг с другом.
Рассмотрим следующие три структуры:
struct s1 { struct s1 *next; int dat[1000]; int x,y; };
struct s2 { struct s1 *next; int x,y; int dat[1000]; };
struct s3 { struct s1 *next; int x,y; int *dat; };
, к которым обращаются следующие l oop:
while(p->x)
p = p->next;
производительность второго, вероятно, будет намного лучше, чем первый из них, поскольку первый имел бы два промаха в кэше для большинства итераций l oop, в то время как второй - только один. Если небольшой размер позволил бы размещать структуры близко друг к другу, производительность третьего может быть даже выше, чем второго при обработке вышеупомянутого l oop (возможно, в среднем на одну итерацию приходится не более одного пропуска кэша), но гораздо хуже второго при доступе к первым нескольким элементам dat
(поскольку перенос структуры в кеш также привел бы к первым нескольким элементам dat
при использовании второй формы, но не при использовании третьей).
Обратите внимание, что тесты производительности могут быть обманчивы, если они не выполнены в реальных условиях. Маловероятно, что struct s2
будет работать хуже, чем s1
в большинстве реальных условий, но на относительную производительность между s2
и s3
могут оказать значительное влияние незначительные различия в действиях внешнего кода.