Сначала просто: переместите все «старые» переменные в другой массив. Вы никогда не получаете к ним доступ в своем основном цикле, поэтому вы затрагиваете вдвое больше памяти, чем вам действительно нужно (и, таким образом, получаете вдвое больше пропусков кэша). Вот недавнее сообщение в блоге на эту тему: http://msinilo.pl/blog/?p=614. И, конечно, вы можете предварительно выбрать на несколько частиц вперед, например. p [j + k], где k - некоторая постоянная, которая потребует некоторых экспериментов.
Если вы тоже переместите массу, вы можете хранить такие вещи:
struct ParticleData
{
Vector pos, vel, acc, jerk;
};
ParticleData* currentParticles = ...
ParticleData* oldParticles = ...
real* masses = ...
затем обновление старых данных частиц из новых данных становится единой большой памятью от текущих частиц к старым частицам.
Если вы хотите сделать код немного уродливым, возможно, вы сможете улучшить оптимизацию SIMD, храня вещи в «транспонированном» формате, например,
struct ParticleData
{
// data_x[0] == pos.x, data_x[1] = vel.x, data_x[2] = acc.x, data_x[3] = jerk.x
Vector4 data_x;
// data_y[0] == pos.y, data_y[1] = vel.y, etc.
Vector4 data_y;
// data_z[0] == pos.z, data_y[1] = vel.z, etc.
Vector4 data_z;
};
где Vector4 - это один SIMD-вектор с одинарной точностью или два с двойной точностью. Этот формат распространен в трассировке лучей для тестирования нескольких лучей одновременно; это позволяет вам выполнять операции, такие как точечные продукты, более эффективно (без перемешивания), а также означает, что загрузка вашей памяти может быть выровнена на 16 байт. Хотя определенно требуется несколько минут, чтобы обернуть голову:)
Надеюсь, это поможет, дайте мне знать, если вам нужна ссылка на использование транспонированного представления (хотя я не уверен, какая помощь здесь будет на самом деле).