Cómo acelerar el código matemático C#(específicamente para Windows Phone 7 y Xbox 360)
performance windows-phone-7 (5)
1) ¿Realmente necesitas dobles? Especialmente estás mezclando algunos flotadores, dobles y enteros.
2) Debe precalcular el patrón 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;
Tengo un código de interpolación en 3D que ocupa el 90% del tiempo de ejecución de mis proyectos y no se puede precalcular.
¿Cuáles son algunas técnicas que podría usar para acelerar esto? ¿Algorítmica o Micro Optimización?
Aquí está el código para los interesados.
Básicamente, toma los datos que se colocaron en las 2 matrices en 3D e interpola el resto de los datos.
EDITAR: También estoy dividiendo esto en hilos en un nivel más alto para un mayor rendimiento, pero esto no ayuda en el teléfono de Windows, ya que son todos de un solo núcleo ...
Probablemente haga algo como (Single [] DensityMap = new Single [128 * 128 * 128];) para eliminar el golpe de matriz multi-D. Accedo a la matriz en cientos de lugares y esperaba no tener que hacer eso (envolver una función no ayuda, ya que el teléfono de Windows no alineará la llamada a la función y no ayuda a perf entonces ...)
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);
}
}
}
}
}
Hay algunas cosas que puede hacer para acelerar su código:
- Evite usar matrices multidim porque son lentas
- Usa múltiples hilos
- Almacenar variables que se lanzarán como un doble en una variable doble
- Precalcula todo lo que puedas (mira la publicación de Hatchet)
Arrays
Para simular la matriz 3D, solo puede hacerlo de esta manera:
Single[] DensityMap = new Single[128 * 128 * 128];
DensityMap[z + (y * 128) + (x * 128 * 128)] = ...;
Parece que tienes muchas oportunidades para optimizar tu código. Su bucle x se ejecuta 128 veces, su bucle y ejecuta 128 * 128 = 16,384 veces, y su bucle z ejecuta 128 ^ 3 = 2,097,152 veces. Hay una serie de términos dentro de su bucle z que solo dependen de las iteraciones x, o y, pero se vuelven a calcular en cada z iteración. Por ejemplo,
int poxox = plusOffsetX - offsetX;
y
double poxxpoxox = ((plusOffsetX - x) / (double)poxox);
Estos dos términos se calculan más de 2 millones de veces, pero solo deben calcularse 128 veces si mi análisis superficial de su función es correcto. Mueva los términos al nivel de bucle que sea apropiado para que no pierda ciclos recalculando los mismos valores muchas veces.
Aquí está su código con las optimizaciones básicas realizadas. Tengo curiosidad por saber cómo esto afecta tus tiempos de ejecución. Varios de los términos solo dependen del valor de iteración, y son los mismos para x, y y z. Así que los saqué por completo y los precalculé una vez. También moví las operaciones externas de mod del bucle interno y modifiqué la lógica para asegurar el cortocircuito de la evaluación, lo que debería eliminar la mayoría de las operaciones de modificación que se estaban ejecutando previamente.
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);
}
}
}
}
Puedes cambiar tus bucles for ya que no estás haciendo nada por los valores intermedios de todos estos
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) {
Hacer esto en paralelo sería aún mejor.
Con esto puedes eliminar los 6 millones de cálculos de mod% y más de 60 mil multiplicaciones.
--edit-- Lo siento, me perdí el "!" en tu línea con los 3 mods. Todavía puede omitir algunos de esos cálculos. Ver comentarios a continuación.
Use una matriz dentada en lugar de multidimensional, es decir, haga
float[][][] DensityMap = new float[128][][];
Y luego cree las matrices internas usando los bucles for, o la sintaxis LINQ (que podría ser subóptima).
Esto proporcionará MUCHO mejor rendimiento que el uso de una matriz multidimensional, y un rendimiento igual o mejor que el uso de una matriz de una sola dimensión y el cálculo de los desplazamientos de ti mismo. Es decir, a menos que el costo de inicializar la matriz dentada sea significativo; después de todo, creará 128 ^ 2 matrices. Lo compararía y solo volvería a una matriz de una dimensión si el costo realmente es significativo.