C# resource contentions - c#

I am creating a flowfield for AI units and am trying to speed up some threaded code. My test grid size is 2000 x 2000 and I currently have the generation time down to 5.5 seconds. Profiling the code pointed something out that seems strange. The method that I use for the threads reports on average 140 Inclusive Contentions. 65 of which are for this line.
var neighbours = GetNeighbors4(cell.Point);
and below is the method being called.
private IEnumerable<IntegrationCell> GetNeighbors4(Point point)
{
var sizeX = _size.X - 1;
var sizeY = _size.Y - 1;
var x = point.X;
var y = point.Y;
//corners
if (x == 0 && y == 0)
{
return new[]
{
Cells[1, 0],
Cells[0, 1]
};
}
if (x == sizeX && y == 0)
{
return new[]
{
Cells[sizeX - 1, 0],
Cells[sizeX, 1]
};
}
if (x == 0 && y == sizeY)
{
return new[]
{
Cells[0, sizeY - 1],
Cells[1, sizeY]
};
}
if (x == sizeX && y == sizeY)
{
return new[]
{
Cells[sizeX - 1, sizeY],
Cells[sizeX, sizeY - 1]
};
}
//top row
if (y == 0)
{
return new[]
{
Cells[x - 1, 0],
Cells[x + 1, 0],
Cells[x, 1]
};
}
//bottom row
if (y == sizeY)
{
return new[]
{
Cells[x - 1, y],
Cells[x + 1, y],
Cells[x, y - 1]
};
}
//left column
if (x == 0)
{
return new[]
{
Cells[0, y - 1],
Cells[0, y + 1],
Cells[1, y]
};
}
//right column
if (x == sizeX)
{
return new[]
{
Cells[x, y - 1],
Cells[x, y + 1],
Cells[x - 1, y]
};
}
//everything else
return new[]
{
Cells[x, y - 1],
Cells[x, y + 1],
Cells[x - 1, y],
Cells[x + 1, y]
};
}
Cells is just a simple 2 dimensional array to represent a grid
IntegrationCell[,] Cells;
Now the way it works is that given a target cell in the grid, I step out like a 'wave' or 'ripple' from the target. Each iteration of the wave steps out one further from the target. As I do this, each iteration has more cells as the distance from the target increases. For each cell in each iteration, I spawn a new thread that computes the cells cost and returns a list of new cells that need to be computed/recomputed. There is a lot more that happens, but that's basically it. At one point I peak at roughly 120 threads before I hit the edge of the map, and I begin to have less cells each iteration until there are none left.
This is the full method of the thread run for each cell. (I can have over 100 running at any one time)
private IEnumerable<IntegrationCell> CostStep(IntegrationCell cell)
{
var result = new List<IntegrationCell>(); \\14 contentions
var costBlock = _costfield.Cells[cell.Point.X, cell.Point.Y];
if (costBlock.Cost == 255)
return result;
var neighbours = GetNeighbors4(cell.Point); \\65 contentions
foreach (var neighbour in neighbours) \\18 contentions
{
var newCost = costBlock.Cost + neighbour.Cost;
if (cell.Cost > newCost)
cell.Cost = newCost;
var childCostBlock = _costfield.Cells[neighbour.Point.X, neighbour.Point.Y];
var newChildCost = cell.Cost + childCostBlock.Cost;
if (childCostBlock.Cost == 255)
neighbour.Cost = 255;
else if (neighbour.Cost > newChildCost)
{
neighbour.Cost = newChildCost;
result.Add(neighbour); \\39 contentions
}
}
return result;
}
I have placed comments of the contentions reported against each line. The contentions vary with each run, but what I cant understand is why I would have contentions reading from an array? Yes I'm updating the the array/cell and its neighbors if needed and each cell may be calculated more than once.

