Dev Ghai commited on 2014-01-05 09:37:05
Showing 2 changed files, with 74 additions and 15 deletions.
... | ... |
@@ -1,9 +1,12 @@ |
1 | 1 |
#define DEVELOPING |
2 |
+#define DO_THREADED |
|
3 |
+#define DO_UNTHREADED |
|
2 | 4 |
|
3 | 5 |
using System; |
4 | 6 |
using System.Collections.Generic; |
5 | 7 |
using System.IO; |
6 | 8 |
using System.Diagnostics; |
9 |
+using System.Text; |
|
7 | 10 |
|
8 | 11 |
namespace Boggle |
9 | 12 |
{ |
... | ... |
@@ -28,7 +31,7 @@ namespace Boggle |
28 | 31 |
string input; |
29 | 32 |
bool isError = false; |
30 | 33 |
#endif |
31 |
- int boardSideLength = 3; |
|
34 |
+ int boardSideLength = 10; |
|
32 | 35 |
int minWordLength = 4; |
33 | 36 |
|
34 | 37 |
char wordListInput = 'Z'; |
... | ... |
@@ -91,12 +94,22 @@ namespace Boggle |
91 | 94 |
} |
92 | 95 |
} while (isError); |
93 | 96 |
#endif |
97 |
+ Console.WriteLine("Board Side Length (in Tiles): {0}, Minimum word length (in alphabets): {1}, Word list: {2}", boardSideLength, minWordLength, listToUseForLookup); |
|
94 | 98 |
st.Start(); |
95 | 99 |
bl.LoadList(listToUseForLookup, boardSideLength, minWordLength, Directory.GetCurrentDirectory()); |
96 | 100 |
st.Stop(); |
97 |
- Console.WriteLine("Loaded list in {0} ms.", st.ElapsedMilliseconds); |
|
101 |
+ Console.WriteLine("Loaded list (and serialized) in {0} ms.", st.ElapsedMilliseconds); |
|
102 |
+ |
|
103 |
+ //Generate a default random string for each run. |
|
104 |
+ string boardString = string.Empty; |
|
105 |
+ StringBuilder randomDefaultString = new StringBuilder(boardSideLength * boardSideLength); |
|
106 |
+ Random rand = new Random(DateTime.Now.Millisecond); |
|
107 |
+ for (int i = 0; i < boardSideLength * boardSideLength; i++) |
|
108 |
+ { |
|
109 |
+ randomDefaultString.Append((char)rand.Next('A', 'Z')); |
|
110 |
+ } |
|
111 |
+ boardString = randomDefaultString.ToString(); |
|
98 | 112 |
|
99 |
- string boardString = "qwertyuio"; |
|
100 | 113 |
#if !DEVELOPING |
101 | 114 |
do |
102 | 115 |
{ |
... | ... |
@@ -116,20 +129,23 @@ namespace Boggle |
116 | 129 |
board.Print(); |
117 | 130 |
BoggleSolver solver = new BoggleSolver(board, bl, minWordLength); |
118 | 131 |
st.Reset(); |
132 |
+#if DO_UNTHREADED |
|
119 | 133 |
st.Start(); |
120 | 134 |
HashSet<WordOnBoard> wordsUnThreaded = solver.GetWordsOnBoard(false); |
121 | 135 |
st.Stop(); |
122 | 136 |
PrintWords(wordsUnThreaded); |
123 | 137 |
|
124 | 138 |
Console.WriteLine("Got solution in {0} ms. Number of words (UNTHREADED): {1}\n\n", st.ElapsedMilliseconds, wordsUnThreaded.Count); |
139 |
+#endif |
|
125 | 140 |
st.Reset(); |
141 |
+#if DO_THREADED |
|
126 | 142 |
st.Start(); |
127 | 143 |
HashSet<WordOnBoard> wordsThreaded = solver.GetWordsOnBoard(true); |
128 | 144 |
st.Stop(); |
129 |
- st.Reset(); |
|
130 | 145 |
PrintWords(wordsThreaded); |
131 | 146 |
Console.WriteLine("\nGot solution in {0} ms. Number of words (THREADED): {1}.", st.ElapsedMilliseconds, wordsThreaded.Count); |
132 |
- |
|
147 |
+#endif |
|
148 |
+#if DO_THREADED && DO_UNTHREADED |
|
133 | 149 |
Console.WriteLine("Time for sanity checks... comparing solutions from threaded and non-threaded mode."); |
134 | 150 |
if (wordsThreaded.Count == wordsUnThreaded.Count) |
135 | 151 |
{ |
... | ... |
@@ -147,7 +163,7 @@ namespace Boggle |
147 | 163 |
Console.WriteLine("Words in threaded collection that are not in unthreaded collection:"); |
148 | 164 |
PrintWords(wordsThreaded); |
149 | 165 |
} |
150 |
- |
|
166 |
+#endif |
|
151 | 167 |
Console.WriteLine("\nPress any key to exit."); |
152 | 168 |
Console.ReadKey(); |
153 | 169 |
} |
... | ... |
@@ -12,6 +12,13 @@ namespace Boggle |
12 | 12 |
BoggleBoard _Board; |
13 | 13 |
int _MinWordLength; |
14 | 14 |
HashSet<WordOnBoard> _wordsOnBoard; |
15 |
+ //WaitHandle.WaitAll can wait at most for 64 handles. |
|
16 |
+ int _MaxParallelSolvers; |
|
17 |
+ ManualResetEvent[] doneEvents; |
|
18 |
+ Queue<int> openSlots; |
|
19 |
+ int[] correspondingSlots; |
|
20 |
+ |
|
21 |
+ static int doneEventCount; |
|
15 | 22 |
|
16 | 23 |
/// <summary> |
17 | 24 |
/// Constructor. |
... | ... |
@@ -26,6 +33,13 @@ namespace Boggle |
26 | 33 |
_MinWordLength = minWordLength; |
27 | 34 |
_wordEqualityComparer = new WordComparer(); |
28 | 35 |
_wordsOnBoard = new HashSet<WordOnBoard>(_wordEqualityComparer); |
36 |
+ _MaxParallelSolvers = _Board.Tiles.Length > 64 ? 64 : _Board.Tiles.Length; |
|
37 |
+ doneEvents = new ManualResetEvent[_MaxParallelSolvers]; |
|
38 |
+ openSlots = new Queue<int>(); |
|
39 |
+ correspondingSlots = new int[_Board.Tiles.Length]; |
|
40 |
+ |
|
41 |
+ for (int i = 0; i < _MaxParallelSolvers; i++) |
|
42 |
+ openSlots.Enqueue(i); |
|
29 | 43 |
} |
30 | 44 |
|
31 | 45 |
/// <summary> |
... | ... |
@@ -40,25 +54,52 @@ namespace Boggle |
40 | 54 |
return GetWordsOnBoardThreaded(); |
41 | 55 |
} |
42 | 56 |
|
43 |
- private ManualResetEvent[] doneEvents; |
|
57 |
+ |
|
44 | 58 |
|
45 | 59 |
/// <summary> |
46 | 60 |
/// Entry point for getting all possible words on board in threaded manner. |
47 | 61 |
/// </summary> |
62 |
+ /// <remarks> |
|
63 |
+ /// There can be two approaches: |
|
64 |
+ /// 1. Start scanning from each tile on the board and add new tile if any of |
|
65 |
+ /// it is left over. |
|
66 |
+ /// 2. Divide the number of tiles on the board by max number of threads that the |
|
67 |
+ /// system can support and wait for each chunk to complete. This is less efficient |
|
68 |
+ /// than the first approach because there can be chunks that exit earlier compared |
|
69 |
+ /// to most time consuming chunk. |
|
70 |
+ /// |
|
71 |
+ /// Hence this function implements the first approach. |
|
72 |
+ /// </remarks> |
|
48 | 73 |
/// <returns>Hashset of all words that can appear on the board.</returns> |
49 | 74 |
private HashSet<WordOnBoard> GetWordsOnBoardThreaded() |
50 | 75 |
{ |
51 |
- doneEvents = new ManualResetEvent[_Board.SideLength * _Board.SideLength]; |
|
52 |
- for (int i = 0; i < _Board.SideLength; i++) |
|
76 |
+ int i = 0, j = 0; |
|
77 |
+ int tileIndex = 0; |
|
78 |
+ int nextSlot; |
|
79 |
+ while (tileIndex < _Board.Tiles.Length) |
|
53 | 80 |
{ |
54 |
- for (int j = 0; j < _Board.SideLength; j++) |
|
81 |
+ while (openSlots.Count > 0 && tileIndex < _Board.Tiles.Length) |
|
55 | 82 |
{ |
56 |
- doneEvents[i * _Board.SideLength + j] = new ManualResetEvent(false); |
|
57 |
- |
|
83 |
+ //Resume from the previous value |
|
84 |
+ i = tileIndex / _Board.SideLength; |
|
85 |
+ j = tileIndex % _Board.SideLength; |
|
86 |
+ nextSlot = openSlots.Dequeue(); |
|
87 |
+ if (doneEvents[nextSlot] != null) |
|
88 |
+ doneEvents[nextSlot].Reset(); |
|
89 |
+ else |
|
90 |
+ doneEvents[nextSlot] = new ManualResetEvent(false); |
|
58 | 91 |
ThreadPool.QueueUserWorkItem(CollectWordsFromPivotThreaded, _Board[i, j]); |
92 |
+ correspondingSlots[tileIndex] = nextSlot; |
|
93 |
+ tileIndex++; |
|
59 | 94 |
} |
95 |
+ |
|
96 |
+ //If all the slots have filled up, then wait for atleast one to get free before |
|
97 |
+ //queueing another thread. |
|
98 |
+ WaitHandle.WaitAny(doneEvents); |
|
60 | 99 |
} |
100 |
+ |
|
61 | 101 |
WaitHandle.WaitAll(doneEvents); |
102 |
+ |
|
62 | 103 |
return _wordsOnBoard; |
63 | 104 |
} |
64 | 105 |
|
... | ... |
@@ -68,14 +109,16 @@ namespace Boggle |
68 | 109 |
//Copy the board |
69 | 110 |
BoggleBoard boardForThisThread = new BoggleBoard(this._Board); |
70 | 111 |
//Initialize a list that will hold the tiles that will make up the word. Its max size will be square of side. |
71 |
- List<Tile> newWordList = new List<Tile>(boardForThisThread.SideLength * boardForThisThread.SideLength); |
|
112 |
+ List<Tile> newWordList = new List<Tile>(_Board.Tiles.Length); |
|
72 | 113 |
//Figure out the tile in the new board |
73 | 114 |
Tile t = boardForThisThread[passedTile.X, passedTile.Y]; |
74 | 115 |
//Initialize this list with the tile that we will start finding words from. |
75 | 116 |
newWordList.Add(t); |
76 | 117 |
//Collect all the possible words from this tile. End result should be in the Hashset. |
77 | 118 |
CollectWordsFromPivot(boardForThisThread, _WordList.Wordlist[t.Alphabet].Next, newWordList); |
78 |
- doneEvents[t.X * _Board.SideLength + t.Y].Set(); |
|
119 |
+ int tileNumberOnBoard = (t.X * _Board.SideLength + t.Y) ; |
|
120 |
+ doneEvents[correspondingSlots[tileNumberOnBoard]].Set(); |
|
121 |
+ openSlots.Enqueue(tileNumberOnBoard % _MaxParallelSolvers); |
|
79 | 122 |
} |
80 | 123 |
|
81 | 124 |
private HashSet<WordOnBoard> GetWordsOnBoardNoThreads() |
... | ... |
@@ -85,7 +128,7 @@ namespace Boggle |
85 | 128 |
for (int j = 0; j < _Board.SideLength; j++) |
86 | 129 |
{ |
87 | 130 |
//Collect all the tiles that make up the word. |
88 |
- List<Tile> newWordList = new List<Tile>(_Board.SideLength * _Board.SideLength); |
|
131 |
+ List<Tile> newWordList = new List<Tile>(_Board.Tiles.Length); |
|
89 | 132 |
//Initialize this list with the tile that we will start finding words from. |
90 | 133 |
newWordList.Add(_Board[i, j]); |
91 | 134 |
//Collect all the possible words from this tile. End result should be in the Hashset. |
92 | 135 |