Подтвердить что ты не робот

Как ускорить математический код С# (в частности, для Windows Phone 7 и Xbox 360)

У меня есть некоторый 3d-интерполяционный код, который занимает 90% времени выполнения моих проектов и не может быть предварительно вычислен.

Какие методы я могу использовать для ускорения этого? Алгоритмическая или микро-оптимизация?

Вот код для заинтересованных.

В основном он берет данные, которые были помещены в 2 3D-массивы и интерполирует остальные данные.

EDIT: Также я уже разделяю это на потоки на более высоком уровне для повышения производительности, но это не помогает на телефоне Windows, поскольку все они одноядерные...

Я, вероятно, сделаю что-то вроде: (Single [] DensityMap = new Single [128 * 128 * 128];), чтобы удалить массив с множеством D-массивов. Я получаю доступ к массиву в 100 местах и ​​надеялся не делать этого (обертка в функции не помогает, поскольку телефон Windows не будет встроить вызов функции, и это не помогает перфомансу...)

float[, ,] DensityMap = new float[128, 128, 128];
float[, ,] PressureMap = new float[128, 128, 128];

unchecked
{
    for (int x = 0; x < g_CraftWorldConstants.RegionSizeX; x++)
    {
        int offsetX = (x / SAMPLE_RATE_3D_HOR) * SAMPLE_RATE_3D_HOR;
        int plusOffsetX = SAMPLE_RATE_3D_HOR + offsetX;
        int poxox = plusOffsetX - offsetX;
        double poxxpoxox = ((plusOffsetX - x) / (double)poxox);
        double xoxpoxox = ((x - offsetX) / (double)poxox);

        for (int y = 0; y < g_CraftWorldSettings.GET.RegionSizeY; y++)
        {
            int offsetY = (y / SAMPLE_RATE_3D_VERT) * SAMPLE_RATE_3D_VERT;
            int plusOffsetY = SAMPLE_RATE_3D_VERT + offsetY;
            int poyoy = plusOffsetY - offsetY;
            double poyypoyoy = ((plusOffsetY - y) / (double)poyoy);
            double yoypoyoy = ((y - offsetY) / (double)poyoy);

            for (int z = 0; z < g_CraftWorldConstants.RegionSizeZ; z++)
            {
                if (!(x % SAMPLE_RATE_3D_HOR == 0 && y % SAMPLE_RATE_3D_VERT == 0 && z % SAMPLE_RATE_3D_HOR == 0))
                {
                    int offsetZ = (z / SAMPLE_RATE_3D_HOR) * SAMPLE_RATE_3D_HOR;
                    int plusOffsetZ = SAMPLE_RATE_3D_HOR + offsetZ;
                    int pozoz = plusOffsetZ - offsetZ;
                    double pozzpozoz = ((plusOffsetZ - z) / (double)pozoz);
                    double zozpozoz = ((z - offsetZ) / (double)pozoz);

                    double x00 = poxxpoxox * in_DensityMap[offsetX, offsetY, offsetZ] + xoxpoxox * in_DensityMap[plusOffsetX, offsetY, offsetZ];
                    double x10 = poxxpoxox * in_DensityMap[offsetX, offsetY, plusOffsetZ] + xoxpoxox * in_DensityMap[plusOffsetX, offsetY, plusOffsetZ];
                    double x01 = poxxpoxox * in_DensityMap[offsetX, plusOffsetY, offsetZ] + xoxpoxox * in_DensityMap[plusOffsetX, plusOffsetY, offsetZ];
                    double x11 = poxxpoxox * in_DensityMap[offsetX, plusOffsetY, plusOffsetZ] + xoxpoxox * in_DensityMap[plusOffsetX, plusOffsetY, plusOffsetZ];

                    double r0 = poyypoyoy * x00 + yoypoyoy * x01;
                    double r1 = poyypoyoy * x10 + yoypoyoy * x11;
                    in_DensityMap[x, y, z] = (float)(pozzpozoz * r0 + zozpozoz * r1);

                    double x02 = poxxpoxox * in_CaveDensity[offsetX, offsetY, offsetZ] + xoxpoxox * in_CaveDensity[plusOffsetX, offsetY, offsetZ];
                    double x12 = poxxpoxox * in_CaveDensity[offsetX, offsetY, plusOffsetZ] + xoxpoxox * in_CaveDensity[plusOffsetX, offsetY, plusOffsetZ];
                    double x03 = poxxpoxox * in_CaveDensity[offsetX, plusOffsetY, offsetZ] + xoxpoxox * in_CaveDensity[plusOffsetX, plusOffsetY, offsetZ];
                    double x13 = poxxpoxox * in_CaveDensity[offsetX, plusOffsetY, plusOffsetZ] + xoxpoxox * in_CaveDensity[plusOffsetX, plusOffsetY, plusOffsetZ];

                    double r2 = poyypoyoy * x02 + yoypoyoy * x03;
                    double r3 = poyypoyoy * x12 + yoypoyoy * x13;
                    in_CaveDensity[x, y, z] = (float)(pozzpozoz * r2 + zozpozoz * r3);
                }
            }
        }
    }
}
4b9b3361