For each cell in each iteration, I spawn a new thread that ...
Probably that is the problem. Since you are doing CPU bound calculations, use only as many threads as CPU has your computer, indicated by the static property:
System.Environment.ProcessorCount
Otherwise, you are trying to schedule too much, causing a lot of contention and context switches.
I mean, more threads does not mean faster, actually, it may do the application slower because the thread management overhead. You use more threads when you have I/O bound operations, and therefore many threads are idle waiting for things to happen somewhere else (e.g.: a web service call, a database operation, an incoming request....)
Take a look at this answer: https://stackoverflow.com/a/12021285/307976, and also about how to limit the parallelism with PLINQ.

Related

Flood fill algorithm stackoverflow

Im working on a painting game but i can't get the flood fill algorithm to work on large areas. Its a recursive algorithm and i read that implementing a stack might work however i can get it to work as well. Here's the code;
private void FillCluster(int x, int y, int colorIndex, HashSet<string> traversedCells)
{
Debug.Log(colorIndex);
// Check if this cell is within the bounds of the picture and has a color number and the
if(x < 0 || x >= ActivePictureInfo.XCells ||
y < 0 || y >= ActivePictureInfo.YCells ||
ActivePictureInfo.ColorNumbers[y][x] == -1 ||
ActivePictureInfo.ColorNumbers[y][x] != colorIndex)
{
return;
}
string cellKey = string.Format("{0}_{1}", x, y);
// Check if this cell has already been traversed by FillBlob
if (traversedCells.Contains(cellKey))
{
return;
}
// Check if this cell is already colored in with the correct color
if (!ActivePictureInfo.HasProgress || ActivePictureInfo.Progress[y][x] != -1)
{
ColorCell(x, y, colorIndex);
}
// Add this cells key to the traversed hashset to indicate it has been processed
traversedCells.Add(cellKey);
// Recursively call recursively with the four cells adjacent to this cell
FillCluster(x - 1, y, colorIndex, traversedCells);
FillCluster(x + 1, y, colorIndex, traversedCells);
FillCluster(x, y - 1, colorIndex, traversedCells);
FillCluster(x, y + 1, colorIndex, traversedCells);
FillCluster(x - 1, y - 1, colorIndex, traversedCells);
FillCluster(x - 1, y + 1, colorIndex, traversedCells);
FillCluster(x + 1, y - 1, colorIndex, traversedCells);
FillCluster(x + 1, y + 1, colorIndex, traversedCells);
}
a version without having to remember all visited cells because they are already marked by the color
private void FillCluster(int x, int y, int colorIndex)
{
Debug.Log(colorIndex);
var currentSeam = new Queue<PointDirection>();
if (FillPoint(x, y, colorIndex))
{
currentSeam.Enqueue(new PointDirection(x - 1, y, Direction.Left));
currentSeam.Enqueue(new PointDirection(x + 1, y, Direction.Right));
currentSeam.Enqueue(new PointDirection(x, y - 1, Direction.Up));
currentSeam.Enqueue(new PointDirection(x, y + 1, Direction.Down));
}
while (currentSeam.Count > 0)
{
var current = currentSeam.Dequeue();
if (FillPoint(current.X, current.Y, colorIndex))
{
if (current.Direction != Direction.Right)
currentSeam.Enqueue(new PointDirection(x - 1, y, Direction.Left));
if (current.Direction != Direction.Left)
currentSeam.Enqueue(new PointDirection(x + 1, y, Direction.Right));
if (current.Direction != Direction.Down)
currentSeam.Enqueue(new PointDirection(x, y - 1, Direction.Up));
if (current.Direction != Direction.Up)
currentSeam.Enqueue(new PointDirection(x, y + 1, Direction.Down));
}
}
}
private bool FillPoint(int x, int y, int colorIndex)
{
if (x < 0 || x >= ActivePictureInfo.XCells ||
y < 0 || y >= ActivePictureInfo.YCells ||
ActivePictureInfo.ColorNumbers[y][x] == -1 ||
ActivePictureInfo.ColorNumbers[y][x] == colorIndex)
{
return false;
}
ActivePictureInfo.ColorNumbers[y][x] = colorIndex;
return true;
}
private struct PointDirection
{
public PointDirection(int x, int y, Direction direction)
{
X = x;
Y = y;
Direction = direction;
}
public int X { get; }
public int Y { get; }
public Direction Direction { get; }
}
private enum Direction : byte
{
Up,
Right,
Down,
Left
}
So the reason you are getting a stackOverflow is because the state of the previous iteration is saved on the stack unless both of these condition are met:
You are doing tail recursion (look it up, but it isn't relevant right now)
Your language optimizes tail recursion (C# doesn't)
As the stack size is limited and your algorithm very quickly reaches thousands of calls one after the other, you cannot make the recursive version work in C#, that is impossible, but it seems like you understood this already
Here is a pseudocode solution to do it without recursion. I don't know C# so the synthax is not correct, but the idea is correct, you will have to transform it into proper C#
private void FillCluster(int x, int y, int colorIndex, HashSet<string> traversedCells) {
Debug.Log(colorIndex);
// Check if this cell is within the bounds of the picture and has a color number and the
//Declare a set, add inside the current node
Set<Tuple> nodesToFill = new Set<>()
nodesToFill.add(new Tuple(x, y));
//Replace the recursion by a loop
for (Tuple tuple in nodesToFill) {
//initialize the current position
x = tuple.first
y = tuple.second
//Deal with the current node
if(FillClusterInner(x, y, colorIndex, traversedCells)) {
//Add the new nodes to the set instead of using recursion
nodesToFill.add(new Tuple(x-1, y))
nodesToFill.add(new Tuple(x + 1, y))
nodesToFill.add(new Tuple(x, y - 1))
nodesToFill.add(new Tuple(x, y + 1))
nodesToFill.add(new Tuple(x - 1, y - 1))
nodesToFill.add(new Tuple(x - 1, y + 1))
nodesToFill.add(new Tuple(x + 1, y - 1))
nodesToFill.add(new Tuple(x + 1, y + 1))
}
//Remove the current tuple from the set as you have dealt with it
nodesToFill.remove(tuple)
}
}
//This is a non-recursive method which fills a single specified node
bool FillClusterInner(int x, int y, int colorIndex, HashSet<string> traversedCells) {
if(x < 0 || x >= ActivePictureInfo.XCells ||
y < 0 || y >= ActivePictureInfo.YCells ||
ActivePictureInfo.ColorNumbers[y][x] == -1 ||
ActivePictureInfo.ColorNumbers[y][x] != colorIndex)
{
return false;
}
string cellKey = string.Format("{0}_{1}", x, y);
// Check if this cell has already been traversed by FillBlob
if (traversedCells.Contains(cellKey))
{
return false;
}
// Check if this cell is already colored in with the correct color
if (!ActivePictureInfo.HasProgress || ActivePictureInfo.Progress[y][x] != -1)
{
ColorCell(x, y, colorIndex);
}
// Add this cells key to the traversed hashset to indicate it has been processed
traversedCells.Add(cellKey);
return true;
}
You can see the idea: instead of recursion, we use a set, which contains all the indexes of the nodes we have yet to fill. You add to this set instead of calling the recursive function, and you remove from the set each position you have handled. When the set is empty you have filled every cell that had to be filled.

Tilebased boardgame dicenumber restriction (Incremental algorithms)

Developing a 2D tile-based "boardgame" I'm struggling with the restriction I have to make, when a player rolls the dice(Move 5 tiles if you land a 5 etc.)
I'm trying to use the following logic:
Start on starting point
Check the position to the sides, above and below
Check if the neighbour tiles are walkable, if they are, change them to reachable
Go to neighbour tile, repeat
I've been looking on A* and D* pathing, but it's a new subject to me and they seem more focused on getting from point A to B, not "reach" which is what I need.
How do I do this through code?
I created a 2D array from an array which was holding my tile(I need a normal array of the tilemap for another purpose):
for(int i = 0; i < 27; i++)
{
for(int j = 0; j < 33; j++)
{
tileMap[i, j] = goTile[i * 33 + j];
}
}
I now use the tileMap as my positiong factor, e.g. my players current position is tileMap[2,4].
I then tried to develop a function:
void pathFinding(Vector2 playerPosition, int diceNumber)
{
GameObject currentPos = tileMap[(int)playerPosition.x, (int)playerPosition.y];
for (int i = 0; i < diceNumber; i++) {
if (tileMap[(int)playerPosition.x + 1, (int)playerPosition.y].tag == "walkableGrid")
{
tileMap[(int)playerPosition.x + 1, (int)playerPosition.y].gameObject.tag = "reachable";
playerPosition.x++;
}
if (tileMap[(int)playerPosition.x - 1, (int)playerPosition.y].tag == "walkableGrid")
{
playerPosition.x--;
}
if (tileMap[(int)playerPosition.x, (int)playerPosition.y + 1].tag == "walkableGrid")
{
playerPosition.y++;
}
if (tileMap[(int)playerPosition.x, (int)playerPosition.y - 1].tag == "walkableGrid")
{
playerPosition.y--;
}
}
}
But as finishing this (if it even would work), would require MANY lines of code, and I believe there's a swifter method using a nested for loop maybe?
//I have now edited the code to better reflect your real data
public void ShowMoves(Vector2 playerPosition, int diceNumber, bool[] blocks)
{
int x = (int)playerPosition.x;
int y = (int)playerPosition.y;
if(tileMap.GetUpperBound(0) < x + 1)
{
if(tileMap[x + 1, y].tag == "walkableGrid" && blocks[0])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x + 1, y), diceNumber - 1, new bool[] { x != tileMap.GetUpperBound(0), false, y != tileMap.GetUpperBound(1), y != 0 });
}
}
if(x - 1 >= 0)
{
if(tileMap[x - 1, y].tag == "walkableGrid" && blocks[1])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x - 1, y), diceNumber - 1, new bool[] { false, x != 0, y != tileMap.GetUpperBound(1), y != 0 });
}
}
if(tileMap.GetUpperBound(1) < y + 1)
{
if(tileMap[x, y + 1].tag == "walkableGrid" && blocks[2])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x, y + 1), diceNumber - 1, new bool[] { x != tileMap.GetUpperBound(0), x != 0, y != tileMap.GetUpperBound(1), false });
}
}
if(y - 1 >= 0)
{
if(tileMap[x, y - 1].tag == "walkableGrid" && blocks[3])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x, y - 1), diceNumber - 1, new bool[] { x != tileMap.GetUpperBound(0), x != 0, false, y != 0 });
}
}
}
This code might not compile, but it's an example to help you along - it will loop until there are no available moves, and exhaust every option. It will also not go back on itself due to the blocks boolean array. The input format would be the position they're at two ints, one for x and one for y, the tiles available, the number of moves left in their roll, and the block available from the beginning (always new bool[] {true, true, true, true}
Be careful, there may be errors in my code, I wrote it in SO and have no clue how well it runs, if it runs at all or anything. Even if it does not, it should be a good starting point for you to create your logic and code it all
EDIT: Code has been changed so that it better fits how your code looks and the data types it uses
To avoid always calling the method by inputting a blocks variable of new bool[] {true, true, true, true}; you can make it an optional operator by making this the method parameters
public void ShowMoves(Vector2 playerPosition, int diceNumber, bool[] blocks = new bool[] {true, true, true, true})

While loop wrong output given

I have a list of Points List<Point> newcoor = new List<Point>(); and specific coordinate which is the center of the List of Points. int centerx, centery;
What I want to do is add 1 with centerx and subtract 1 with centery until it reaches a combination that will match a Point inside a list. Then store that point inside an array. This is my code:
List<Point> newcoor = new List<Point>(); // list of points that where the tempx and tempy will be compared to.
//...
Point[] vector = new Point[4];
int x = 0;
while (x <= 3)
{
var tempx = centerx + 1; //add 1 to centerx
var tempy = centerx - 1; //subtrat 1 to centery
int y = 0;
if (y < newcoor.Count() - 1 && newcoor[y].X == tempx && newcoor[y].Y == tempy) // compare if there is a Point in the List that is equal with the (tempx,tempy) coordinate
{
vector[x].X = tempx;// store the coordinates
vector[x].Y = tempy;
}
break; // this is what I don't understand, I want to exit the loop immediately if the if-condition is true. And add 1 to x so the while loop will update.
}
Tried New Code:
for (int y = 0; y < newcoor.Count() - 1; y++)
{
var tempx = centerx + 1;
var tempy = centery - 1;
for (int x = 0; x < newcoor.Count() - 1; x++)
{
if (newcoor[y].X == tempx && newcoor[y].Y == tempy)
{
//vectorPoints.Add(new Point(tempx,tempy));
MessageBox.Show("success");
}
}
}
But no messagebox success shows, meaning there was no match. but there must be.
All I need is 4 output that's why I have conditon while (x <= 3)
Update:
My centerx = 30 and centery = 28
And here is my list:
What I want to do is add 1 to centerx and subtract 1 to centery
from original centerx= 30 and centery= 28, it should be
(31,27)
(32,26)
(33,25)
(34,24)
(35,23) <----- This should be the to the one with the same value inside my list, which is shown in the image above.
No idea what you're hoping for here, but there's several problems I can spot anyway;
Firtly, tempx and tempy will be the same value on each loop, as nothing inside the loop manipulates centerx or centery.
Secondly, the loop will exit on the first run, as the break statement is not inside the if {..} block. Perhaps you meant;
if (y < newcoor.Count() - 1 && newcoor[y].X == tempx && newcoor[y].Y == tempy)
{
vector[x].X = tempx;// store the coordinates
vector[x].Y = tempy;
break; // <-- needs to be inside the if{..} block
}

Find number of ways in a maze non recursively

Given a matrix[n,n] I want to find out how many ways we can reach from [0,0] to [n,n] non recursively.
My approach is to
Create a stuct Node to store row, col and path travelled so far
Add node to a Queue
Iterate thru queue till not empty . Increment row, increment col. Add to Queue
Print the path if row=n, col=n
Question
Is there a different way of storing row,col and path
If n is very large, storing nodes in Queue can be a problem. How can we avoid this?
Please not I am not looking for recursive solution.
I see such questions in many interview forums and so want to know if this would be the right approach.
Below is the structure of Node and the function
struct Node
{
public int row;
public int col;
public string path;
public Node(int r, int c, string p)
{
this.row = r;
this.col = c;
this.path = p;
}
}
public static void NextMoveNonRecursive(int max)
{
int rowPos;
int colPos;
string prevPath = "";
Node next;
while (qu.Count > 0)
{
Node current = qu.Dequeue();
rowPos = current.row;
colPos = current.col;
prevPath = current.path;
if (rowPos + 1 == max && colPos + 1 == max)
{
Console.WriteLine("Path = ..." + prevPath);
TotalPathCounter++;
}
if (rowPos + 1 < max)
{
if (prevPath == "")
prevPath = current.path;
prevPath = prevPath + ">" + (rowPos + 1) + "" + (colPos);
next = new Node(rowPos + 1, colPos, prevPath);
qu.Enqueue(next);
prevPath = "";
}
if (colPos + 1 < max)
{
if (prevPath == "")
prevPath = current.path;
prevPath = prevPath + ">" + (rowPos) + "" + (colPos+1);
next = new Node(rowPos, colPos+1, prevPath);
qu.Enqueue(next);
prevPath = "";
}
}
}
Let dp[i, j] be the number of paths from [0, 0] to [i, j].
We have:
dp[0, i] = dp[i, 0] = 1 for all i = 0 to n
dp[i, j] = dp[i - 1, j] + come down from all paths to [i - 1, j]
dp[i, j - 1] + come down from all paths to [i, j - 1]
dp[i - 1, j - 1] come down from all paths to [i - 1, j - 1]
for i, j > 0
Remove dp[i - 1, j - 1] from the above sum if you cannot increase both the row and the column.
dp[n, n] will have your answer.
Given a matrix [n,n], how many ways we can reach from [0,0] to [n,n] by increasing either a col or a row?
(n*2-2) choose (n*2-2)/2
If you can only go down or right (i.e., increase row or col), it seems like a binary proposition -- we can think of 'down' or 'right' as '0' or '1'.
In an nxn matrix, every path following the down/right condition will be n*2-2 in length (for example, in a 3x3 square, paths are always length 4; in a 4x4 square, length 6).
The number of total combinations for 0's and 1's in binary numbers of x digits is 2^x. In this case, our 'x' is n*2-2, but we cannot use all the combinations since the number of 'down's or 'right's cannot exceed n-1. It seems we need all binary combinations that have an equal number of 0's and 1's. And the solution is ... tada:
(n*2-2) choose (n*2-2)/2
In Haskell, you could write the following non-recursive function to list the paths:
import Data.List
mazeWays n = nub $ permutations $ concat $ replicate ((n*2-2) `div` 2) "DR"
if you want the number of paths, then:
length $ mazeWays n
Javascript solutions with sample
var arr = [
[1, 1, 1, 0, 0, 1, 0],
[1, 0, 1, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 0, 0],
[1, 1, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1]
];
function sols2(arr){
var h = arr.length,
w = arr[0].length,
i, j, current, left, top;
for(i = 0; i < h; i++){
for(j = 0; j < w; j++){
current = arr[i][j];
top = i === 0 ? 0.5 : arr[i - 1][j];
left = j === 0 ? 0.5 : arr[i][j-1];
if(left === 0 && top === 0){
arr[i][j] = 0;
} else if(current > 0 && (left > 0 || top > 0)){
arr[i][j] = (left + top) | 0;
} else {
console.log('a6');
arr[i][j] = 0;
}
}
}
return arr[h-1][w-1];
}
sols2(arr);

StackOverflow in my flood fill

I am currently creating a little paint program for exercise. Right now i'm trying to do the paint bucket tool, or in other words a flood fill. The funny thing is: if the number of pixels which have to be filled is small, everything works fine. If hate number of to filling pixels is higher, it gives me a SO-Exception. Here is my code:
private void FloodFill(Bitmap picture, int x, int y)
{
if (x <= 0 || y <= 0 || x >= DrawingPanel.Width || y >= DrawingPanel.Height)
{
return;
}
if (picture.GetPixel(x, y) != löschFarbe)
{
return;
}
if (picture.GetPixel(x, y) == löschFarbe)
{
picture.SetPixel(x, y, ColorButton.BackColor);
}
FloodFill(picture, x + 1, y);
FloodFill(picture, x, y + 1);
FloodFill(picture, x - 1, y);
FloodFill(picture, x, y - 1);
FloodFill(picture, x + 1, y + 1);
FloodFill(picture, x - 1, y + 1);
FloodFill(picture, x + 1, y - 1);
FloodFill(picture, x - 1, y - 1);
}
"löschFarbe" is the color which is clicked (which will be erased/overwritten with another color)
Error occurring: If i want to fill the complete picture or a big space, I get an error here:
if (picture.GetPixel(x, y) != löschFarbe)
{
return;
}
Anyone knows how I can solve this?
BTW this is a picture of my program:
Even a moderate sized floodfill will blow your call stack.
Try converting this recursion based method to a stack based method as in this question.

Categories

Resources