Objects Passed to ParameterizedThreadStart being overwritten? - c#

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?

Related

The instance of entity type +cannot be tracked because another instance with the same key value

I have the following code
double csave = (double)FindAcc(memid).MovAmt;
double psave = (double)FindAcc(memid).VolSafe;
double psaveamt = (double)FindAcc(memid).PriSave;
Single compAmt = 0;
ToxAccInfo ta = new ToxAccInfo();
ta.MemId = (int?)memid;
ta.AccId = (long)FindAcc(memid).AccId;
The ta.AccID is the primary key for the table ToxAccInfo.
Issue is when I attempt to save changes without the ta.AccId = (long)Find(memid).Accid, it creates another line instead of updating the original. But, when I now add the line, it fails with the error The instance of entity type...cannot be tracked because another instance with the same key value.
I have spent the past 3 days trying to find a work around but still can't get it to work.
Thank you.
I am adding the whole procedure to create what is to be saved (ta) and the saving subroutine
public IEnumerable<ToxAccInfo> UpdateBoth(DateTime stdate, DateTime spdate, long memid, bool chkcomp, bool chkvol)
{
double csave = (double)FindAcc(memid).MovAmt;
double psave = (double)FindAcc(memid).VolSafe;
double psaveamt = (double)FindAcc(memid).PriSave;
Single compAmt = 0;
var ta = new ToxAccInfo();
ta.MemId = (int?)memid;
//ta.AccId = (long)FindAcc(memid).AccId;
for (DateTime curdate = stdate; curdate <= spdate; curdate = curdate.AddMonths(1))
{
if (GoAhead(memid, curdate))
{
ta.MovAmt = csave;
ta.VolSafe = psave;
if (chkvol == true)
{
ta.VolSafe = psave + psaveamt;
//ta.VolSafe += psaveamt;
}
if (curdate.Month > 1 && curdate.Year >= 2011)
{
compAmt = 1000;
}
else
{
compAmt = 200;
}
if (chkcomp == true)
{
ta.MovAmt = csave + compAmt; //do the check for year it changed to 1000
}
ta.Done = curdate;
ta.Udate = curdate;
ta.Amt = compAmt + psaveamt;
//check for interest for month 12 and year greater than 2007.
if (curdate.Month == 12 && curdate.Year >= 2017)
{
ta.VolSafe = doInterest((decimal)ta.VolSafe);
ta.MovAmt = doInterest((decimal)ta.MovAmt);
psave = (double)ta.VolSafe;
csave = (double)ta.MovAmt;
goto jumper;
}
//psave += psaveamt;
//csave += compAmt;
psave = (double)ta.VolSafe;
csave = (double)ta.MovAmt;
jumper:;
}
}
yield return UpdateMemAccount(ta);
}
private ToxAccInfo UpdateMemAccount(ToxAccInfo UpAm)
{
_db.ToxAccInfos.Update(UpAm);
_db.SaveChanges();
return null;
}
The error occurs after the yield calls private ToxAccInfo UpdateMemAccount(ToxAccInfo UpAm), at _db.ToxAccinfos.Update(UpAm);, I have tried using add instead of Update still the same issue.
I call Updateboth from the controller with the following code, the two methods are in an interface. The code below calls the interface.
case "Commit Update":
ViewData["taa"] = _ar.UpdateBoth(stdate, eddate, (int)_ar.FindAcc(long.Parse(HttpContext.Session.GetString("memberid"))).MemId, chkcomp, chkvol).ToList();
ViewBag.isSet = false;
break;
Here is what i assuming your code doing:
FindAcc(memid) will use the DbContext to select the entity base on the memid (this is your unique value), then extract the value out of it.
After that, you create another instance and assign the same key to that along with the modified values you want.
But unfortunately, the AccId is your PK, and ta.AccId = (long)Find(memid).Accid make it another new instance with the same Id that got tracked by the EF Core itself while it select the entity back by the FindAcc(memid).
What you might want to do:
If you want to update that entity:
var entityFromDb = FindAcc(memid);
entityFromDb.volSafe = SomeNewValueHere;
YourDbContext.SaveChange();
If you want to add another entity:
var entityFromDb = FindAcc(memid);
var ta = new ToxAccInfo();
ta.volSafe = SomeNewValueHere;
YourDbContext.YourToxAccInfoSet.Add(ta);
YourDbContext.SaveChange();
Furthur information can be found here

