While profiling my application I found that DateTime.FromFileTime(long fileTime) is slow.
Does anyone know of a fast managed equivalent or the windows file time format?
[EDIT]
I was able to get some speed gains doing the following:
var timeStr = FastTimeStringFormat(new DateTime(fileTime + 0x701ce1722770000L, DateTimeKind.Utc).ToLocalTime()); // + 0x701ce1722770000L is the offset needed to convert to UTC DateTime
For more speed but less safety (no intra-day daylight savings time checks), you can cache the ToLocalTime offset (long) and spare the more expensive expensive ToLocalTime() call.
On App Start:
long fileTimeOffset = DateTime.Today.Subtract(DateTime.Today.ToUniversalTime()).Ticks + 0x701ce1722770000L;
Then in your critical path:
var timeStr = FastTimeStringFormat(new DateTime(fileTime + fileTimeOffset));
As it turned out ToString is very expensive and the following is faster.
public static unsafe string FastTimeStringFormat(DateTime time)
{
// modified version from the following post:
// http://geekswithblogs.net/akraus1/archive/2006/04/23/76146.aspx
// this one is also more accurate because of true divide by 10, beware of less accurate versions that do not use division
char* FixedCharArray = stackalloc char[13];
int hour = time.Hour; // Cache property values
int minute = time.Minute;
int second = time.Second;
int ms = time.Millisecond;
// hour
FixedCharArray[0] = (Char)('0' + hour / 10);
FixedCharArray[1] = (Char)('0' + hour % 10);
FixedCharArray[2] = ':';
// minute
FixedCharArray[3] = (Char)('0' + minute / 10);
FixedCharArray[4] = (Char)('0' + minute % 10);
FixedCharArray[5] = ':';
// seconds
FixedCharArray[6] = (Char)('0' + second / 10);
FixedCharArray[7] = (Char)('0' + second % 10);
FixedCharArray[8] = '.';
// miliseconds
FixedCharArray[9] = (Char)('0' + ms / 100);
FixedCharArray[10] = (Char)('0' + ms % 100 / 10);
FixedCharArray[11] = (Char)('0' + ms % 10);
return new String(FixedCharArray);
}
DateTime.FromFileTime() is implemented in managed code, there are no P/Invoke calls involved.
The performance hit you're experiencing might come from the conversion to local time that is performed internally. Try using DateTime.FromFileTimeUtc() instead, if at all possible.
Related
I'm using a PIC 18F2550 programmed with CCS to do several things. The root of my problem is that one of my interruptions handles an equation that I need to process often, and that controls several variables. This equation process involves typecasting, it has int16's as input variables and it's result is an int8 variable. This process takes too long and it slows down my other interruptions, causing several problems.
How can I speed up this function as much as possible but without hindering it's accuracy as much?
void dynamicControl(){
refDelta = phRef - procSignal[0]; //all 3 are int16 variables.
int16 prOffset = procSignal[1] - 500; //Their values will change on every iteration
int16 _fixed(2) calcVar; //Tried using float and double here but with no success.
if (refDelta < 0) {
risePr = TRUE; dropPr = FALSE;
calcVar = ((int16 _fixed(2))prOffset / 65.00) * (int16 _fixed(2))(4 - refDelta) + 1;
nValPuls = (int8)calcVar;
} else if(refDelta > 0){
risePr = FALSE; dropPr = TRUE;
nValPuls = (refDelta - 1);
} else {
risePr = dropPr = FALSE;
nValPuls = 0x00;
}
if (refDelta < 10){
calcVar = ((int16 _fixed(2))prOffset / 100.00) * (int16 _fixed(2))(5 - refDelta) * 0.70 + 18.00;
pumpDutyLock = (int8)calcVar;
if(pumpDutyLock > 30) pumpDutyLock = 30;
} else { pumpDutyLock = 0x00; }
}
The 4 lines of code that slow down my code are the following. If I comment them all, the function runs at a good speed.
calcVar = ((int16 _fixed(2))prOffset / 65.00) * (int16 _fixed(2))(4 - refDelta) + 1;
nValPuls = (int8)calcVar;
calcVar = ((int16 _fixed(2))prOffset / 100.00) * (int16 _fixed(2))(5 - refDelta) * 0.70 + 18.00;
pumpDutyLock = (int8)calcVar;
I realize that typecasting takes a long time here, I've tried using float or double for calcVar, instead of int16 _fixed(2), with no progress.
EDIT: After some further research I've found that, as stated by the comments, most of these operations take at least 50 us to complete, which means my problem cannot be solved by modifying the equation. I've resorted to transfering most of the calculations over to the PC side of the system.
I have a MsSql database which calculates the timespan between two dates in seconds. That works fine. I use this column afterwards in C# and write them in an array.
This array is the input for a chart later on.
So far this works well, but I cannot find a way to display the seconds in a format like hhh:mm:ss as the timespan can be greater than 24h.
I tried ChartArea.AxisY.LabelStyle.Format = "hhmmss"; but it does not work at all.
Does anybody has an idea how I could do that?
EDIT:
I add the data this way:
chart2.Series.Clear();
chart2.ChartAreas.Clear();
Series BoxPlotSeries = new Series();
ChartArea ChartArea2 = new ChartArea();
ChartArea ChartArea3 = new ChartArea();
chart2.ChartAreas.Add(ChartArea2);
chart2.ChartAreas.Add(ChartArea3);
ChartArea2.Name = "Data Chart Area";
ChartArea3.Name = "BoxPlotArea";
BoxPlotSeries.Name = "BoxPlotSeries";
BoxPlotSeries.ChartType = SeriesChartType.BoxPlot;
BoxPlotSeries.ChartArea = "BoxPlotArea";
chart2.Series.Add(BoxPlotSeries);
Series Input1 = new Series();
Input1.Name = "Input1";
Input1.ChartType = SeriesChartType.Point;
Input1.ChartArea = "Data Chart Area";
chart2.Series.Add(Input1);
chart2.Series["Input1"].Points.DataBindY(InputArray);
chart2.ChartAreas["BoxPlotArea"].AxisX.CustomLabels.Add(2, 0.0, "BoxPlot1");
chart2.Series["BoxPlotSeries"]["BoxPlotSeries"] = "Input1";
chart2.Series["BoxPlotSeries"]["BoxPlotShowMedian"] = "true";
chart2.Series["BoxPlotSeries"]["BoxPlotShowUnusualValues"] = "false";
chart2.Series["BoxPlotSeries"]["PointWidth"] = "0.5";
chart2.Series["BoxPlotSeries"].IsValueShownAsLabel = false;
ChartArea2.Visible = false;
ChartArea3.BackColor = Color.FromArgb(224,224,224);
//I tried to format it this way but it didn't work
//ChartArea3.AxisY.LabelStyle.Format = "{0:HHHmmss}";
chart2.ChartAreas["BoxPlotArea"].AxisX.LabelStyle.Angle = -90;
EDIT2:
And here's how I populate the input array
int[] InputArray = new int[1000000];
int c = 0;
con.Open();
dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
int n;
if (int.TryParse(dr[0].ToString(),out n) == true)
{
InputArray[c] = Convert.ToInt32(dr[0].ToString());
c++;
}
}
}
if (c == 0) { c = 1; }
Array.Resize(ref InputArray, c - 1);
EDIT 3:
The Boxplot should look like this in the end:
In Excel the format to display hours greater than 24 is called "[h]:mm:ss;#"
EDIT4:
Thanks to #TAW I nearly managed to solve my problem. I made some adjustments to his solution and came up with this:
In the chart code block:
The Value "max" is set before.
ChartArea3.AxisY.MajorTickMark.Interval = addCustomLabels(ChartArea3, BoxPlotSeries, 60 * 60, max);
int addCustomLabels(ChartArea ca, Series series, int interval, int max)
{
int tickNo = 0;
ca.AxisY.CustomLabels.Clear();
if(max / interval > 10)
{
interval = (max / 10) - (max / 10) % (60*30);
tickNo = (max / 10) - (max / 10) % (60*30);
}
if (max / interval <= 2 )
{
interval = (max / 4) - (max / 4) % (60 * 15);
tickNo = (max / 4) - (max / 4) % (60 * 15);
}
for (int i = 0; i < max; i += interval)
{
CustomLabel cl = new CustomLabel();
cl.FromPosition = i - interval / 2;
cl.ToPosition = i + interval / 2;
cl.Text = hhh_mm_ss(i);
ca.AxisY.CustomLabels.Add(cl);
}
return tickNo;
}
My problem is now, that sometimes no axis lable (apart from 0:00) is shown even when the code runs through it without any problems.
Has anybody and idea what could be wrong?
Your task involves two parts:
displaying seconds in the hhh:mm:ss format
putting them as labels on the y-axis
There is no suitable date-time formatting string for this in c#, so we can't make use of the built-in automatic labels and their formatting.
There also no way to use expressions that call a function on the automatic labels, unfortunately.
So we can't use those.
Instead we will have to add CustomLabels. This is not very hard but does take a few steps..
But let's start with a function that converts an int to the hhh:mm:ss string we want; this should do the job:
string hhh_mm_ss(int seconds)
{
int sec = seconds % 60;
int min = ((seconds - sec)/60) % 60;
int hhh = (seconds - sec - 60 * min) / 3600;
return hhh > 0 ? string.Format("{2}:{1:00}:{0:00}", sec, min, hhh)
: min + ":" + sec.ToString("00");
}
Maybe it can be optimized, but for our purpose it'll do.
Next we need to create the CustomLabels. They will replace the normal axis labels and we need to add them in a separate loop over the data after each binding.
One special thing about them is their positioning. Which is smack between two values we need to give them: the FromPosition and ToPosition, both in the unit of the axis-values.
Another difference to normal, automatic Labels is that it is up to us to create as many or few of them as we need..
This function tries to create a number that will go up to the maximum y-value and space the CustomLabels at a given interval:
void addCustomLabels(ChartArea ca, Series series, int interval)
{
// we get the maximum form the 1st y-value
int max = (int)series.Points.Select(x => x.YValues[0]).Max();
// we delete any CLs we have
ca.AxisY.CustomLabels.Clear();
// now we add new custom labels
for (int i = 0; i < max; i += interval)
{
CustomLabel cl = new CustomLabel();
cl.FromPosition = i - interval / 2;
cl.ToPosition = i + interval / 2;
cl.Text = hhh_mm_ss(i);
ca.AxisY.CustomLabels.Add(cl);
}
}
The first parameters to call this are obvious; the last one however is tricky:
You need to decide to interval you want your labels to have. It will depend on various details of your chart:
the range of values
the size of the chart area
the size of the font of the axis
I didn't set any special Font in the function; CustomLabels use the same Font as normal axis labels, i.e. AxisY.LabelStyle.Font.
For my screenshot I prepared the chart like this:
ca.AxisX.Minimum = 0;
ca.AxisY.MajorTickMark.Interval = 60 * 60; // one tick per hour
addCustomLabels(ca, s, 60 * 30); // one label every 30 minutes
I have also added DataPoint Labels for testing to show the values..:
series.Points[p].Label = hhh_mm_ss((int)y) + "\n" + y;
Here is the result:
UPDATE: This answer may be quite useful for other readers, but it pretty much misses the OP's issues. I'll leave it as it stands, but it will not help in creating specially formatted y-axis labels..
Most Chart problems stem from invalid or useless x-values. The following discussion tries to help avoiding or getting around them..
A number is a number and you can't simply display it as a DateTime, or for that matter a TimeSpan.
So you need to add the X-Values as either DateTime or as double that contain values that can be converted to DateTime. The fomer is what I prefer..
So instead of adding the seconds directly add them as offsets from a given DateTime:
Change something like this
series.Points.AddXY(sec, yValues);
To this:
var dt = new DateTime(0).AddSeconds(sec);
series.Points.AddXY(dt, yValues);
Now you can use the date and time formatting strings as needed..:
chartArea.AxisX.LabelStyle.Format = "{mm:ss}";
You could also add them as doubles that actually are calculated from DateTimes via the ToOADate:
series.Points.AddXY(dt.ToOADate(), yValues);
But now you will have to set the ChartValueType.DateTime and probably also AxisX.IntervalType and AxisX.Interval to make sure the chart gets the formatting right..:
s.XValueType = ChartValueType.DateTime;
ca.AxisX.Interval = 5;
ca.AxisX.IntervalType = DateTimeIntervalType.Seconds;
ca.AxisX.LabelStyle.Format = "{mm:ss}";
Pick values that suit your data!
Note that the problem with your original code is that the X-Values internally always are doubles, but the seconds are not integer values in them but fractional parts; so you need some kind of calculation. That's what ToOADate does. Here is a short test that shows what one second actually does amount to as a OADate double :
Best add the X-Values as DateTimes so all further processing can rely on the type..
Update I just saw that you have finally added the real code to your question and that is uses Points.DataBindY. This will not create meaningful X-Values, I'm afraid. Try to switch to Points.DataBindXY! Of course the X-Values you bind to also need to follow the rules I have explained above..!
You can do a loop over your array and convert the numbers like I shown above; here is a simple example:
int[] seconds = new int[5] { 1, 3, 88, 123, 3333 };
double[] oaSeconds = seconds.Select(x => new DateTime(0).AddSeconds(x).ToOADate())
.ToArray();
If you are trying to show more than 2 digits of hour I think this should work for you
//yourTimeSpan is the TimeSpan that you already have
var hoursDouble = Math.Floor(yourTimeSpan.TotalHours);
string hours;
string minutes;
string seconds;
//check hours
if(hoursDouble < 10)
{
hours = string.Format("0{0}", hoursDouble);
}
else
{
hours = hoursDouble.ToString();
}
//check minutes
if (yourTimeSpan.Minutes < 10)
{
minutes = string.Format("0{0}", yourTimeSpan.Minutes);
}
else
{
minutes = yourTimeSpan.Minutes.ToString();
}
//check seconds
if (yourTimeSpan.Seconds < 10)
{
seconds = string.Format("0{0}", yourTimeSpan.Seconds);
}
else
{
seconds = yourTimeSpan.Seconds.ToString();
}
string formattedSpan = String.Format("{0}:{1}:{2}", hours, minutes, seconds);
Update: I think this should solve the problem you were seeing with single digit numbers
Scenario:
I have a iPad web app which connects to my Exchange server to display the visibility of meeting rooms in Outlook.
The clock must be very accurate (within 5 seconds of the Exchange Server time)
My problem is:
The iPad syncs its time with apples timeserver and there is no way of changing this without jailbreaking the iPad
The iPad clock is not in sync with our server
My current solution:
I have a Clock.aspx page which returns the current time and displays it in the correct div:
var getTime = function () {
$.get('Clock.aspx', function (data) {
$('#txt').html('<h1>' + data + '</h1>');
});
}
However:
This is very inefficient as the call to the server is every 10 seconds (this causes the iPad to crash once every 3/4 days)
What I would like to do:
Is pull the time from Clock.aspx once a hour and increment it using javascript for the rest of the time which will reduce the calls to the server significantly.
Please advise me on the best and most efficient way to do this.
If you have any other suggestions on ways to improve the efficiency I would also love to hear them.
Okay, this isn't perfect but meets my requirements for now - I'd once again appreciate it if more efficient and accurate solutions come up!
var hour = parseInt("<%=DateTime.Now.Hour %>", 10);
var minute = parseInt("<%=DateTime.Now.Minute %>", 10);
var second = parseInt("<%=DateTime.Now.Second %>", 10);
// var x = hour.tostring() +":"+ minute.tostring() +":"+ second.tostring();
// alert(x);
function showTime() {
second++;
if (second > 59) {
second = 0;
minute++;
}
if (minute > 59) {
minute = 0;
hour++;
}
minute = checkTime(minute);
second = checkTime(second);
var clock = hour.toString() + ":" + minute.toString() + ":" + second.toString();
document.getElementById("txt").innerHTML = "<h1>" + clock + "</h1>";
function checkTime(i) {
return (i < 10 ? '0' : '') + i;
}
}
setInterval('showTime()', 1000);
Having used my original answer for a few days I noticed that the sync was out, this script sync's with the server time every hour to re-sync the time accordingly, this means that the accuracy of the clock is very very close with the server. The previous script could lose upto 5 minutes per day. I hope this helps someone!
var serverDate;
function getServerDate() {
serverDate = new Date("<%=DateTime.Now %>");
}
function tick() {
serverDate.setSeconds(serverDate.getSeconds() + 1);
var min = serverDate.getMinutes();
var hour = serverDate.getHours();
var sec = serverDate.getSeconds();
if (min == 30) {
$.get('Clock.aspx?type=2', function (data) {
serverDate = new Date(data.split(">")[1].split("<")[0]);
min = serverDate.getMinutes();
hour = serverDate.getHours();
sec = serverDate.getSeconds();
});
}
if (hour < 10) hour = "0" + hour;
if (min < 10) min = "0" + min;
if (sec < 10) sec = "0" + sec;
document.getElementById("txt").innerHTML = "<h1>" + hour + ":" + min + ":" + sec; + "</h1>";
}
window.onload = function () {
getServerDate();
setInterval("tick()", 1000);
}
Edited to add that clock.aspx is a page which outputs DateTime.Now.ToString()
I would like to output the time remaining for a process on the following format:
d hh:mm:ss
When the process start, it sets a DateTime startTimevariable with the current time.
From there I have timer on my GUI that updates a label every second and here is what I have to calculated the remaining time:
TimeSpan timeRemaining = TimeSpan.FromTicks(DateTime.Now.Subtract(startTime).Ticks * (iProgress.Maximum - iProgress.Value) / iProgress.Value);
And using on the label as:
labelTimeRemaining.Text = timeRemaining.ToString(#"d\ hh\:mm\:ss");
However the time variation is displayed is going on very oddly jumping several minutes with getting into a steady mode.
I know it should jump on the begin but it should stabilize at some point right ?
I process about 1~3 records per second however the time remaining is jumping 10~20 minutes every update.
What am I doing wrong or how could I fix this issue ?
Let me know if u need more info or code.
UPDATE
Below is portion of the code I am using:
private Stopwatch _timer = new Stopwatch();
private int _total = 0;
private int _current = 0;
Start a thread or task with:
private void MyTask()
{
_timer.Reset();
_timer.Start();
for (int i = 0; i < _total; i++)
{
_current++;
Thread.Sleep(500);
}
_timer.Stop();
}
Create a winforms Timer with the below function to run every second:
if (_timer.IsRunning && _current > 0)
{
float elapsedMin = ((float)_timer.ElapsedMilliseconds / 1000) / 60;
float minLeft = (elapsedMin / _current) * (_total - _current);
TimeSpan eta = TimeSpan.FromMinutes(minLeft);
iStatus.Text = eta.ToString(#"d\ hh\:mm\:ss");
}
Based on the comments you provided, when you are working with 10K to 10M records, that a fluctuation is rather big as you could see. Here is the test example that will show you the fluctuation.
DateTime dt = DateTime.Now;
Thread.Sleep(3000);
TimeSpan ts = TimeSpan.FromTicks(DateTime.Now.Subtract(dt).Ticks * (100000 - 7) / 7);
Debug.WriteLine(ts.ToString());
Thread.Sleep(1000);
ts = TimeSpan.FromTicks(DateTime.Now.Subtract(dt).Ticks * (100000 - 9) / 9);
Debug.WriteLine(ts.ToString());
Also, even if you take care of integer division
DateTime dt = DateTime.Now;
Thread.Sleep(3000);
TimeSpan ts = TimeSpan.FromTicks((long)((double)DateTime.Now.Subtract(dt).Ticks * (100000.0 - 7.0) / 7.0));
Debug.WriteLine(ts.ToString());
Thread.Sleep(1000);
ts = TimeSpan.FromTicks((long)((double)DateTime.Now.Subtract(dt).Ticks * (100000.0 - 9.0) / 9.0));
Debug.WriteLine(ts.ToString());
But if you could normalize your records number to percents. Let's say you have 100K records, 1K is one percent and that is done in approximately 1000/2/60 around 8 minutes. After 32 minutes you will have 4% done. After another you will have 6% done and after another 24 minutes you will have around 9% done but lets say 10%. After more 30 minutes you should have 14% done. And now, if you update your textBox rarely the users will have the illusion that you really know how long it will take, and you will end up changing time for hour or two, but do it on every minute or on every percent changed (have you ever copied 30GB on Windows?)
DateTime dt1 = DateTime.Now;
DateTime dt2 = DateTime.Now;
dt2 = dt2.AddMinutes(32);
ts = TimeSpan.FromTicks(dt2.Subtract(dt1).Ticks * (100 - 4) / 4);
Debug.WriteLine(ts.ToString());
dt2 = dt2.AddMinutes(16);
ts = TimeSpan.FromTicks(dt2.Subtract(dt1).Ticks * (100 - 6) / 6);
Debug.WriteLine(ts.ToString());
dt2 = dt2.AddMinutes(24);
ts = TimeSpan.FromTicks(dt2.Subtract(dt1).Ticks * (100 - 10) / 10);
Debug.WriteLine(ts.ToString());
dt2 = dt2.AddMinutes(30);
ts = TimeSpan.FromTicks(dt2.Subtract(dt1).Ticks * (100 - 14) / 14);
Debug.WriteLine(ts.ToString());
how to convert seconds in Minute:Second format
A versatile version is to use TimeSpan like this:
var span = new TimeSpan(0, 0, seconds); //Or TimeSpan.FromSeconds(seconds); (see Jakob C´s answer)
var yourStr = string.Format("{0}:{1:00}",
(int)span.TotalMinutes,
span.Seconds);
int totalSeconds = 222;
int seconds = totalSeconds % 60;
int minutes = totalSeconds / 60;
string time = minutes + ":" + seconds;
Just for completeness I will add an answer using TimeSpan (works as of .NET 4.0):
int seconds = 1045;
var timespan = TimeSpan.FromSeconds(seconds);
Console.WriteLine(timespan.ToString(#"mm\:ss"));
Something like this:
string minSec = string.Format("{0}:{1:00}", seconds / 60, seconds % 60);
Note that that will ensure the seconds are always displayed as two digits, e.g. "2:05" for 125 seconds. The minutes aren't currently treated the same way, but of course they could be.
This doesn't deal well with negative numbers. If your seconds may be negative, you may want something like this:
string minSec = string.Format("{0}:{1:00}", seconds / 60,
(Math.Abs(seconds)) % 60);
Finally, will you always have less than an hour of seconds? It might look odd to see "80:00" when you really mean "1:20:00" for example.
double seconds=125;
TimeSpan.FromSeconds(seconds).ToString()
will give you : 00:02:05. As per my understanding this built-in solution is more extensible, since it can give you hours too, without any plumbing of the logic.
var seconds = 60;
//10,000 ticks in a millisecond
var ticks = seconds*10000*1000;
DateTime dt = new DateTime(ticks);
dt.ToString("mm:ss");
Simple maths. 60 seconds to a minute.
int mins = totalseconds/60;
int secs = totalseconds % 60;
Console.Writeline(string.Format("{0}:{1}", mins, secs));
Not strictly answering the original question, but in case anyone (or myself) in the future comes here from google wanting to format a float with milliseconds as well:
float secs = 23.69;
string m_ss_mmm = string.Format("{0}:{1:00}.{2:000}", (int)secs / 60, (int)(secs % 60), (secs - ((int)secs)) * 1000);
Result:
0:23.690
An alternative way to display the seconds as e.g. "2:05" could be to use PadLeft.
string time = minutes.ToString() + ":" + seconds.ToString().PadLeft(2,'0');
What works for me...
public static string SecondsToMinutes(int seconds)
{
var ts = new TimeSpan(0, 0, seconds);
return new DateTime(ts.Ticks).ToString(seconds >= 3600 ? "hh:mm:ss" : "mm:ss");
}
If you need it as float or double:
float seconds = 339;
float totalTimeInMins = (float) (Math.Floor(seconds / 60) + (seconds % 60) / 100);