Linux time Command: A Deep Dive from System Calls to Performance Analysis#

When optimizing code performance, the most common question is: why is this command slow? Is it CPU-bound, I/O-bound, or system call overhead? Linux time command gives you the answer. But most people only see those three lines of output—there’s much more hidden underneath.

Three Implementations: Which One Are You Using?#

Here’s a fact many don’t know: you likely have three different time commands on your system.

# 1. Bash built-in time
type time
# time is a shell keyword

# 2. External command /usr/bin/time
which time
# /usr/bin/time

# 3. Zsh's time (more powerful formatting)

Key difference: Bash built-in time is simple, showing only real/user/sys times. /usr/bin/time provides richer data: memory, I/O, context switches, and more.

# Bash built-in time
time sleep 1
# real  0m1.001s
# user  0m0.000s
# sys   0m0.000s

# External command /usr/bin/time
/usr/bin/time sleep 1
# 0.00user 0.00system 1:00.01elapsed 0%CPU (0avgtext+0avgdata 1960maxresident)k
# 0inputs+0outputs (0major+0minor)pagefaults 0swaps

To use the external command, either use the full path or escape with \time.

The Three Times: real/user/sys Explained#

The three times time outputs have fundamentally different meanings:

real  0m5.234s   # Wall clock time: actual elapsed time
user  0m3.120s   # User mode time: CPU executing user code
sys   0m0.890s   # Kernel mode time: CPU executing syscalls

real ≠ user + sys#

Many assume real = user + sys. This is wrong. The actual relationship:

real = user + sys + waiting_time + other_processes_time

Example with a network request:

time curl https://example.com
# real  0m2.345s   # Network latency
# user  0m0.150s   # CPU processing data
# sys   0m0.020s   # Syscall overhead

Network wait time counts toward real but doesn’t use CPU, so user + sys is much less than real.

CPU Usage Calculation#

CPU% = (user + sys) / real × 100%

If CPU% is close to 100%, it’s CPU-bound. If much lower, there’s significant waiting (I/O, network, locks).

Under the Hood: wait4() and rusage#

/usr/bin/time gets its data from the wait4() system call:

#include <sys/wait.h>
#include <sys/resource.h>

pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);

The rusage structure from <sys/resource.h>:

struct rusage {
    struct timeval ru_utime;  // User mode time
    struct timeval ru_stime;  // Kernel mode time
    long ru_maxrss;           // Maximum resident set size (KB)
    long ru_ixrss;            // Integral shared memory size
    long ru_idrss;            // Integral unshared data size
    long ru_isrss;            // Integral unshared stack size
    long ru_minflt;           // Minor page faults (no I/O)
    long ru_majflt;           // Major page faults (requires disk)
    long ru_nswap;            // Number of swaps
    long ru_inblock;          // Block input operations
    long ru_oublock;          // Block output operations
    long ru_msgsnd;           // Messages sent
    long ru_msgrcv;           // Messages received
    long ru_nsignals;         // Signals received
    long ru_nvcsw;            // Voluntary context switches
    long ru_nivcsw;           // Involuntary context switches
};

That’s why /usr/bin/time outputs so much information—the kernel collects it when the process exits.

Formatted Output: -v and -f Options#

Verbose Mode -v#

/usr/bin/time -v find / -name "*.conf"

Output includes:

  • Maximum resident set size: Peak memory usage
  • Major/minor page faults: Page fault statistics
  • Voluntary/involuntary context switches: Context switch counts
  • File system inputs/outputs: File I/O operations
  • Percent of CPU this job got: CPU utilization
  • Elapsed (wall clock) time: Wall clock duration

This data is invaluable for performance analysis.

Custom Format -f#

/usr/bin/time -f "CPU: %P\nMemory: %M KB\nTime: %e s" ./myapp

Common format specifiers:

Specifier Meaning
%E Wall clock time (mm:ss.ss)
%e Wall clock time (seconds)
%U User mode time
%S Kernel mode time
%P CPU percentage
%M Maximum resident set size (KB)
%t Average shared memory
%K Average total memory
%D Average unshared data size
%p Average unshared stack size
%F Major page faults
%R Minor page faults
%W Number of swaps
%I File system inputs
%O File system outputs
%c Involuntary context switches
%w Voluntary context switches

Performance Analysis in Practice#

Scenario 1: Identifying Bottlenecks#

