-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src: round nsec instead of truncating stat times #12607
Conversation
Have you ran the existing |
a nodejs issue causes certain dates to be off by 1ms after calling utimes See: nodejs/node#12607
From a CI log:
|
Mmm needs to include math for linux, had only built on Windows. |
Linux benchmark results:
Windows benchmark results:
Pretty substantial hit. Especially for linux considering it can't even have sub-ms filetimes. |
@refack How does uv_uptime relate to this? |
🤦I was half asleep. |
To rectify my half-baked-sleepy-thought from before, let me suggest a different solution: lib/fs.js | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index c1d8db9f8..8759dcf83 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -157,10 +157,14 @@ function Stats(
this.ino = ino;
this.size = size;
this.blocks = blocks;
- this.atime = new Date(atim_msec);
- this.mtime = new Date(mtim_msec);
- this.ctime = new Date(ctim_msec);
- this.birthtime = new Date(birthtim_msec);
+ Object.defineProperties(fs, {
+ atime: {enumerable: true, get() {return new Date(Math.Round(atim_msec))}},
+ mtime: {enumerable: true, get() {return new Date(Math.Round(mtim_msec))}},
+ ctime: {enumerable: true, get() {return new Date(Math.Round(ctim_msec))}},
+ birthtime: {enumerable: true, get() {
+ return new Date(Math.Round(birthtim_msec))
+ }}
+ }
}
fs.Stats = Stats; |
Also there's the FYI on windows the values are only accurate to 0.1 millisecond
|
src/node_file.cc
Outdated
@@ -464,7 +465,7 @@ void FillStatsArray(double* fields, const uv_stat_t* s) { | |||
// Dates. | |||
#define X(idx, name) \ | |||
fields[idx] = (static_cast<double>(s->st_##name.tv_sec) * 1000) + \ | |||
(static_cast<double>(s->st_##name.tv_nsec / 1000000)); \ | |||
round(static_cast<double>(s->st_##name.tv_nsec) / 1000000); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a C++ maven but maybe
(static_cast<double>((s->st_##name.tv_nsec + 500000) / 1000000))
Would be faster
Also If you're already here could you #define
the magic numbers please?
@refack yeah truncating and adding 0.5ms is another option. felt a bit ugly to suggest but it would fix the issue with likely lower impact on performance. rounding seems like the more correct approach, but considering the slowdown it is worth considering trunc+add. i'm not too sure that using lazy properties on the stat object would be good.. i did consider it, but it's a bit unexpected that reading time values after stat finishes involves a function call. might not really matter, though. |
AFAIK the only issue with adding 0.5 and dividing would be if the value was negative, but since that should/will never be the case here, it may be safe to do. It would be interesting to see the performance difference anyway compared to just |
In hot spots we generally prefer performance over "the most correct solution" |
|
There is/was previous discussion about these kinds of changes to |
@mscdex couldn't find anything recent about the times... Do you remember where? |
@mscdex thanks! |
I'll try to run some more benchmarks tomorrow. Been up for a few days now, getting tired.. If we're looking into the lazy prop approach i'd expect to have to define them once on the prototype rather than in the constructor, as defineProperty is plenty more expensive than instantiating a Date. To do that we'll need a way to get the msec times outside the constructor, and also the return values should likely be cached in get(). I can give it a look when i have a little time to see what effect it would have. |
Yes on all 👍 |
The define in the ctor allowed me to hide them in a closure, but you could store the raw numbers in _XXXX members and answer #8276 |
a nodejs issue causes certain dates to be off by 1ms after calling utimes See: nodejs/node#12607
This also reverts commit 9836cf5. Fixes: npm/npm#16734 Ref: nodejs#12607 Ref: nodejs#12818
* convert ’ to ' to turn md file to ASCII Fixes: nodejs#8276 Refs: nodejs#12607 Refs: nodejs#12818 Refs: nodejs#13256
PR-URL: nodejs#13173 Fixes: nodejs#8276 Refs: nodejs#12607 Refs: nodejs#12818 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Brian White <mscdex@mscdex.net>
Should this land on v6.x? |
Yes
|
This is not landing cleanly, would you be willing to do a backport with the commits you mention? |
ping |
a nodejs issue causes certain dates to be off by 1ms after calling utimes See: nodejs/node#12607
Truncation of submillisecond timestamp accuracy on windows creates situations where attempting to change access or modification times becomes off by one ms. Example:
This PR fixes the issue by rounding nsec rather than truncating in node_file.cc. A concern with that is that stat is a rather hot bit of code.. adding in 4 rounding operations could make a noticeable difference.
Another option could be to increment the return from toUnixTimestamp in fs.js enough to offset the precision loss, that would at least fix the issue if only node processes are updating the files.. but not particularly robust.
A third solution is to ignore the problem. It likely isn't something that affects that many users.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
src