Thread-Safe Code with Parallel.ForEach

I rarely touch threading with my code, but was facing significant pressure to reduce runtime and so have attempted to utilize the parallel.ForEach loop.
The code executes calculations on bids (Items). The items have been loaded into a List<List<Item>> (the outer List being items of all the same ID and the inner list being the various items). I've included the class Item at the end.
List<List<Item>> queryItemsByAcceptID = phyBidList.GroupBy(bids => bids.acceptID)
.Select(group => group.ToList())
.ToList();
All bids have a property (settlement period) that separates them by time e.g. period 1 = 00:00-00:30, period 2 = 00:30-01:00 and so on.
A bid from period 2 doesn't need any information from a bid from period 1.
Hence, I separated the bids into their settlement periods, to run the calculations within a parallel.ForEach.
List<Item> phyBidList = new List<Item>();
var queryMassBySetPeriod = phyBidList.GroupBy(x => x.settlementPeriod)
.Select(group => group.ToList())
.ToList();
However, when I run the code I get inconsistent results both with the 'unParallel code' and with previous outputs when run multiple times. This leads me to think my code isn't 'thread safe', as the 'unParallel code' is both correct and consistent (but just way too slow).
Is this thread safe? And what should I do to generate consistent results?
I'm wondering if I should be locking the daylight out of everything...
acceptIdItem.FPN.Add(fpn);
acceptIdItem.qAboPosArea.Add(tempQABOposArea);
acceptIdItem.qAboNegArea.Add(tempQABOnegArea);
However, I'm not sure if locking is appropriate because the threads aren't (or at least shouldn't be) accessing the same variables... both because information from other blocks isn't required and bids only go through calculation once.
P.S. I've included code below, I've tried to remove what I don't think is necessary to make it shorter and easier to read.
Parallel.ForEach(queryMassBySetPeriod, block =>
{
Console.WriteLine("GroupBy UnitID");
var queryItemsByUnitID = block.GroupBy(bids => bids.unitID)
.Select(group => group.ToList())
.ToList();
Console.WriteLine("GroupBy AcceptID");
queryItemsByAcceptID = block.GroupBy(bids => bids.acceptID)
.Select(group => group.ToList())
.ToList();
Console.WriteLine("Beginning mass interpretation...");
foreach (var list in queryItemsByAcceptID)
{
int bY = 0;
foreach (var acceptIdItem in list)
{
DateTime fromTime = acceptIdItem.fromTime;
DateTime toTime = acceptIdItem.toTime;
TimeSpan duration = toTime - fromTime;
for (int i = 0; i < (duration.Minutes); i++) //qTime fix (duration.Minutes + 1)
{
var queryPNdata = (from item in PNList
where item.unitID == acceptIdItem.unitID && item.fromTime <= fromTime && item.toTime >= fromTime
select item).FirstOrDefault();
int time = (acceptIdItem.qTimes[i] - acceptIdItem.fromTime).Minutes;
float boa = MathHelper.calcBOA(acceptIdItem.fromLevel, acceptIdItem.toLevel, (duration).Minutes, time);
float fpn = MathHelper.calcFPN(queryPNdata.fromLevel, queryPNdata.toLevel, duration.Minutes, time);
acceptIdItem.qTimes.Add(fromTime + i * ((toTime - fromTime) / duration.Minutes));
acceptIdItem.boa.Add(boa);
acceptIdItem.FPN.Add(fpn);
string[] tempBOUR = new string[6]; string[] tempBOLR = new string[6];
float[] tempQABOneg = new float[6]; float[] tempQABOpos = new float[6];
for (int k = 1; k < 7; k++)
{
//calculate tempBOUR/ tempBOLR/ tempQABOpos/ tempQABOneg
}
acceptIdItem.BOUR.Add(tempBOUR);
acceptIdItem.BOLR.Add(tempBOLR);
acceptIdItem.qAboPos.Add(tempQABOpos);
acceptIdItem.qAboNeg.Add(tempQABOneg);
}
int aZ = 0; //declared outside the loop to access later
for (aZ = 0; aZ < (acceptIdItem.qAboNeg.Count() - 1); aZ++)
{
float[] tempQABOnegArea = new float[6]; float[] tempQABOposArea = new float[6];
for (int k = 1; k < 7; k++)
{
//calculate tempQABOnegArea/ tempQABOposArea
}
acceptIdItem.qAboPosArea.Add(tempQABOposArea);
acceptIdItem.qAboNegArea.Add(tempQABOnegArea);
}
bY++;
}
}
});
At the beginning information is assigned and the class is added to a list (phyBidList). Here is the class...
class Item
{
public String unitID, acceptID, prevAcceptID, type;
public DateTime fromTime, toTime, acceptTime;
public int settlementPeriod, duration;
public List<DateTime> qTimes = new List<DateTime>();
public List<string[]> BOLR = new List<string[]>();
public List<string[]> BOUR = new List<string[]>();
public List<float[]> qAboNeg = new List<float[]>();
public List<float[]> qAboPos = new List<float[]>();
public List<float> boa = new List<float>();
public List<float> prevBOA = new List<float>();
public List<float> FPN = new List<float>();
public List<float[]> qAboNegArea = new List<float[]>();
public List<float[]> qAboPosArea = new List<float[]>();
}
****Edit****
In response to #calum-mcveigh, I changed the lists to concurrentBags using the below snippet, and other relevant changes. However, it still produces inconsistent results. I've put the full code on here https://pastebin.com/EgnE2285
ConcurrentBag<ConcurrentBag<Item>> queryMass = new ConcurrentBag<ConcurrentBag<Item>>();
foreach (var items in queryMassBySetPeriod)
{
ConcurrentBag<Item> item = new ConcurrentBag<Item>();
foreach (var bid in items)
{
item.Add(bid);
}
queryMass.Add(item);
}
To ensure thread safety you could use a concurrent collection such as ConcurrentBag<T> rather than a List<T>
You can read more about threadsafe collections here
Variable queryItemsByAcceptID is declared outside Parallel.ForEach but is set and used inside it. Is stopped looking there, but maybe there are other variables with the same problem.

C# Timer-Filter for Command

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.

Multi.Threading windows form seperate memory for i variable

I am new to c# programming. The variables in the calling functions gets changed when I call the function from multiple threads. Ie, when we use a single thread, the program correctly goes from 1 to 100 but when I run more than 100 threads, the program escapes some of the i variable values. I want to have separate memory for each threads calling threadproc() so that all of the value of variable i will be incremented independently on all the threads.
private void button1_Click(object sender, EventArgs e)
{
for (int threadloop = 1; threadloop <= Convert.ToInt32(threadcnttxt.Text); threadloop++)
{
//Thread th;
Thread th = new Thread(threadproc);
//th.IsBackground = true;
th.Start();
}
// th.Join();
MessageBox.Show("End of Code reached.");
}
public void threadproc()
{
for (int iterateloop = 0; iterateloop < 10; iterateloop++)
{
//if i run this function for 4 threads it should store 40000 in the dummy table but the application just stores less records would be due to locks..
var requestresult = TService(strarr[iterateloop].ToString()); //Here i pass ith element of array to TService function.
sql = "insert into dbo.dummy values(" + Thread.CurrentThread.ManagedThreadId.ToString() + "," + requestresult + ")";
command = new SqlCommand(sql, connection);
command.ExecuteNonQueryAsync();//command.ExecuteReaderAsync();
}
}
public requestResult TService(string enquiryline)
{
string requestResult = "";
int X = 0; //what to do with these local variable????? should i lock these also?
X = 1st element of enquiryline; // Eg of enquiryline 1,client name,xxx,other details comma delimited string
requestResult = sendrequest(enquiryline);
return requestResult;
}

Monitoring the FPS of a Direct X Application

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.

Categories

Resources