/usr/bin/time -v tar czf archive.tar.gz /large/directory

If output shows:

  • Percent of CPU this job got: 95% → CPU bottleneck (compression is compute-intensive)
  • File system inputs/outputs: 50000 → I/O bottleneck (disk reads)
  • Major page faults: 1000 → Memory bottleneck (frequent page faults)

Scenario 2: Comparing Algorithms#

/usr/bin/time -f "%e %P" python slow_algorithm.py
/usr/bin/time -f "%e %P" python fast_algorithm.py

Scenario 3: Analyzing Syscall Overhead#

# A script that frequently stats files
/usr/bin/time -v ./check_files.sh

# High sys time indicates syscall overhead
# Optimization: batch operations, reduce stat calls

Scenario 4: Memory Leak Detection#

/usr/bin/time -v ./myapp

# Maximum resident set size: 1,048,576 kbytes
# Abnormally high peak memory may indicate a leak

Context Switches: Voluntary vs Involuntary#

/usr/bin/time -v ./myapp

# Voluntary context switches: 150
# Involuntary context switches: 5000
  • Voluntary: Program yields CPU voluntarily (sleep, I/O wait)
  • Involuntary: Time slice exhausted, forced switch

High involuntary switches indicate CPU contention—consider reducing concurrency or adjusting priority.

Page Fault Analysis#

/usr/bin/time -v ./myapp

# Major page faults: 10
# Minor page faults: 5000
  • Major page faults: Requires disk read, high latency
  • Minor page faults: Page found in memory, low latency

High major page faults → Insufficient memory or poor access pattern

Bash Built-in Time Advanced Usage#

Bash’s time supports TIMEFORMAT environment variable:

TIMEFORMAT='R: %R  U: %U  S: %S  CPU: %P'
time sleep 1
# R: 1.001  U: 0.000  S: 0.000  CPU: 0%

Format specifiers:

  • %R: Real time
  • %U: User time
  • %S: System time
  • %P: CPU percentage

You can also time entire code blocks:

time {
    step1
    step2
    step3
}

Node.js Implementation: Simulating time#

Web browsers can’t access rusage directly, but Node.js can:

const { spawn } = require('child_process');

function measureTime(command, args) {
  const start = process.hrtime.bigint();
  const startUsage = process.cpuUsage();

  const child = spawn(command, args);

  child.on('close', (code) => {
    const end = process.hrtime.bigint();
    const endUsage = process.cpuUsage(startUsage);

    const realNs = Number(end - start);
    const realSec = realNs / 1e9;
    const userSec = endUsage.user / 1e6;  // microseconds to seconds
    const sysSec = endUsage.system / 1e6;

    console.log(`real  ${realSec.toFixed(3)}s`);
    console.log(`user  ${userSec.toFixed(3)}s`);
    console.log(`sys   ${sysSec.toFixed(3)}s`);
    console.log(`CPU   ${((userSec + sysSec) / realSec * 100).toFixed(1)}%`);
  });
}

measureTime('sleep', ['1']);

Common Pitfalls#

1. time in Pipes#

# Wrong: only times grep
find / -name "*.conf" | time grep "pattern"

# Correct: times entire pipeline
time (find / -name "*.conf" | grep "pattern")

2. Command Not Found#

time: cannot run?: No such file or directory

Ensure command path is correct, or use absolute path.

3. Output Redirection#

# time output goes to stderr, won't be in file
/usr/bin/time ./myapp > output.txt 2>&1

# Redirect only time output
/usr/bin/time -o time.txt ./myapp
Tool Features Use Case
time Simple, overview stats Quick performance check
perf Powerful, hardware-level Deep performance optimization
strace Syscall tracing Analyze syscall overhead
valgrind Memory analysis Memory leak detection
gprof Function-level profiling Find hot functions

Summary#

Linux time command is more powerful than it seems. Remember:

  1. Distinguish three time implementations—external /usr/bin/time has the most features
  2. Understand real/user/sys meanings—they reveal CPU vs I/O relationships
  3. Use -v for complete resource stats, -f for custom format output
  4. Watch context switches and page faults—they signal performance issues

Next time you debug performance, don’t just run time cmd. Try /usr/bin/time -v cmd and discover insights you didn’t expect.


Related: Linux uptime Command | Linux top Command | Linux vmstat Command