First of all, I am pretty new to coding. I am currently trying to program my Discord-bot and I ran into this issue:
I want to create a command with a timer on it, so everytime someone uses this command, the timer starts ticking and after the timer reached a certain amount of seconds, the user is able to use this command again. The command is a "joke-command" where everytime you use it, the bot tells you a joke.
Is there a way to implement this?
I currently have this:
//joke-integer
int jokeNumber;
jokeNumber = 0;
//joke-list
joke = new string[]
{
"aaa",
"bbb",
"ccc",
};
//joke-command
commands.CreateCommand("joke")
.Do(async e =>
{
jokeNumber = jokeNumber + 1;
if (jokeNumber.Equals(3))
{
jokeNumber = 0;
}
else
{
jokeNumber = jokeNumber + 0;
}
string jokePost = joke[jokeNumber];
await e.Channel.SendMessage(jokePost);
});
So this (^) just tells you a joke out of a list if you use the command "!joke" but I want that this command is only "enabled" if the timer passed for example 100 seconds.
You need to store the last time an user has requested the command, and then check this date every time a new command is requested.
Inside your class:
private static readonly ConcurrentDictionary<long, DateTimeOffset> UsersJokesLastCall = new ConcurrentDictionary<long, DateTimeOffset>();
When creating your command:
commands.CreateCommand("joke")
.Do(async e =>
{
var id = e.ChannelId; // example using channel id as unique key
var nowUtc = DateTimeOffset.UtcNow;
var canReturnJoke = true;
UsersJokesLastCall.AddOrUpdate(id, nowUtc, (key, oldValue) =>
{
var elapsed = nowUtc - oldValue; // check elapsed time
if(elapsed.TotalSeconds < 100)
{
canReturnJoke = false; // min time has not passed
return oldValue; // do not change the last joke time
}
return nowUtc; // update to current time
});
if(!canReturnJoke)
{
// return something to the user
await e.Channel.SendMessage("Try later");
return;
}
jokeNumber = jokeNumber + 1;
if (jokeNumber.Equals(3))
{
jokeNumber = 0;
}
else
{
jokeNumber = jokeNumber + 0;
}
string jokePost = joke[jokeNumber];
await e.Channel.SendMessage(jokePost);
});
You may need to further work on this code, but this should give you the overall approach.
Related
I am using a restful api that will return a maximum amount of 50 records per call, if you need more than this you must create multiple calls and pass it an offset.
There are times when we require 'all' of the results to be loaded, we are using something that resembles the code below - this does one request after another and adds the results to a list, stopping when either the maximum is reached or the amount returned in any one call was less than the amount requested.
How can refactor this (using tasks/parallel/threads) to load this data with multiple requests at any one time and still get the exact same results, I have looked at creating multiple Tasks and awaiting them but the problem is that the amount of records to load is unknown until the point of 'no more being available' or hitting the max.
public IEnumerable<T> GetItems(int maxAmount = -1)
{
var moreData = true;
var result = new List<T>();
var counter = 0;
var batchAmount = 50;
while(moreData)
{
var requestAmount = Math.Min(batchAmount,maxAmount-result.Count);
var items = GetItemsFromService<T>(requestAmount,counter);
counter += items.Count;
moreData = items.Count == requestAmount && (maxAmount == -1 || maxAmount> items.Count);
result.AddRange(items);
}
return result;
}
private IEnumerable<T> GetItemsFromService(int batchAmount,int offset)
{
//Lets assume that this gets data from a rest service that returns a maximum of batchAmount
//and offsets using the offset variable.
}
Unfortunately you can't use async here as you are relying on the number of items from the previous request. This must be synchronous unless you want to do some asynchronous operations on the data that you've received.
It must be a badly designed API that returns paged result without total pages or total number of items.
I managed to get this working, basically I keep sending the paged request until one of the requests come back with nothing - since they are started in order once a response comes back with nothing we do not need to make anymore requests, just allow existing requests to finish.
My working code looks like this.
private IEnumerable<object> GetEntitiesInParallel(Type type, string apiPath, Dictionary<string, string> parameters, int startPosition, int maxAmount)
{
var context = new TaskThreadingContext(maxAmount, startPosition);
var threads = Enumerable.Range(0, NumberOfThreads).Select(i =>
{
var task = Task.Factory.StartNew(() =>
{
while (context.Continue)
{
var rawData = String.Empty;
var offset = context.NextAmount();
var result = GetEntitiesSingleRequest(type, parameters, offset, apiPath, out rawData);
if (result.Any())
{
context.AddResult(result.Cast<object>(), rawData);
}
else
{
context.NoResult();
}
}
});
return task;
}).ToArray();
Task.WaitAll(threads);
var results = context.GetResults<object>();
return results;
}
private IEnumerable<object> GetEntitiesSingleRequest(Type type,Dictionary<string,string> parameters,
int offset,string apiPath, out string rawData)
{
var request = Utility.CreateRestRequest(apiPath, Method.GET,ApiKey,50,offset,parameters);
type = typeof(List<>).MakeGenericType(type);
var method = Client.GetType().GetMethods().Single(m => m.IsGenericMethod && m.Name == "Execute").MakeGenericMethod(type);
try
{
dynamic response = (IRestResponse)method.Invoke(Client, new object[] { request });
var data = response.Data as IEnumerable;
var dataList = data.Cast<object>().ToList();
rawData = response.Content.Replace("\n", Environment.NewLine);
return dataList.OfType<object>().ToList();
}
catch (Exception ex)
{
if (ex.Message.IndexOf("404") != -1)
{
rawData = null;
return Enumerable.Empty<object>();
}
throw;
}
}
private class TaskThreadingContext
{
private int batchAmount = 50;
private object locker1 = new object();
private object locker2 = new object();
private CancellationTokenSource tokenSource;
private CancellationToken token;
private volatile bool cont = true;
private volatile int offset = 0;
private volatile int max = 0;
private volatile int start = 0;
private List<object> result = new List<object>();
private List<string> raw = new List<string>();
public bool Continue { get { return cont; } }
public TaskThreadingContext(int maxRows = 0,int startPosition = 0)
{
max = maxRows;
offset = start = startPosition;
}
public int NextAmount()
{
lock(locker1)
{
var ret = offset;
var temp = offset + batchAmount;
if (temp - start > max && max > 0)
{
temp = max - offset;
}
offset = temp;
if (offset - start >= max && max > 0)
{
cont = false;
}
return ret;
}
}
public TaskThreadingContext()
{
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
}
public void AddResult(IEnumerable<object> items,string rawData)
{
lock(locker2)
{
result.AddRange(items);
raw.Add(rawData);
}
}
public IEnumerable<T> GetResults<T>()
{
return this.result.Cast<T>().ToList();
}
public void NoResult()
{
cont = false;
}
}
I am looking to create an external application that monitors the 'FPS' of a DirectX application (like FRAPS without the recording). I have read several Microsoft articles on performance measuring tools - but I am looking to get the feedback (and experience) of the community.
My question: what is the best method for obtaining the FPS of a DirectX application?
Windows has some Event Tracing for Windows providers related to DirectX profiling. The most intresting ones are Microsoft-Windows-D3D9 and Microsoft-Windows-DXGI, which allow tracing of the frame presentation events. The simplest way to calculate FPS is to count the number of PresentStart events withing a time interval and divide that by the length of the interval.
To work with ETW in C#, install Microsoft.Diagnostics.Tracing.TraceEvent package.
The following code sample displays FPS of running processes:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using Microsoft.Diagnostics.Tracing.Session;
namespace ConsoleApp1
{
//helper class to store frame timestamps
public class TimestampCollection
{
const int MAXNUM = 1000;
public string Name { get; set; }
List<long> timestamps = new List<long>(MAXNUM + 1);
object sync = new object();
//add value to the collection
public void Add(long timestamp)
{
lock (sync)
{
timestamps.Add(timestamp);
if (timestamps.Count > MAXNUM) timestamps.RemoveAt(0);
}
}
//get the number of timestamps withing interval
public int QueryCount(long from, long to)
{
int c = 0;
lock (sync)
{
foreach (var ts in timestamps)
{
if (ts >= from && ts <= to) c++;
}
}
return c;
}
}
class Program
{
//event codes (https://github.com/GameTechDev/PresentMon/blob/40ee99f437bc1061a27a2fc16a8993ee8ce4ebb5/PresentData/PresentMonTraceConsumer.cpp)
public const int EventID_D3D9PresentStart = 1;
public const int EventID_DxgiPresentStart = 42;
//ETW provider codes
public static readonly Guid DXGI_provider = Guid.Parse("{CA11C036-0102-4A2D-A6AD-F03CFED5D3C9}");
public static readonly Guid D3D9_provider = Guid.Parse("{783ACA0A-790E-4D7F-8451-AA850511C6B9}");
static TraceEventSession m_EtwSession;
static Dictionary<int, TimestampCollection> frames = new Dictionary<int, TimestampCollection>();
static Stopwatch watch = null;
static object sync = new object();
static void EtwThreadProc()
{
//start tracing
m_EtwSession.Source.Process();
}
static void OutputThreadProc()
{
//console output loop
while (true)
{
long t1, t2;
long dt = 2000;
Console.Clear();
Console.WriteLine(DateTime.Now.ToString() + "." + DateTime.Now.Millisecond.ToString());
Console.WriteLine();
lock (sync)
{
t2 = watch.ElapsedMilliseconds;
t1 = t2 - dt;
foreach (var x in frames.Values)
{
Console.Write(x.Name + ": ");
//get the number of frames
int count = x.QueryCount(t1, t2);
//calculate FPS
Console.WriteLine("{0} FPS", (double)count / dt * 1000.0);
}
}
Console.WriteLine();
Console.WriteLine("Press any key to stop tracing...");
Thread.Sleep(1000);
}
}
public static void Main(string[] argv)
{
//create ETW session and register providers
m_EtwSession = new TraceEventSession("mysess");
m_EtwSession.StopOnDispose = true;
m_EtwSession.EnableProvider("Microsoft-Windows-D3D9");
m_EtwSession.EnableProvider("Microsoft-Windows-DXGI");
//handle event
m_EtwSession.Source.AllEvents += data =>
{
//filter out frame presentation events
if (((int)data.ID == EventID_D3D9PresentStart && data.ProviderGuid == D3D9_provider) ||
((int)data.ID == EventID_DxgiPresentStart && data.ProviderGuid == DXGI_provider))
{
int pid = data.ProcessID;
long t;
lock (sync)
{
t = watch.ElapsedMilliseconds;
//if process is not yet in Dictionary, add it
if (!frames.ContainsKey(pid))
{
frames[pid] = new TimestampCollection();
string name = "";
var proc = Process.GetProcessById(pid);
if (proc != null)
{
using (proc)
{
name = proc.ProcessName;
}
}
else name = pid.ToString();
frames[pid].Name = name;
}
//store frame timestamp in collection
frames[pid].Add(t);
}
}
};
watch = new Stopwatch();
watch.Start();
Thread thETW = new Thread(EtwThreadProc);
thETW.IsBackground = true;
thETW.Start();
Thread thOutput = new Thread(OutputThreadProc);
thOutput.IsBackground = true;
thOutput.Start();
Console.ReadKey();
m_EtwSession.Dispose();
}
}
}
Based on the source code of PresentMon project.
Fraps inserts a DLL into every running application and hooks specific DX calls to figure out the framerate and capture video, pretty sure that you'll have to do something similar. After a bit of poking around I found a Github project that does some basic DX hooking for doing captures and overlays, so that might be a good spot to start out with. Though I've not used it personally so I can't totally vouch for the quality.
http://spazzarama.com/2011/03/14/c-screen-capture-and-overlays-for-direct3d-9-10-and-11-using-api-hooks/
Building on https://stackoverflow.com/a/54625953/12047161:
I had more success not using the stopwatch as the event triggers seems to be asynchronous with the actual frames. I kept getting batches of 20-50 frames all at once, making the estimated FPS fluctuate between 50 and 250% of the actual value.
Instead i used TimeStampRelativeMSec
//handle event
m_EtwSession.Source.AllEvents += data =>
{
//filter out frame presentation events
if((int) data.ID == EventID_DxgiPresentStart && data.ProviderGuid == DXGI_provider)
{
int pid = data.ProcessID;
long t;
t = watch.ElapsedMilliseconds;
//if process is not yet in Dictionary, add it
if (!frames.ContainsKey(pid))
{
frames[pid] = new TimestampCollection();
string name = "";
var proc = Process.GetProcessById(pid);
if (proc != null)
{
using (proc)
{
name = proc.ProcessName;
}
}
else name = pid.ToString();
frames[pid].Name = name;
}
frames[pid].Add((long)data.TimeStampRelativeMSec);
}
};
property from the TraceEvent class, and calculate FPS by rounding the average time between an arbitrary number of past entries:
public double GetFrameTime(int count)
{
double returnValue = 0;
int listCount = timestamps.Count;
if(listCount > count)
{
for(int i = 1; i <= count; i++)
{
returnValue += timestamps[listCount - i] - timestamps[listCount - (i + 1)];
}
returnValue /= count;
}
return returnValue;
}
This method gave me far more accurate (Compared to, as available, in-game counters) of several different games i've tried.
I have following two loops in C#, and I am running these loops for a collection with 10,000 records being downloaded with paging using "yield return"
First
foreach(var k in collection) {
repo.Save(k);
}
Second
var collectionEnum = collection.GetEnumerator();
while (collectionEnum.MoveNext()) {
var k = collectionEnum.Current;
repo.Save(k);
k = null;
}
Seems like that the second loop consumes less memory and it faster than the first loop. Memory I understand may be because of k being set to null(Even though I am not sure). But how come it is faster than for each.
Following is the actual code
[Test]
public void BechmarkForEach_Test() {
bool isFirstTimeSync = true;
Func<Contact, bool> afterProcessing = contactItem => {
return true;
};
var contactService = CreateSerivce("/administrator/components/com_civicrm");
var contactRepo = new ContactRepository(new Mock<ILogger>().Object);
contactRepo.Drop();
contactRepo = new ContactRepository(new Mock<ILogger>().Object);
Profile("For Each Profiling",1,()=>{
var localenumertaor=contactService.Download();
foreach (var item in localenumertaor) {
if (isFirstTimeSync)
item.StateFlag = 1;
item.ClientTimeStamp = DateTime.UtcNow;
if (item.StateFlag == 1)
contactRepo.Insert(item);
else
contactRepo.Update(item);
afterProcessing(item);
}
contactRepo.DeleteAll();
});
}
[Test]
public void BechmarkWhile_Test() {
bool isFirstTimeSync = true;
Func<Contact, bool> afterProcessing = contactItem => {
return true;
};
var contactService = CreateSerivce("/administrator/components/com_civicrm");
var contactRepo = new ContactRepository(new Mock<ILogger>().Object);
contactRepo.Drop();
contactRepo = new ContactRepository(new Mock<ILogger>().Object);
var itemsCollection = contactService.Download().GetEnumerator();
Profile("While Profiling", 1, () =>
{
while (itemsCollection.MoveNext()) {
var item = itemsCollection.Current;
//if First time sync then ignore and overwrite the stateflag
if (isFirstTimeSync)
item.StateFlag = 1;
item.ClientTimeStamp = DateTime.UtcNow;
if (item.StateFlag == 1)
contactRepo.Insert(item);
else
contactRepo.Update(item);
afterProcessing(item);
item = null;
}
contactRepo.DeleteAll();
});
}
static void Profile(string description, int iterations, Action func) {
// clean up
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// warm up
func();
var watch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
I m using the micro bench marking, from a stackoverflow question itself benchmarking-small-code
The time taken is
For Each Profiling Time Elapsed 5249 ms
While Profiling Time Elapsed 116 ms
Your foreach version calls var localenumertaor = contactService.Download(); inside the profile action, while the enumerator version calls it outside of the Profile call.
On top of that, the first execution of the iterator version will exhaust the items in the enumerator, and on subsequent iterations itemsCollection.MoveNext() will return false and skip the inner loop entirely.
Background:
I'm working on a small app that will read Events from an Eventlog remotely via WMI. Basically I'm searching for when a workstation locks and unlocks.
Problem:
I create an array of threads. I loop through my dataset (computernames) and fire off multiple
ParameterizedThreadStart objects with a custom object (LockHunterArgs). The problem is that I know my dataset doesn't have duplicates in it. I added a console.writeline to the end of the thread function and it displays duplicates.
Also, before I tried using threads. If I ran the code synchronously it functioned fine. It just took a long time. So that's why I'm trying to introduce multithreading.
Example Output:
//...Snipped some unique lines above
Computer: COMP Time: 3/29/2012 8:05:11 AM Session: 3935dd76-6a10-41a9-bd96-86143c66482d
Computer: COMP Time: 3/29/2012 8:05:11 AM Session: 3935dd76-6a10-41a9-bd96-86143c66482d
//...Snipped some unique and duplicated lines below
My Hypothesis:
If I place a breakpoint in the first few lines of the get_lock_data function where it is casting and step to the next line. It is random. It will step forward once then hit the same line twice. I have even seen it go two lines down then go backwards. I assume that this is because I'm firing off threads and it is hitting the points at different times giving the illusion that it is going backwards. But it is almost like the object that is being passed in is being overwritten by later threads.
I tried creating another array of LockHunterArgs and creating and assigning them during the thread firing process but that also didn't work.
It is probably something dumb. Thanks in advance.
// lance
Code:
public class LockHunterArgs
{
public LockHunterArgs(string comp, DateTime limit, Guid session)
{
Computer = comp;
LimitTime = limit;
sessionID = session;
}
public string Computer;
public DateTime LimitTime;
public Guid sessionID;
}
public class LockHunter
{
private void get_lock_data(object args)
{
string computer = ((LockHunterArgs)args).Computer;
DateTime limitTime = ((LockHunterArgs)args).LimitTime;
Guid sessionID = ((LockHunterArgs)args).sessionID;
//....SNippet ... code connects to the box and pulls data...
Console.WriteLine("Computer: " + computer + " Time: " + limitTime.ToString() + " Session: " + sessionID.ToString());
}
public void HuntLocks()
{
//....Snippet... code connects to database and gets a list of objects (currentSessions)
Thread[] threadArray = new Thread[currentSessions.Count];
int cnt = 0;
foreach (LINQ.session sesson in currentSessions)
{
DateTime mostRecentTimestamp = (from q in db.actions
where q.session_id == sesson.uid
orderby q.timestamp descending
select q.timestamp).FirstOrDefault();
ParameterizedThreadStart start = new ParameterizedThreadStart(get_lock_data);
threadArray[cnt] = new Thread(start);
threadArray[cnt].Start(new LockHunterArgs(sesson.computername , mostRecentTimestamp, sesson.uid));
cnt++;
}
for (int i = 0; i < threadArray.Length; i++)
{
threadArray[i].Join();
}
Console.WriteLine(DateTime.Now.ToString() + " Threads have joined");
//....Snippet of saving the gathered data from the threads to the database
}
}
Solution:
I added a new class. Then looped through my LINQ-to-SQL results to create a list of that new class. Then I base my thread firing from that list instead of the LINQ-to-SQL generated one. All is well. Can anyone explain this?
public class TempSession
{
public TempSession(LINQ.session sess)
{
this.computername = sess.computername;
this.timestamp = sess.start_time;
this.uid = sess.uid;
}
public string computername;
public DateTime timestamp;
public Guid uid;
}
public void HuntLocks()
{
//select EventCode,TimeGenerated,Message from Win32_NTLogEvent WHERE logfile='Security' and (EventCode='4800' or EventCode='4801') and TimeGenerated > '20120327 08:08:08'
// 4800 = locked
// 4801 = unlocked
LINQ.Login_ActionsDataContext db = new LINQ.Login_ActionsDataContext();
List<LINQ.session> currentSessions = (from q in db.sessions
where q.end_time == null
orderby q.computername ascending
select q).ToList();
// START Solution Changes
List<TempSession> newCurrentSessions = new List<TempSession>();
foreach (LINQ.session session in currentSessions)
{
newCurrentSessions.Add(new TempSession(session));
}
Thread[] threadArray = new Thread[newCurrentSessions.Count];
// END solution changes
for (int i = 0; i < newCurrentSessions.Count; i++)
{
DateTime mostRecentTimestamp = (from q in db.actions
where q.session_id == newCurrentSessions[i].uid
orderby q.timestamp descending
select q.timestamp).FirstOrDefault();
ParameterizedThreadStart start = new ParameterizedThreadStart(get_lock_data);
threadArray[i] = new Thread(start);
threadArray[i].Start(new LockHunterArgs(newCurrentSessions[i].computername, mostRecentTimestamp, newCurrentSessions[i].uid));
}
for (int i = 0; i < threadArray.Length; i++)
{
threadArray[i].Join();
}
Console.WriteLine(DateTime.Now.ToString() + " Threads have joined");
db.actions.InsertAllOnSubmit(newActions);
Console.WriteLine(DateTime.Now.ToString() + " Found " + newActions.Count.ToString() + " locks");
db.SubmitChanges();
newActions = new List<LINQ.action>();
}
Use a temp variable to store the iterated value:
foreach (LINQ.session sesson in currentSessions)
{
var tempSession = session; // now use tempSession
....
This is a known side effect of closure of the iterated value.
I would say the problem is most likely in what you snipped out. I was unable to reproduce your issue with this faked data:
var guids = Enumerable.Range(1, 10)
.Select(i => Guid.NewGuid())
.ToArray();
var currentSessions = Enumerable.Range(1, 10)
.Select(i => new {computername = "pc" + i})
.Zip(guids,(a,g) => new {a.computername, uid = g});
var dbactions = Enumerable.Range(1, 10)
.Select(i => DateTime.Now.AddHours(-1*i))
.Zip(guids, (t,g) => new {session_id = g, timestamp = t});
Given this, can you provide a working example that isn't dependent on any of your local resources?
Im am working in a project that uses intensively List and i try to find the object via the name (that is a member of the object).
My code worked without searching it using a single for-next loop (function find1) but i found that it is possible to the same using the build-in found find, and the code works. However, it feel a bit slow. So, i did a project for test the speed:
I have the next code
public List<MyObject> varbig = new List<MyObject>();
public Dictionary<string,string> myDictionary=new Dictionary<string, string>();
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
myDictionary.Clear();
varbig.Clear();
for (int i = 0; i < 5000; i++) {
varbig.Add(new MyObject("name" + i.ToString(),"value"+i.ToString()));
myDictionary.Add("name" + i.ToString(), i.ToString());
}
// first test
var start1 = Environment.TickCount;
for (int i = 0; i < 3000; i++) {
var ss=find1("name499");
}
var end1 = Environment.TickCount;
Console.WriteLine("time 1 :" + (end1 - start1));
// second test
var start2 = Environment.TickCount;
for (int i = 0; i < 3000; i++) {
var ss=find2("name499");
}
var end2 = Environment.TickCount;
Console.WriteLine("time 2 :" + (end2 - start2));
// third test
var start3 = Environment.TickCount;
for (int i = 0; i < 3000; i++) {
var ss = find3("name499");
}
var end3 = Environment.TickCount;
Console.WriteLine("time 3 :" + (end3 - start3));
// first test b
var start1b = Environment.TickCount;
for (int i = 0; i < 3000; i++) {
var ss=find1("name4999");
}
var end1b = Environment.TickCount;
Console.WriteLine("timeb 1 :" + (end1b - start1b));
// second test
var start2b = Environment.TickCount;
for (int i = 0; i < 3000; i++) {
var ss=find2("name4999");
}
var end2b = Environment.TickCount;
Console.WriteLine("timeb 2 :" + (end2b - start2b));
// third test
var start3b = Environment.TickCount;
for (int i = 0; i < 3000; i++) {
var ss = find3("name4999");
}
var end3b = Environment.TickCount;
Console.WriteLine("timeb 3 :" + (end3b - start3b));
}
public int find1(string name) {
for (int i = 0; i < varbig.Count; i++) {
if(varbig[i].Name == name) {
return i;
}
}
return -1;
}
public int find2(string name) {
int idx = varbig.FindIndex(tmpvar => Name == name);
return idx;
}
public int find3(string name) {
var ss=myDictionary[name];
return int.Parse(ss);
}
}
And i use the next object
public class MyObject {
private string _name = "";
private string _value = "";
public MyObject() {}
public MyObject(string name, string value) {
_name = name;
_value = value;
}
public string Name {
get { return _name; }
set { _name = value; }
}
public string Value {
get { return _value; }
set { _value = value; }
}
}
Mostly it do the next thing:
I create an array with 5000 elements.
time 1 = search the 499th object (index) using a simple for-next.
time 2 = search the 499th using the build in function find of List
time 3 = it do the search of the 499th element using dictionary.
Timeb 1, timeb 2 and timeb 3 do the same but try to search the 4999th element instead of the 499th element.
I ran a couple of times :
time 1 :141
time 2 :1248
time 3 :0
timeb 1 :811
timeb 2 :1170
timeb 3 :0
time 1 :109
time 2 :1170
time 3 :0
timeb 1 :796
timeb 2 :1170
timeb 3 :0
(the small then the fast)
And, for my surprise, the build in function findindex is absurdly slow (in some cases, close to 10x slower. Also, the dictionary approach is almost instantly.
My question is, why?. is it because the predicate?.
The problem is in this line:
int idx = varbig.FindIndex(tmpvar => Name == name);
Name == name is wrong, you should write tmpvar.Name == name instead.
In your code you're comparing name argument with the Name property of your form; they are obviously different, and so the method always examines the whole list instead of stopping when the searched value is found. In fact, as you can see looking the numbers, the time spent by find2() is basically always equal.
About the dictionary, it's obviously faster than the other methods because dictionaries are memory structure specifically built to provide fast keyed access.
In fact they arrive close to O(1) time complexity, while looping a list you have a time complexity equal to O(n).
Find1 is using a simple for( i = 0 to count) method
Find2 uses the built in Find method (which is exactly find1 above), except that you have passed a predicate along with it, which I believe is slowing it down.
Find3 using a dictionary, I would assume is the fastest without any timers, becuase a dictionary uses hashtables under the covers which has an 0(1) look up (contant time)
There is the error in your code - the find2 method uses the Form.Name for the comparison instead of your collection objects names. It should looks like this:
public int find2(string name) {
return varbig.FindIndex((obj) => obj.Name == name);
}
The results without using the Form.Name are more consistent:
time 1 :54
time 2 :50
time 3 :0
timeb 1 :438
timeb 2 :506
timeb 3 :0
You don't need to put for loop to search in find2...
Just call find2 directly, then result will be 0.