如何加速 C# 数学代码
本文关键字:代码 加速 何加速 | 更新日期: 2023-09-27 18:35:16
我有一些 3D 插值代码,它占用了我项目运行时的 90%,无法预先计算。
我可以使用哪些技术来加快速度?算法优化还是微优化?
这是感兴趣的人的代码。
它基本上获取放置在 2 个 3d 数组中的数据,并对其余数据进行插值。
编辑:此外,我已经将其拆分为更高级别的线程以提高性能,但这在Windows手机上没有帮助,因为它们都是单核...
我可能会做类似的事情(Single[] DensityMap = new Single[128 * 128 * 128];)以删除多 D 数组命中。我在 100 多个地方访问数组,并希望不必这样做(包装函数无济于事,因为 Windows Phone 不会内联函数调用,并且它对性能没有帮助......
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);
}
}
}
}
}
似乎你有很多机会来优化你的代码。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);
这两个项的计算次数超过200万次,但是如果我对您的函数的粗略扫描是正确的,则只需要计算128次。将项移动到适当的循环级别,这样您就不会浪费周期多次重新计算相同的值。
下面是进行了基本优化的代码。我很想知道这会如何影响您的运行时间。其中几个项仅依赖于迭代值,并且对于 x、y 和 z 是相同的。所以我把它们完全拉出来,预先计算了一次。我还将外部 mod 操作移出了内部循环,并修改了逻辑以确保评估短路,这应该会删除之前执行的大部分 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);
}
}
}
}
您可以采取一些措施来加快代码速度:
- 避免使用多dim.-array,因为它们很慢
- 使用多个线程
- 将要转换为双精度的变量存储在双精度变量中
- 预先计算你能做的一切(见斧头的帖子)
阵 列
要模拟 3D 阵列,您可以通过以下方式进行操作:
Single[] DensityMap = new Single[128 * 128 * 128];
DensityMap[z + (y * 128) + (x * 128 * 128)] = ...;
使用交错数组而不是多维数组,即 do
float[][][] DensityMap = new float[128][][];
然后使用 for 循环或 LINQ 语法(可能是次优的)创建内部数组。
这将提供比使用多维数组更好的性能,并且比使用一维数组并自己计算偏移量具有相同或更好的性能。也就是说,除非初始化交错数组的成本很高;毕竟它会创建 128^2 个数组。我会对其进行基准测试,只有在成本确实很高的情况下才会恢复为一维数组。
您可以更改 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) {
并行执行这些操作会更好。
有了这个,您可以消除 600 万次 mod % 计算和 60+ 千次乘法。
--编辑--对不起,我错过了你与 3 个模组行上的"!"。 您仍然可以跳过其中一些计算。 请参阅下面的评论。
1)你真的需要双打吗?特别是你正在混合一些浮点数、双精度和整数。
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;