From: Karsten Blees <karsten.blees@gmail.com>
To: Git List <git@vger.kernel.org>
Subject: [PATCH/RFC] read-cache: fix file time comparisons with different precisions
Date: Mon, 28 Sep 2015 12:39:52 +0200 [thread overview]
Message-ID: <560918F8.1080905@gmail.com> (raw)
In-Reply-To: <5605D88A.20104@gmail.com>
Different git variants record file times in the index with different
precisions, according to their capabilities. E.g. git compiled with NO_NSEC
records seconds only, JGit records the mtime in milliseconds, but leaves
ctime blank (because ctime is unavailable in Java).
This causes performance issues in git compiled with USE_NSEC, because index
entries with such 'incomplete' timestamps are considered dirty, triggering
unnecessary content checks.
Add a file time comparison function that auto-detects the precision based
on the number of trailing 0 digits, and compares with the lower precision
of both values. This initial version supports the known precisions seconds
(git + NO_NSEC), milliseconds (JGit) and nanoseconds (git + USE_NSEC), but
can be easily extended to e.g. microseconds.
Use the new comparison function in both dirty and racy checks. As a side
effect, this fixes racy detection in USE_NSEC-enabled git with
core.checkStat=minimal, as the coreStat setting now affects racy checks as
well.
Finally, do not check ctime if ctime.sec is 0 (as recorded by JGit).
Signed-off-by: Karsten Blees <blees@dcon.de>
---
read-cache.c | 62 ++++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 41 insertions(+), 21 deletions(-)
This patch fixes problems 3 and 4, by trying to auto-detect the recorded file
time precision.
diff --git a/read-cache.c b/read-cache.c
index 87204a5..3a4e6cd 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -99,23 +99,50 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)
sd->sd_size = st->st_size;
}
+/*
+ * Compares two file times. Returns 0 if equal, <0 if t1 < t2, >0 if t1 > t2.
+ * Auto-detects precision based on trailing 0 digits. Compares seconds only if
+ * core.checkStat=minimal.
+ */
+static inline int cmp_filetime(uint32_t t1_sec, uint32_t t1_nsec,
+ uint32_t t2_sec, uint32_t t2_nsec) {
+#ifdef USE_NSEC
+ /*
+ * Compare seconds and return result if different, or checkStat=mimimal,
+ * or one of the time stamps has second precision only (nsec == 0).
+ */
+ int diff = t1_sec - t2_sec;
+ if (diff || !check_stat || !t1_nsec || !t2_nsec)
+ return diff;
+
+ /*
+ * Check if one of the time stamps has millisecond precision only (i.e.
+ * the trailing 6 digits are 0). First check the trailing 6 bits so that
+ * we only do (slower) modulo division if necessary.
+ */
+ if ((!(t1_nsec & 0x3f) && !(t1_nsec % 1000000)) ||
+ (!(t2_nsec & 0x3f) && !(t2_nsec % 1000000)))
+ /* Compare milliseconds. */
+ return (t1_nsec - t2_nsec) / 1000000;
+
+ /* Compare nanoseconds */
+ return t1_nsec - t2_nsec;
+#else
+ return t1_sec - t2_sec;
+#endif
+}
+
int match_stat_data(const struct stat_data *sd, struct stat *st)
{
int changed = 0;
- if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
- changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
- sd->sd_ctime.sec != (unsigned int)st->st_ctime)
- changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
- if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+ if (cmp_filetime(sd->sd_mtime.sec, sd->sd_mtime.nsec,
+ (unsigned) st->st_mtime, ST_MTIME_NSEC(*st)))
changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
- sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+ if (trust_ctime && check_stat && sd->sd_ctime.sec &&
+ cmp_filetime(sd->sd_ctime.sec, sd->sd_ctime.nsec,
+ (unsigned) st->st_ctime, ST_CTIME_NSEC(*st)))
changed |= CTIME_CHANGED;
-#endif
if (check_stat) {
if (sd->sd_uid != (unsigned int) st->st_uid ||
@@ -276,16 +303,9 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
static int is_racy_stat(const struct index_state *istate,
const struct stat_data *sd)
{
- return (istate->timestamp.sec &&
-#ifdef USE_NSEC
- /* nanosecond timestamped files can also be racy! */
- (istate->timestamp.sec < sd->sd_mtime.sec ||
- (istate->timestamp.sec == sd->sd_mtime.sec &&
- istate->timestamp.nsec <= sd->sd_mtime.nsec))
-#else
- istate->timestamp.sec <= sd->sd_mtime.sec
-#endif
- );
+ return istate->timestamp.sec &&
+ (cmp_filetime(istate->timestamp.sec, istate->timestamp.nsec,
+ sd->sd_mtime.sec, sd->sd_mtime.nsec) <= 0);
}
static int is_racy_timestamp(const struct index_state *istate,
--
2.1.0.msysgit.0
next prev parent reply other threads:[~2015-09-28 10:40 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-25 23:28 broken racy detection and performance issues with nanosecond file times Karsten Blees
2015-09-28 10:39 ` Karsten Blees [this message]
2015-09-28 12:52 ` [PATCH/RFC] read-cache: fix file time comparisons with different precisions Johannes Schindelin
2015-09-29 10:23 ` Karsten Blees
2015-09-29 13:42 ` Johannes Schindelin
2015-09-28 17:38 ` broken racy detection and performance issues with nanosecond file times Junio C Hamano
2015-09-29 11:28 ` Karsten Blees
2015-09-28 18:17 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=560918F8.1080905@gmail.com \
--to=karsten.blees@gmail.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.