如何简化 CPU Intensive For Loop C#
How To Simplify CPU Intensive For Loop C#
因此,我在 C# 中创建了一个程序生成的世界,Visual Studio on Windows 10 在 Unity 中。在我偶然发现我的代码中的这组循环之前,我无法弄清楚为什么我的地图无法处理大尺寸。
for (int index = 0; index < terrainCoords.Count; index++)
{
if (index == terrainCoords.Count)
{
break;
}
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}
if (touchCount < 2)
{
terrainCoords.Remove(terrainCoords[index]);
}
}
for (int j = 0; j < caveSmoothness; j++)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (!terrainCoords.Contains(new Vector3Int(x, y, 0)))
{
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(x + neighbors[posAdd].Item1, y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : -1;
}
if (touchCount > 1)
{
terrainCoords.Add(new Vector3Int(x, y, 0));
}
}
}
}
}
现在,作为参考,terrainCoords
是我预先生成的用于制作地图的 Vector3Int 列表,尽管生成的地图很乱,所以这些 for 循环通过遍历列表来清理它们称为 'neighbors' 的元组,这是一组数字,当添加到块的坐标时,会给出直接接触它们的块的所有坐标。第一个循环的目的是移除自由漂浮的固体块,这些固体块没有接触足够多的固体块来构成任何重要的结构。第二个循环是通过在未接触足够空块的空块位置添加块来填充可能出现的随机口袋。当 caveSmoothness
增加时,它还可以填充小而窄的尾部隧道。然而,如果我有一个地图,比方说,一个 100*100 大小和一个 'caveSmoothness' 为 2 的地图,它必须迭代自身 100,000,000 次,这太多了。这是一个关键的过程,但如果没有它会导致尴尬的地图。为了预先生成地形,我使用了 Accidental Noise 的 minecraft 世界示例和这些设置:
terraintree=
{
{name="ground_gradient", type="gradient", x1=0, x2=0, y1=0, y2=1},
{name="lowland_shape_fractal", type="fractal", fractaltype=anl.BILLOW, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=2, frequency=0.25},
{name="lowland_autocorrect", type="autocorrect", source="lowland_shape_fractal", low=0, high=1},
{name="lowland_scale", type="scaleoffset", source="lowland_autocorrect", scale=0.125, offset=-0.45},
{name="lowland_y_scale", type="scaledomain", source="lowland_scale", scaley=0},
{name="lowland_terrain", type="translatedomain", source="ground_gradient", ty="lowland_y_scale"},
{name="highland_shape_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
{name="highland_autocorrect", type="autocorrect", source="highland_shape_fractal", low=-1, high=1},
{name="highland_scale", type="scaleoffset", source="highland_autocorrect", scale=0.25, offset=0},
{name="highland_y_scale", type="scaledomain", source="highland_scale", scaley=0},
{name="highland_terrain", type="translatedomain", source="ground_gradient", ty="highland_y_scale"},
{name="mountain_shape_fractal", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
{name="mountain_autocorrect", type="autocorrect", source="mountain_shape_fractal", low=-1, high=1},
{name="mountain_scale", type="scaleoffset", source="mountain_autocorrect", scale=0.45, offset=0.15},
{name="mountain_y_scale", type="scaledomain", source="mountain_scale", scaley=0.25},
{name="mountain_terrain", type="translatedomain", source="ground_gradient", ty="mountain_y_scale"},
{name="terrain_type_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
{name="terrain_autocorrect", type="autocorrect", source="terrain_type_fractal", low=0, high=1},
{name="terrain_type_y_scale", type="scaledomain", source="terrain_autocorrect", scaley=0},
{name="terrain_type_cache", type="cache", source="terrain_type_y_scale"},
{name="highland_mountain_select", type="select", low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
{name="cave_shape", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
{name="ground_cave_multiply", type="combiner", operation=anl.MULT, source_0="cave_select", source_1="ground_select"}
}
尽管我努力在他们的文档中找到噪声 reduction/fractal 平滑度设置并调整设置,但我似乎无法生成与我使用 for 循环的 Ruth-Goldberg 机器相同的结果是。如果您可以简化我的 for 循环,找到一种更有效的方法来实现这些结果,或者可以以我没有注意到的方式调整分形设置,将非常感谢您的意见。谢谢你! =)
一个突出的优化是缩短循环以删除 terrainCoords
元素。您不关心真实计数,只关心它小于 2。所以当 touchCount
测试失败时,退出循环的其余部分。
for (int posAdd = 0; posAdd < neighbors.Count && touchCount < 2; posAdd++)
{
newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}
if (touchCount < 2)
{
terrainCoords.Remove(terrainCoords[index]);
}
您可以对最内层的回路应用类似的短路以实现洞穴平滑。
我觉得太复杂了。它是一个二维数组,那么为什么不从简单的二维数组开始呢?
Random rnd = new Random(1);
int xSize = 10000;
int ySize = 1000;
byte[,] terrainCoords = new byte[ySize + 2, xSize + 2];
for (int y = 1; y <= ySize; ++y)
{
for (int x = 1; x <= xSize; ++x)
{
if (rnd.Next(100) < 40) //40% fill ratio
{
terrainCoords[y, x] = 1;
}
}
}
var st = new System.Diagnostics.Stopwatch();
st.Start();
for (int y = 1; y <= ySize; ++y)
{
for (int x = 1; x <= xSize; ++x)
{
int touchCount =
terrainCoords[y - 1, x - 1]
+ terrainCoords[y - 1, x]
+ terrainCoords[y - 1, x + 1]
+ terrainCoords[y, x - 1]
+ terrainCoords[y, x + 1]
+ terrainCoords[y + 1, x - 1]
+ terrainCoords[y + 1, x]
+ terrainCoords[y + 1, x + 1];
if (touchCount < 2)
{
terrainCoords[y, x] = 0;
}
}
}
int caveSmoothness = 5;
for (int j = 0; j < caveSmoothness; j++)
{
for (int y = 1; y <= ySize; ++y)
{
for (int x = 1; x <= xSize; ++x)
{
int touchCount =
terrainCoords[y - 1, x - 1]
+ terrainCoords[y - 1, x]
+ terrainCoords[y - 1, x + 1]
+ terrainCoords[y, x - 1]
+ terrainCoords[y, x + 1]
+ terrainCoords[y + 1, x - 1]
+ terrainCoords[y + 1, x]
+ terrainCoords[y + 1, x + 1];
if (touchCount > 4)
{
terrainCoords[y, x] = 1;
}
}
}
}
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds.ToString()); //800ms
我认为这是一个很好的起点。如果你需要更多的性能,你可以进一步优化它,但它必须在真实数据上完成。
因此,我在 C# 中创建了一个程序生成的世界,Visual Studio on Windows 10 在 Unity 中。在我偶然发现我的代码中的这组循环之前,我无法弄清楚为什么我的地图无法处理大尺寸。
for (int index = 0; index < terrainCoords.Count; index++)
{
if (index == terrainCoords.Count)
{
break;
}
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}
if (touchCount < 2)
{
terrainCoords.Remove(terrainCoords[index]);
}
}
for (int j = 0; j < caveSmoothness; j++)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (!terrainCoords.Contains(new Vector3Int(x, y, 0)))
{
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(x + neighbors[posAdd].Item1, y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : -1;
}
if (touchCount > 1)
{
terrainCoords.Add(new Vector3Int(x, y, 0));
}
}
}
}
}
现在,作为参考,terrainCoords
是我预先生成的用于制作地图的 Vector3Int 列表,尽管生成的地图很乱,所以这些 for 循环通过遍历列表来清理它们称为 'neighbors' 的元组,这是一组数字,当添加到块的坐标时,会给出直接接触它们的块的所有坐标。第一个循环的目的是移除自由漂浮的固体块,这些固体块没有接触足够多的固体块来构成任何重要的结构。第二个循环是通过在未接触足够空块的空块位置添加块来填充可能出现的随机口袋。当 caveSmoothness
增加时,它还可以填充小而窄的尾部隧道。然而,如果我有一个地图,比方说,一个 100*100 大小和一个 'caveSmoothness' 为 2 的地图,它必须迭代自身 100,000,000 次,这太多了。这是一个关键的过程,但如果没有它会导致尴尬的地图。为了预先生成地形,我使用了 Accidental Noise 的 minecraft 世界示例和这些设置:
terraintree=
{
{name="ground_gradient", type="gradient", x1=0, x2=0, y1=0, y2=1},
{name="lowland_shape_fractal", type="fractal", fractaltype=anl.BILLOW, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=2, frequency=0.25},
{name="lowland_autocorrect", type="autocorrect", source="lowland_shape_fractal", low=0, high=1},
{name="lowland_scale", type="scaleoffset", source="lowland_autocorrect", scale=0.125, offset=-0.45},
{name="lowland_y_scale", type="scaledomain", source="lowland_scale", scaley=0},
{name="lowland_terrain", type="translatedomain", source="ground_gradient", ty="lowland_y_scale"},
{name="highland_shape_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
{name="highland_autocorrect", type="autocorrect", source="highland_shape_fractal", low=-1, high=1},
{name="highland_scale", type="scaleoffset", source="highland_autocorrect", scale=0.25, offset=0},
{name="highland_y_scale", type="scaledomain", source="highland_scale", scaley=0},
{name="highland_terrain", type="translatedomain", source="ground_gradient", ty="highland_y_scale"},
{name="mountain_shape_fractal", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
{name="mountain_autocorrect", type="autocorrect", source="mountain_shape_fractal", low=-1, high=1},
{name="mountain_scale", type="scaleoffset", source="mountain_autocorrect", scale=0.45, offset=0.15},
{name="mountain_y_scale", type="scaledomain", source="mountain_scale", scaley=0.25},
{name="mountain_terrain", type="translatedomain", source="ground_gradient", ty="mountain_y_scale"},
{name="terrain_type_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
{name="terrain_autocorrect", type="autocorrect", source="terrain_type_fractal", low=0, high=1},
{name="terrain_type_y_scale", type="scaledomain", source="terrain_autocorrect", scaley=0},
{name="terrain_type_cache", type="cache", source="terrain_type_y_scale"},
{name="highland_mountain_select", type="select", low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
{name="cave_shape", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
{name="ground_cave_multiply", type="combiner", operation=anl.MULT, source_0="cave_select", source_1="ground_select"}
}
尽管我努力在他们的文档中找到噪声 reduction/fractal 平滑度设置并调整设置,但我似乎无法生成与我使用 for 循环的 Ruth-Goldberg 机器相同的结果是。如果您可以简化我的 for 循环,找到一种更有效的方法来实现这些结果,或者可以以我没有注意到的方式调整分形设置,将非常感谢您的意见。谢谢你! =)
一个突出的优化是缩短循环以删除 terrainCoords
元素。您不关心真实计数,只关心它小于 2。所以当 touchCount
测试失败时,退出循环的其余部分。
for (int posAdd = 0; posAdd < neighbors.Count && touchCount < 2; posAdd++)
{
newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}
if (touchCount < 2)
{
terrainCoords.Remove(terrainCoords[index]);
}
您可以对最内层的回路应用类似的短路以实现洞穴平滑。
我觉得太复杂了。它是一个二维数组,那么为什么不从简单的二维数组开始呢?
Random rnd = new Random(1);
int xSize = 10000;
int ySize = 1000;
byte[,] terrainCoords = new byte[ySize + 2, xSize + 2];
for (int y = 1; y <= ySize; ++y)
{
for (int x = 1; x <= xSize; ++x)
{
if (rnd.Next(100) < 40) //40% fill ratio
{
terrainCoords[y, x] = 1;
}
}
}
var st = new System.Diagnostics.Stopwatch();
st.Start();
for (int y = 1; y <= ySize; ++y)
{
for (int x = 1; x <= xSize; ++x)
{
int touchCount =
terrainCoords[y - 1, x - 1]
+ terrainCoords[y - 1, x]
+ terrainCoords[y - 1, x + 1]
+ terrainCoords[y, x - 1]
+ terrainCoords[y, x + 1]
+ terrainCoords[y + 1, x - 1]
+ terrainCoords[y + 1, x]
+ terrainCoords[y + 1, x + 1];
if (touchCount < 2)
{
terrainCoords[y, x] = 0;
}
}
}
int caveSmoothness = 5;
for (int j = 0; j < caveSmoothness; j++)
{
for (int y = 1; y <= ySize; ++y)
{
for (int x = 1; x <= xSize; ++x)
{
int touchCount =
terrainCoords[y - 1, x - 1]
+ terrainCoords[y - 1, x]
+ terrainCoords[y - 1, x + 1]
+ terrainCoords[y, x - 1]
+ terrainCoords[y, x + 1]
+ terrainCoords[y + 1, x - 1]
+ terrainCoords[y + 1, x]
+ terrainCoords[y + 1, x + 1];
if (touchCount > 4)
{
terrainCoords[y, x] = 1;
}
}
}
}
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds.ToString()); //800ms
我认为这是一个很好的起点。如果你需要更多的性能,你可以进一步优化它,但它必须在真实数据上完成。