I am trying to optimize a search algorithm I am using to find marked Symbols in TwinCat 3 through the ADS Interface. The question is not TwinCat related so don't get scared off yet.
The problems:
Symbols are not loaded at once. I think the TwinCatAds library use lazy loading.
Symbols have treelike structure of non-binary not balanced tree.
The solution:
You can open more than one stream to ADS. And handle the the streams in multiple threads.
The question is, I divide the first level of symbols by the number of the processor cores. So Because the tree is unbalanced some of the Threads finish faster than the others. Because of this I need a nicer solution how to divide the work between the threads.
PS: I can't use the Parallel.ForEach(). Because of the streams it results in the same or greater time amount as the single thread solution.
My test code looks looks this, it just counts all Symbols of a huge Project.
using TwinCAT.Ads;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Collections;
namespace MultipleStreamsTest
{
class Program
{
static int numberOfThreads = Environment.ProcessorCount;
static TcAdsClient client;
static TcAdsSymbolInfoLoader symbolLoader;
static TcAdsSymbolInfoCollection[] collection = new TcAdsSymbolInfoCollection[numberOfThreads];
static int[] portionResult = new int[numberOfThreads];
static int[] portionStart = new int[numberOfThreads];
static int[] portionStop = new int[numberOfThreads];
static void Connect()
{
client = new TcAdsClient();
client.Connect(851);
Console.WriteLine("Conected ");
}
static void Main(string[] args)
{
Connect();
symbolLoader = client.CreateSymbolInfoLoader();
CountAllOneThread();
CountWithMultipleThreads();
Console.ReadKey();
}
static public void CountAllOneThread()
{
Stopwatch stopwatch = new Stopwatch();
int index = 0;
stopwatch.Start();
Console.WriteLine("Counting with one thread...");
//Count all symbols
foreach (TcAdsSymbolInfo symbol in symbolLoader)
{
index++;
}
stopwatch.Stop();
//Output
Console.WriteLine("Counted with one thred " + index + " symbols in " + stopwatch.Elapsed);
}
static public int countRecursive(TcAdsSymbolInfo symbol)
{
int i = 0;
TcAdsSymbolInfo subSymbol = symbol.FirstSubSymbol;
while (subSymbol != null)
{
i = i + countRecursive(subSymbol);
subSymbol = subSymbol.NextSymbol;
i++;
}
return i;
}
static public void countRecursiveMultiThread(object portionNum)
{
int portionNumAsInt = (int)portionNum;
for (int i = portionStart[portionNumAsInt]; i <= portionStop[portionNumAsInt]; i++)
{
portionResult[portionNumAsInt] += countRecursive(collection[portionNumAsInt][i]);//Collection Teil
}
}
static public void CountWithMultipleThreads()
{
Stopwatch stopwatch = new Stopwatch();
int sum = 0;
stopwatch.Start();
Console.WriteLine("Counting with multiple thread...");
for (int i = 0; i < numberOfThreads; i++)
{
collection[i] = symbolLoader.GetSymbols(true);
}
int size = (int)(collection[0].Count / numberOfThreads);
int rest = collection[0].Count % numberOfThreads;
int m = 0;
for (; m < numberOfThreads; m++)
{
portionStart[m] = m * size;
portionStop[m] = portionStart[m] + size - 1;
}
portionStop[m - 1] += rest;
Thread[] threads = new Thread[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++)
{
threads[i] = new Thread(countRecursiveMultiThread);
threads[i].Start(i);
Console.WriteLine("Thread #" + threads[i].ManagedThreadId + " started, fieldIndex: " + i);
}
//Check when threads finishing:
int threadsFinished = 0;
bool[] threadFinished = new bool[numberOfThreads];
int x = 0;
while (true)
{
if (threads[x].Join(10) && !threadFinished[x] )
{
Console.WriteLine("Thread #" + threads[x].ManagedThreadId + " finished ~ at: " + stopwatch.Elapsed);
threadsFinished++;
threadFinished[x] = true;
}
x++;
x = x % numberOfThreads;
if (threadsFinished == numberOfThreads) break;
Thread.Sleep(50);
}
foreach (int n in portionResult)
{
sum += n;
}
sum += collection[0].Count;
stopwatch.Stop();
//Output
Console.WriteLine("Counted with multiple threds in Collection " + sum + " symbols " + " in " + stopwatch.Elapsed);
for (int i = 0; i < numberOfThreads; i++)
{
Console.WriteLine("#" + i + ": " + portionResult[i]);
}
}
}
}
The console output:
If you trying to run the Code use TwinCat.Ads Version 4.0.17.0(that i am using). They broke something in the new version that is available with NuGet.
Make a thread pool and keep track of threads running and idling status. At each branch check if there is idling threads, if there is assign thread to sub branch.
Related
I made the following C# Console App:
class Program
{
static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
public static ConcurrentDictionary<int, int> StateCount { get; set; }
static int length = 1000000000;
static void Main(string[] args)
{
StateCount = new ConcurrentDictionary<int, int>();
for (int i = 0; i < 3; i++)
{
StateCount.AddOrUpdate(i, 0, (k, v) => 0);
}
Console.WriteLine("Processors: " + Environment.ProcessorCount);
Console.WriteLine("Starting...");
Console.WriteLine();
Timer t = new Timer(1000);
t.Elapsed += T_Elapsed;
t.Start();
Stopwatch sw = new Stopwatch();
sw.Start();
Parallel.For(0, length, (i) =>
{
var rand = GetRandomNumber();
int newState = 0;
if(rand < 0.3)
{
newState = 0;
}
else if (rand < 0.6)
{
newState = 1;
}
else
{
newState = 2;
}
StateCount.AddOrUpdate(newState, 0, (k, v) => v + 1);
});
sw.Stop();
t.Stop();
Console.WriteLine();
Console.WriteLine("Total time: " + sw.Elapsed.TotalSeconds);
Console.ReadKey();
}
private static void T_Elapsed(object sender, ElapsedEventArgs e)
{
int total = 0;
for (int i = 0; i < 3; i++)
{
if(StateCount.TryGetValue(i, out int value))
{
total += value;
}
}
int percent = (int)Math.Round((total / (double)length) * 100);
Console.Write("\r" + percent + "%");
}
public static double GetRandomNumber()
{
var bytes = new Byte[8];
rng.GetBytes(bytes);
var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11);
Double randomDouble = ul / (Double)(1UL << 53);
return randomDouble;
}
}
Before running this, the Task Manager reported <2% CPU usage (across all runs and machines).
I ran it on a machine with a Ryzen 3800X. The output was:
Processors: 16
Total time: 209.22
The speed reported in the Task Manager while it ran was ~4.12 GHz.
I ran it on a machine with an i7-7820HK The output was:
Processors: 8
Total time: 213.09
The speed reported in the Task Manager while it ran was ~3.45 GHz.
I modified Parallel.For to include the processor count (Parallel.For(0, length, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, (i) => {code});). The outputs were:
3800X: 16 - 158.58 # ~4.13
7820HK: 8 - 210.49 # ~3.40
There's something to be said about Parallel.For not natively identifying the Ryzen processors vs cores, but setting that aside, even here the Ryzen performance is still significantly poorer than would be expected (only ~25% faster with double the cores/processors, a faster speed, and larger L1-3 caches). Can anyone explain why?
Edit: Following a couple of comments, I made some changes to my code. See below:
static int length = 1000;
static void Main(string[] args)
{
StateCount = new ConcurrentDictionary<int, int>();
for (int i = 0; i < 3; i++)
{
StateCount.AddOrUpdate(i, 0, (k, v) => 0);
}
var procCount = Environment.ProcessorCount;
Console.WriteLine("Processors: " + procCount);
Console.WriteLine("Starting...");
Console.WriteLine();
List<double> times = new List<double>();
Stopwatch sw = new Stopwatch();
for (int m = 0; m < 10; m++)
{
sw.Restart();
Parallel.For(0, length, new ParallelOptions() { MaxDegreeOfParallelism = procCount }, (i) =>
{
for (int j = 0; j < 1000000; j++)
{
var rand = GetRandomNumber();
int newState = 0;
if (rand < 0.3)
{
newState = 0;
}
else if (rand < 0.6)
{
newState = 1;
}
else
{
newState = 2;
}
StateCount.AddOrUpdate(newState, 0, (k, v) => v + 1);
}
});
sw.Stop();
Console.WriteLine("Total time: " + sw.Elapsed.TotalSeconds);
times.Add(sw.Elapsed.TotalSeconds);
}
Console.WriteLine();
var avg = times.Average();
var variance = times.Select(x => (x - avg) * (x - avg)).Sum() / times.Count;
var stdev = Math.Sqrt(variance);
Console.WriteLine("Average time: " + avg + " +/- " + stdev);
Console.ReadKey();
Console.ReadKey();
}
The outside loop is 1,000 instead of 1,000,000,000, so there are "only" 1,000 parallel "tasks." Within each parallel "task" however there's now a loop of 1,000,000 actions, so the act of "getting the task" or whatever should have a much smaller affect on the total. I also loop the whole thing 10 times and get the average + standard devation. Output:
Ryzen 3800X: 158.531 +/- 0.429 # ~4.13
i7-7820HK: 202.159 +/- 2.538 # ~3.48
Even here, the Ryzen's twice as many threads and 0.60 GHz higher clock only result in a ~75% faster time for the total operation.
I need to count the number of comparisons done by the following search function..
Also how to calculate the execution time..Any help? I want the count and time to be printed in the output.
// C# program for Naive Pattern Searching
using System;
class GFG {
public static void search(String txt, String pat)
{
int M = pat.Length;
int N = txt.Length;
/* A loop to slide pat one by one */
for (int i = 0; i <= N - M; i++) {
int j;
/* For current index i, check for pattern
match */
for (j = 0; j < M; j++)
if (txt[i + j] != pat[j])
break;
// if pat[0...M-1] = txt[i, i+1, ...i+M-1]
if (j == M)
Console.WriteLine("Pattern found at index " + i);
}
}
// Driver code
public static void Main()
{
String txt = "AABAACAADAABAAABAA";
String pat = "AABA";
search(txt, pat);
}
}
// This code is Contributed by Sam007
I need to count the number of comparisons done by the following search function...
You can use counter for this:
public static void search(String txt, String pat, out int counter)
{
int M = pat.Length;
int N = txt.Length;
counter = 0;
/* A loop to slide pat one by one */
for (int i = 0; i <= N - M; i++)
{
int j;
/* For current index i, check for pattern
match */
for (j = 0; j < M; j++)
{
counter++; // counter for below if statement
if (txt[i + j] != pat[j])
{
break;
}
}
// if pat[0...M-1] = txt[i, i+1, ...i+M-1]
if (j == M)
{
Console.WriteLine("Pattern found at index " + i);
}
counter++; // counter for above if statement
}
}
Also how to calculate the execution time.
You can use StopWatch class for this case:
public static async Task Main(string[] args)
{
String txt = "AABAACAADAABAAABAA";
String pat = "AABA";
var stopWatch = new Stopwatch();
stopWatch.Start();
search(txt, pat, out var counter);
stopWatch.Stop();
Console.WriteLine("--------------");
Console.WriteLine($"Count of operations: {counter}, elapsed time: {stopWatch.ElapsedMilliseconds} miliseconds");
}
What I want to do is to spread tasks over a number of servers randomly with very little bias if possible. So far what I worked on is able to randomly spread the tasks over a different servers. The problem is that whenever I spread the tasks over the servers, it spreads the 1-3 tasks per server. The load balancing method used is Power of Two Choices. Forgive me if I get the concept wrong.
Power of Two Choices is where two random queues are chosen, where the one with the least tasks, is assigned a task. Correct me if I'm wrong.
The photo below shows my current output.
What I want is this
My main file is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace C1._2
{
class Program
{
static void details(Server[] server, int num)
{
int count = 0;
for (int i = 0; i < server.Length; i++)
{
if (server[i].Tasks == num)
{
count++;
}
}
Console.WriteLine("There are " + count + " servers with " + num + " tasks.");
}
static void Main(string[] args)
{
Random rand = new Random();
int n = 256; //number of tasks
int m; //number of servers
m = 64;//Convert.ToInt32(Console.ReadLine())
Console.WriteLine("Number of servers(m): " + m);
int d = 1; //random servers to be chosen
Console.WriteLine("Number of tasks(n): " + n);
Console.WriteLine("Number of randomly selected server(d): " + d);
//Main server setup
Server[] servers = new Server[m];
for (int i = 0; i < m; i++)
{
servers[i] = new Server(i);
}
if (d == 1)
{
for (int i = 0; i < n; i++)
{
int randS = rand.Next(m);
servers[randS].Tasks++;
}
}
//Power of Two choice algorithm is here
if (d == 2)
{
for(int i = 0; i < n; i++)
{
Server s1 = servers[rand.Next(m)];
Server s2 = servers[rand.Next(m)];
if (s1.Tasks < s2.Tasks)
{
for(int j = 0; j < m; j++)
{
if (servers[j].SNo == s1.SNo)
{
servers[j].Tasks++;
}
}
}
else
{
for (int j = 0; j < m; j++)
{
if (servers[j].SNo == s2.SNo)
{
servers[j].Tasks++;
}
}
}
}
}
//Server min max
Server maxServer = new Server();
maxServer = servers[0];
for (int i = 0; i < m; i++)
{
if (maxServer.Tasks < servers[i].Tasks)
{
maxServer = servers[i];
}
}
Console.WriteLine("\nIndex of servers with most tasks: " + "[" + maxServer.SNo + "]");
Console.WriteLine("Highest number of tasks: " + maxServer.Tasks+"\n");
Server minServer = new Server();
minServer = servers[0];
for (int i = 0; i < m; i++)
{
if (minServer.Tasks > servers[i].Tasks)
{
minServer = servers[i];
}
}
Console.WriteLine("\nIndex of servers with least tasks: " + "[" + minServer.SNo + "]");
Console.WriteLine("Lowest number of tasks: " + minServer.Tasks+"\n");
//details
details(servers, 0);
details(servers, 1);
details(servers, 2);
details(servers, 3);
details(servers, 4);
details(servers, 5);
details(servers, 6);
details(servers, 7);
details(servers, 8);
details(servers, 9);
}
}
}
Accompanied Server class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace C1._2
{
class Server
{
private int server_number;
private int task;
public Server()
{
this.server_number = (int)0;
this.task = 0;
}
public Server(int sn)
{
this.server_number = sn;
this.task = 0;
}
public int SNo
{
get
{
return this.server_number;
}
set
{
this.server_number = value;
}
}
public int Tasks
{
get
{
return this.task;
}
set
{
this.task = value;
}
}
}
}
Any advice on how to do so?
When you say "random... where the one with the least tasks" means to me not random. But i think that I understand what you want. So we need to select the list of items that have least tasks and chose one of this randomic.
I rewrited a part of the class Program. Let me know if is this the expected result.
class Program
{
static void Main(string[] args)
{
//Random rand = new Random();
int numberOfTasks = 256; //number of tasks
int numberOfServers; //number of servers
numberOfServers = 64;//Convert.ToInt32(Console.ReadLine())
Console.WriteLine("Number of servers(m): " + numberOfServers);
//int d = 1; //random servers to be chosen
Console.WriteLine("Number of tasks(n): " + numberOfTasks);
//Console.WriteLine("Number of randomly selected server(d): " + d);
//Main server setup
Server[] servers = new Server[numberOfServers];
for (int i = 0; i < numberOfServers; i++)
{
servers[i] = new Server(i);
}
for(int i = 0; i < numberOfTasks; i++){
var minimumTasksValue = servers.Min(x => x.Tasks);
var listOfServersToSpread = servers.Where(x => x.Tasks == minimumTasksValue).ToList();
Random rand = new Random();
var randomServer = rand.Next(0, listOfServersToSpread.Count() - 1);
listOfServersToSpread[randomServer].Tasks++;
}
//Server min max
Server maxServer = new Server();
maxServer = servers[0];
for (int i = 0; i < numberOfServers; i++)
{
if (maxServer.Tasks < servers[i].Tasks)
{
maxServer = servers[i];
}
}
Console.WriteLine("\nIndex of servers with most tasks: " + "[" + maxServer.SNo + "]");
Console.WriteLine("Highest number of tasks: " + maxServer.Tasks + "\n");
Server minServer = new Server();
minServer = servers[0];
for (int i = 0; i < numberOfServers; i++)
{
if (minServer.Tasks > servers[i].Tasks)
{
minServer = servers[i];
}
}
Console.WriteLine("\nIndex of servers with least tasks: " + "[" + minServer.SNo + "]");
Console.WriteLine("Lowest number of tasks: " + minServer.Tasks + "\n");
//details
details(servers);
Console.ReadLine();
}
static void details(Server[] server)
{
var numberOfTasksAvailable = server.Select(x => x.Tasks).Distinct().OrderBy(x => x);
foreach(var numberOfTasks in numberOfTasksAvailable)
{
Console.WriteLine("There are " + server.Count(x => x.Tasks == numberOfTasks) + " servers with " + numberOfTasks + " tasks.");
}
}
}
Updated load balancing algorithm
if (d == 2)
{
for(int i = 0; i < n; i++)
{
int a = rand.Next(m);
int b = rand.Next(m);
servers[a < b ? a : b].Tasks++;
}
}
My problem was over-complicating the code. What it does above is that it randomly chooses two random servers from the total servers available. Compares which server has the least burden/tasks, and assigns a task to it.
Is there a simple way to check if any threads are waiting for a specific ManualResetEvent
object to be set? It seems to me that the computer must be somehow keeping a queue of the
paused threads to restore later, so I'm just hoping to do the obvious and access
this list (just to check whether or not it is empty)...but I couldn't find a
method from looking at Microsoft's related Threading/WaitHandle/Monitor help pages.
Test code is below. I'd simply like to replace the uglyWaiters counter.
using System;
using System.Threading;
namespace demo
{
class Program
{
static void Main()
{
var gate = new ManualResetEvent(false);
int uglyWaiters = 0; // I want to stop using this variable and instead use something built into ManualResetEvent
new Thread(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("a" + i);
uglyWaiters++;
gate.WaitOne();
uglyWaiters--;
Thread.Sleep(400);
}
}).Start();
new Thread(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("b" + i);
uglyWaiters++;
gate.WaitOne();
uglyWaiters--;
Thread.Sleep(500);
}
}).Start();
for (int count = 0; count < 15; count++)
{
Console.WriteLine(uglyWaiters + " threads waiting at t=" + count*230 + "ms");
gate.Set(); Thread.Sleep(30); gate.Reset();
Thread.Sleep(200);
}
Console.ReadLine();
}
}
}
I'm fairly new to C# and I made a simple program simmulating lotto draws. It takes first (random) numbers and calculates how many draws it takes to win. It's a polish lotto, so there are 6 numbers to match.
Everything works fine, when program is run in simple for loop. But there is a problem, when I use Parallel For or whatever other multitasking or multithreding option.
First the code:
class Program
{
public static int howMany = 100;
static void Main(string[] args)
{
Six my;
Six computers;
long sum = 0;
double avg = 0;
int min = 1000000000;
int max = 0;
for (int i = 0; i < howMany; i++)
{
my = new Six();
Console.WriteLine((i + 1).ToString() + " My: " + my.ToString());
int counter = 0;
do
{
computers = new Six();
counter++;
} while (!my.Equals(computers));
Console.WriteLine((i + 1).ToString() + " Computers: " + computers.ToString());
Console.WriteLine(counter.ToString("After: ### ### ###") + "\n");
if (counter < min)
min = counter;
if (counter > max)
max = counter;
sum += counter;
}
avg = sum / howMany;
Console.WriteLine("Average: " + avg);
Console.WriteLine("Sum: " + sum);
Console.WriteLine("Min: " + min);
Console.WriteLine("Max: " + max);
Console.Read();
}
}
class Six : IEquatable<Six>
{
internal byte first;
internal byte second;
internal byte third;
internal byte fourth;
internal byte fifth;
internal byte sixth;
private static Random r = new Random();
public Six()
{
GenerateRandomNumbers();
}
public bool Equals(Six other)
{
if (this.first == other.first
&& this.second == other.second
&& this.third == other.third
&& this.fourth == other.fourth
&& this.fifth == other.fifth
&& this.sixth == other.sixth)
return true;
else
return false;
}
private void GenerateRandomNumbers()
{
byte[] numbers = new byte[6];
byte k = 0;
for (int i = 0; i < 6; i++)
{
do
{
k = (byte)(r.Next(49) + 1);
}while (numbers.Contains(k));
numbers[i] = k;
k = 0;
}
Array.Sort(numbers);
this.first = numbers[0];
this.second = numbers[1];
this.third = numbers[2];
this.fourth = numbers[3];
this.fifth = numbers[4];
this.sixth = numbers[5];
}
public override string ToString()
{
return this.first + ", " + this.second + ", " + this.third + ", " + this.fourth + ", " + this.fifth + ", " + this.sixth;
}
}
And when I try to make it Parallel.For:
long sum = 0;
double avg = 0;
int min = 1000000000;
int max = 0;
Parallel.For(0, howMany, (i) =>
{
Six my = new Six();
Six computers;
Console.WriteLine((i + 1).ToString() + " My: " + my.ToString());
int counter = 0;
do
{
computers = new Six();
// Checking when it's getting stuck
if (counter % 100 == 0)
Console.WriteLine(counter);
counter++;
} while (!my.Equals(computers));
Console.WriteLine((i + 1).ToString() + " Computers: " + computers.ToString());
Console.WriteLine(counter.ToString("After: ### ### ###") + "\n");
// It never get to this point, so there is no problem with "global" veriables
if (counter < min)
min = counter;
if (counter > max)
max = counter;
sum += counter;
});
Program gets stuck at some point. Counters get to ~3,000-40,000 and refuses to go further.
What I tried:
Making class a struct
Collecting Garbage every ~1000 iterations
Using ThreadPool
Using Task.Run
Making random class Program member only (tired to make Six class "lighter")
But I got nothing.
I know that this might be a very simple thing for some of you, but man got to learn somehow ;) I even bought a book about async programming to find out why doesn't it work, but couldn't figure it out.
Random isn't thread safe...
Wait for your code to stop writing new lines in the parallel version and the pause. This stops all threads. You'll notice that all your parallel threads are in the while loop.
The numbers array's are all 1,0,0,0,0,0 and r.Next only returns 1. which the byte array always contains. So, you broke Random
To fix this you'll need to make r thread safe, either by locking r every time you access r.Next or changing the static declaration to
private static readonly ThreadLocal<Random> r
= new ThreadLocal<Random>(() => new Random());
and the Next call becomes
k = (byte)(r.Value.Next(49) + 1);
This will create a new static Random instance per thread.
As you noted, creating lots of Random's at the same time result the in same sequence of numbers being produced, to get around this add a seed class
static class RGen
{
private static Random seedGen = new Random();
public static Random GetRGenerator()
{
lock (seedGen)
{
return new Random(seedGen.Next());
}
}
}
and change the declaration to
private static readonly ThreadLocal<Random> r
= new ThreadLocal<Random>(() => RGen.GetRGenerator());
This will ensure each new random instance has a different seed value.
I found a solution based on what James Barrass wrote:
public static readonly ThreadLocal<Random> r = new ThreadLocal<Random>(() => new Random(Thread.CurrentThread.ManagedThreadId + DateTime.Now.Miliseconds));
That made program running well :)