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 |