Ответ 1

Кажется, у вас есть много возможностей для оптимизации вашего кода. Ваш цикл x выполняется 128 раз, ваш цикл y выполняет 128 * 128 = 16,384 раза, а ваш цикл z выполняет 128 ^ 3 = 2,097,152 раза. Внутри вашего z-цикла есть ряд терминов, которые зависят только от x или y-итераций, но они пересчитываются на каждой итерации z. Например,

int poxox = plusOffsetX - offsetX;

и

double poxxpoxox = ((plusOffsetX - x) / (double)poxox);

Эти два термина рассчитываются более чем в 2 миллиона раз, но их нужно вычислять только 128 раз, если мое поверхностное сканирование вашей функции верное. Переместите термины на уровень цикла, который подходит, чтобы вы не тратили циклы, пересчитывая одни и те же значения много раз.

Вот ваш код с базовыми оптимизациями. Мне любопытно узнать, как это влияет на время работы. Некоторые термины зависят только от значения итерации и одинаковы для x, y и z. Поэтому я вытащил их полностью и прекоммутировал их один раз. Я также перенес внешние операции из внутреннего цикла и модифицировал логику, чтобы обеспечить короткое замыкание оценки, которая должна удалить большинство выполняемых ранее операций mod.

int[] offsets = new int[128];
int[] plusOffsets = new int[128];
double[] poii = new double[128];
double[] ioip = new double[128];
for (int i = 0; i < 128; i++) {
    offsets[i] = (i / SAMPLE_RATE_3D_HOR) * SAMPLE_RATE_3D_HOR;
    plusOffsets[i] = SAMPLE_RATE_3D_HOR + offsets[i];
    double poioi = (double) (plusOffsets[i] - offsets[i]);
    poii[i] = ((plusOffsets[i] - i) / poioi);
    ioip[i] = ((i - offsets[i]) / poioi);
}

float[, ,] DensityMap = new float[128, 128, 128];
float[, ,] PressureMap = new float[128, 128, 128];

