I would have assumed that access() was just a wrapper around stat(), but I've been googling around and have found some anecdotes about replacing stat calls with 'cheaper' access calls. Assuming you are only interested in checking if a file exists, is access faster? Does it completely vary by filesystem?
-
it seems to be rather a wrapper around open(). it does make sense to me that it would be faster and it's nice that it's setuid aware. I'd love to see some benchmarks though. – Matt Joyce Sep 23 '15 at 20:37
-
`access` is in man section 2, so I'm assuming it's its own system call instead of being a libc wrapper around something (so, in theory, it could be faster than stat, etc) – Colonel Thirty Two Sep 23 '15 at 21:34
-
2The important thing isn't performance, but semantics. `access()` tells you you can read/write/execute a thing, even after any access-control plugins (ie. SELinux) or other components (extended ACLs; AFS permissions; etc) have been involved. `stat()` tells you the traditional-UNIX file permissions, but they may or may not be the whole story. And because `access()` is [potentially] doing more work, I'd actually expect it to be slower. – Charles Duffy Sep 23 '15 at 22:41
-
1I suppose it is very dependant. Related example: on my PC `fstat()` is 2x faster than `pread()`. However, on one low-RAM box with MIPS cpu, `fstat()` is 1.5x *slower* than `pread()`. So the message of this comment is that you should always *measure* to get concrete times for your environment. – gavv Sep 24 '15 at 17:14
1 Answers
Theory
I doubt that.
In lower layers of kernel there is no much difference between access()
and stat()
calls both are performing lookup operation: they map file name to an entry in dentry cache and to inode (it is actual kernel structure, inode
). Lookup is slow operation because you need to perform it for each part of path, i.e. for /usr/bin/cat
you will need to lookup usr
, bin
and then cat
and it can require reading from disk -- that is why inodes and dentries are cached in memory.
Major difference between that calls is that stat()
performs conversion of inode
structure to stat
structure, while access()
will do a simple check, but that time is small comparing with lookup time.
The real performance gain can be achieved with at-operations like faccessat()
and fstatat()
, which allow to open()
directory once, just compare:
struct stat s;
stat("/usr/bin/cat", &s); // lookups usr, bin and cat = 3
stat("/usr/bin/less", &s); // lookups usr, bin and less = 3
int fd = open("/usr/bin"); // lookups usr, bin = 2
fstatat(fd, "cat", &s); // lookups cat = 1
fstatat(fd, "less", &s); // lookups less = 1
Experiments
I wrote a small python script which calls stat()
and access()
:
import os, time, random
files = ['gzexe', 'catchsegv', 'gtroff', 'gencat', 'neqn', 'gzip',
'getent', 'sdiff', 'zcat', 'iconv', 'not_exists', 'ldd',
'unxz', 'zcmp', 'locale', 'xz', 'zdiff', 'localedef', 'xzcat']
access = lambda fn: os.access(fn, os.R_OK)
for i in xrange(1, 80000):
try:
random.choice((access, os.stat))("/usr/bin/" + random.choice(files))
except:
continue
I traced system with SystemTap to measure time spent in different operations. Both stat()
and access()
system calls use user_path_at_empty()
kernel function which represents lookup operation:
stap -ve ' global tm, times, path;
probe lookup = kernel.function("user_path_at_empty")
{ name = "lookup"; pathname = user_string_quoted($name); }
probe lookup.return = kernel.function("user_path_at_empty").return
{ name = "lookup"; }
probe stat = syscall.stat
{ pathname = filename; }
probe stat, syscall.access, lookup
{ if(pid() == target() && isinstr(pathname, "/usr/bin")) {
tm[name] = local_clock_ns(); } }
probe syscall.stat.return, syscall.access.return, lookup.return
{ if(pid() == target() && tm[name]) {
times[name] <<< local_clock_ns() - tm[name];
delete tm[name];
} }
' -c 'python stat-access.py'
Here are the results:
COUNT AVG
lookup 80018 1.67 us
stat 40106 3.92 us
access 39903 4.27 us
Note that I disabled SELinux in my experiments, as it adds significant influence on the results.

- 11,174
- 2
- 30
- 62
-
6You're measuring the cached case. It's possible that `stat` has to check some things that `access` doesn't, like the block count. Most filesystems probably store a block count in the inode, rather than having to walk the blockmap B-tree, because stat-every-file in a dir is a very common operation that they need to optimize for. Given that, everything needed to answer a `stat` query probably comes from one disk seek to read the inode. – Peter Cordes Sep 23 '15 at 23:26
-
Please do not blindly trust this advice. I tested it in my machine and the performance of stat and faccessat seems completely equivalent. – Siscia Dec 12 '19 at 17:25
-
2@Siscia how your did you perform your experiment? Without more information, it is hard to tell, what factors come to play. – myaut Dec 12 '19 at 20:13