for (int x = 0; x < g_CraftWorldConstants.RegionSizeX; x++)
{
    int offsetX = offsets[x];
    int plusOffsetX = plusOffsets[x];
    double poxxpoxox = poii[x];
    double xoxpoxox = ioip[x];
    bool xModNot0 = !(x % SAMPLE_RATE_3D_HOR == 0);

    for (int y = 0; y < g_CraftWorldConstants.RegionSizeY; y++)
    {
        int offsetY = offsets[y];
        int plusOffsetY = plusOffsets[y];
        double poyypoyoy = poii[y];
        double yoypoyoy = ioip[y];
        bool yModNot0 = !(y % SAMPLE_RATE_3D_VERT == 0);

        for (int z = 0; z < g_CraftWorldConstants.RegionSizeZ; z++)
        {
            //if (!(x % SAMPLE_RATE_3D_HOR == 0 && y % SAMPLE_RATE_3D_VERT == 0 && z % SAMPLE_RATE_3D_HOR == 0))
            if (xModNot0 || yModNot0 || !(z % SAMPLE_RATE_3D_HOR == 0))
            {
                int offsetZ = offsets[z];
                int plusOffsetZ = plusOffsets[z];
                double pozzpozoz = poii[z];
                double zozpozoz = ioip[z];

                double x00 = poxxpoxox * DensityMap[offsetX, offsetY, offsetZ] + xoxpoxox * DensityMap[plusOffsetX, offsetY, offsetZ];
                double x10 = poxxpoxox * DensityMap[offsetX, offsetY, plusOffsetZ] + xoxpoxox * DensityMap[plusOffsetX, offsetY, plusOffsetZ];
                double x01 = poxxpoxox * DensityMap[offsetX, plusOffsetY, offsetZ] + xoxpoxox * DensityMap[plusOffsetX, plusOffsetY, offsetZ];
                double x11 = poxxpoxox * DensityMap[offsetX, plusOffsetY, plusOffsetZ] + xoxpoxox * DensityMap[plusOffsetX, plusOffsetY, plusOffsetZ];

                double r0 = poyypoyoy * x00 + yoypoyoy * x01;
                double r1 = poyypoyoy * x10 + yoypoyoy * x11;
                DensityMap[x, y, z] = (float)(pozzpozoz * r0 + zozpozoz * r1);

                double x02 = poxxpoxox * PressureMap[offsetX, offsetY, offsetZ] + xoxpoxox * PressureMap[plusOffsetX, offsetY, offsetZ];
                double x12 = poxxpoxox * PressureMap[offsetX, offsetY, plusOffsetZ] + xoxpoxox * PressureMap[plusOffsetX, offsetY, plusOffsetZ];
                double x03 = poxxpoxox * PressureMap[offsetX, plusOffsetY, offsetZ] + xoxpoxox * PressureMap[plusOffsetX, plusOffsetY, offsetZ];
                double x13 = poxxpoxox * PressureMap[offsetX, plusOffsetY, plusOffsetZ] + xoxpoxox * PressureMap[plusOffsetX, plusOffsetY, plusOffsetZ];

                double r2 = poyypoyoy * x02 + yoypoyoy * x03;
                double r3 = poyypoyoy * x12 + yoypoyoy * x13;
                PressureMap[x, y, z] = (float)(pozzpozoz * r2 + zozpozoz * r3);
            }
        }
    } 
}

Ответ 2

Есть несколько вещей, которые вы можете сделать для ускорения вашего кода:

  • Избегайте использования multidim.-массивов, потому что они медленны
  • Использовать несколько потоков
  • Сохранить переменные, которые будут отображаться как двойные в двойной переменной
  • Предварительно расчитайте все, что сможете (см. сообщение о шляпе)

Массивы

Чтобы имитировать 3D-массив, вы можете сделать это следующим образом:

Single[] DensityMap = new Single[128 * 128 * 128];
DensityMap[z + (y * 128) + (x * 128 * 128)] = ...;

Ответ 3

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

for (int x = 0; x < 128; x+= SAMPLE_RATE_3D_HOR) {
   for (int y = 0; y < 128; y+= SAMPLE_RATE_3D_VERT) {
      for (int z = 0; z < 128; z+= SAMPLE_RATE_3D_HOR) {

Выполнение этих параллелей было бы еще лучше.

С помощью этого вы можете устранить 6 миллионов вычислений mod% и умножить на 60+ тысяч.

- edit-- Извините, я пропустил "!". на вашей линии с 3 модами. Вы можете пропустить некоторые из этих вычислений. См. Комментарии ниже.

Ответ 4

Использовать зубчатый массив, а не многомерный, т.е. делать

float[][][] DensityMap = new float[128][][];

И затем создайте внутренние массивы, используя для циклов, или синтаксис LINQ (что может оказаться неоптимальным).

Это даст БОЛЬШЕ лучшую производительность, чем использование многомерного массива, и равную или лучшую производительность, чем использование одномерного массива и вычисление смещений самостоятельно. То есть, если стоимость инициализации зубчатого массива значительна; он все равно создаст 128 ^ 2 массивов. Я бы оценил его и возвращался только к одномерному массиву, если стоимость действительно значительна.

Ответ 5

1) Вам действительно нужны двойники? Особенно вы смешиваете несколько поплавков, парных и ints.

2) Вы должны предварительно рассчитать шаблон k/SAMPLE_RATE_3D_HOR * SAMPLE_RATE_3D_HOR.

int pre_calc[128];
for( int i = 0; i < 128; ++i )
    pre_calc[i] = (i / SAMPLE_RATE_3D_HOR) * SAMPLE_RATE_3D_HOR;