Question
I want to measure how long parts of my C program take to run.
I first tried using time() like this:
printf("**MyProgram::before time= %ld\n", time(NULL));
doSomething();
doSomethingLong();
printf("**MyProgram::after time= %ld\n", time(NULL));
What I do not understand is why the before and after values are sometimes the same.
I know this is not full profiling. I just want to measure elapsed time for a piece of code.
I also tried this approach:
struct timeval diff, startTV, endTV;
gettimeofday(&startTV, NULL);
doSomething();
doSomethingLong();
gettimeofday(&endTV, NULL);
timersub(&endTV, &startTV, &diff);
printf("**time taken = %ld %ld\n", diff.tv_sec, diff.tv_usec);
How should I interpret output like these?
time taken = 0 26339time taken = 4 45025
Does 0 26339 mean 26,339 nanoseconds, or 26.339 milliseconds?
Does 4 45025 mean 4 seconds and 25 milliseconds, or something else?
Short Answer
By the end of this page, you will understand why time() often shows the same value before and after fast code, how gettimeofday() measures shorter durations, and how to correctly read tv_sec and tv_usec in C. You will also see safer modern alternatives and practical examples for timing code.
Concept
time() and gettimeofday() both deal with time, but they are used at different levels of precision.
time() returns the current calendar time in whole seconds since the Unix epoch. That means it has 1-second resolution. If your code finishes in less than one second, the value before and after may be identical.
For example, if both calls happen during the same second:
time_t a = time(NULL);
/* code runs quickly */
time_t b = time(NULL);
a and b can be equal even though time did pass.
gettimeofday() gives you a struct timeval, which contains:
tv_sec: whole secondstv_usec: microseconds
A microsecond is 1/1,000,000 of a second.
So this structure lets you measure much smaller intervals than time().
Example:
struct timeval tv;
gettimeofday(&tv, NULL);
If you subtract two timeval values, the result tells you elapsed time as:
diff.tv_sec= secondsdiff.tv_usec= leftover microseconds
So:
0 26339means 0 seconds and 26,339 microseconds4 45025means 4 seconds and 45,025 microseconds
That is:
26,339 microseconds = 26.339 milliseconds45,025 microseconds = 45.025 milliseconds
So 4 45025 means 4.045025 seconds total.
Why this matters in real programming:
- performance checks often need sub-second precision
- network, file, and database operations are often measured in milliseconds
- debugging slow code requires more detail than whole seconds
One important note: gettimeofday() gives wall-clock time, which can be affected by system clock changes. In modern C on POSIX systems, clock_gettime() with a monotonic clock is often preferred for elapsed timing.
Mental Model
Think of time() as a wall clock that only shows hours, minutes, and seconds, but no fractions of a second. If you look at it twice very quickly, it may show the same second both times.
Think of gettimeofday() as a stopwatch that shows:
- seconds
- tiny fractions of a second
So if time() is like saying, "The race started at 10:15:03 and ended at 10:15:03," gettimeofday() is like saying, "It took 0.026339 seconds."
Another way to picture struct timeval:
tv_sec= the big unit buckettv_usec= the leftover small-unit bucket
Together they describe one elapsed duration.
So 4 45025 is not two unrelated numbers. It means:
- 4 full seconds
- plus 45,025 microseconds
- total: 4.045025 seconds
Syntax and Examples
The basic syntax with gettimeofday() looks like this:
#include <stdio.h>
#include <sys/time.h>
int main(void) {
struct timeval start, end, diff;
gettimeofday(&start, NULL);
/* code to measure */
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
printf("Elapsed: %ld.%06ld seconds\n", diff.tv_sec, diff.tv_usec);
return 0;
}
What the fields mean
diff.tv_secis the number of whole secondsdiff.tv_usecis the remaining microseconds%06ldprints microseconds with leading zeros so the decimal-style output looks correct
Example with a delay
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
int main(void) {
struct timeval start, end, diff;
gettimeofday(&start, NULL);
usleep(26339); /* sleep for 26,339 microseconds */
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
printf("Elapsed: %ld seconds and %ld microseconds\n", diff.tv_sec, diff.tv_usec);
printf("Elapsed: %ld.%06ld seconds\n", diff.tv_sec, diff.tv_usec);
return 0;
}
Possible output:
Elapsed: 0 seconds and 26339 microseconds
Elapsed: 0.026339 seconds
Convert to milliseconds
long milliseconds = diff.tv_sec * 1000L + diff.tv_usec / 1000L;
printf("Elapsed: %ld ms\n", milliseconds);
Be aware that integer division removes fractions. If you want decimal milliseconds:
double ms = diff.tv_sec * 1000.0 + diff.tv_usec / 1000.0;
printf("Elapsed: %.3f ms\n", ms);
Why time() shows the same value
#include <stdio.h>
#include <time.h>
int main(void) {
time_t start = time(NULL);
/* fast code */
time_t end = time(NULL);
printf("Start: %ld\n", (long)start);
printf("End: %ld\n", (long)end);
printf("Diff: %ld seconds\n", (long)(end - start));
return 0;
}
If the code runs in under one second, Diff may be 0. That is expected.
Step by Step Execution
Consider this example:
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
int main(void) {
struct timeval start, end, diff;
gettimeofday(&start, NULL);
usleep(45025);
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
printf("%ld %ld\n", diff.tv_sec, diff.tv_usec);
return 0;
}
Step by step
-
gettimeofday(&start, NULL);- The current time is stored in
start. - Example:
start.tv_sec = 100start.tv_usec = 200000
- The current time is stored in
-
usleep(45025);- The program waits about 45,025 microseconds.
- That is about 45.025 milliseconds.
-
gettimeofday(&end, NULL);- The current time is stored in
end. - Example:
end.tv_sec = 100end.tv_usec = 245025
- The current time is stored in
-
timersub(&end, &start, &diff);diff = end - start- Result:
diff.tv_sec = 0diff.tv_usec = 45025
-
printf("%ld %ld\n", diff.tv_sec, diff.tv_usec);- Prints:
0 45025
Another example crossing a second boundary
Suppose:
start = 100 sec, 980000 usecend = 105 sec, 25000 usec
After subtraction:
- elapsed = 4 sec and 45025 usec
So the result becomes:
4 45025
This means:
- 4 whole seconds
- plus 45,025 microseconds
- total =
4.045025seconds
Unit conversions
| Value | Meaning |
|---|---|
1 second | 1,000 milliseconds |
1 millisecond | 1,000 microseconds |
1 microsecond | 1,000 nanoseconds |
So:
26339 microseconds=26.339 milliseconds- not
26,339 nanoseconds
That is because tv_usec is microseconds, not nanoseconds.
Real World Use Cases
Measuring elapsed time is common in many kinds of C programs.
1. Benchmarking a function
You may want to compare two implementations of the same algorithm.
gettimeofday(&start, NULL);
sort_data(items, count);
gettimeofday(&end, NULL);
2. Measuring file I/O
You can check how long it takes to read or write a large file.
- loading logs
- parsing CSV data
- saving reports
3. Monitoring network calls
If your program talks to a server, timing helps identify slow requests.
- DNS lookup
- socket connect
- data receive
4. Checking startup performance
Developers often time stages such as:
- reading configuration
- initializing libraries
- connecting to databases
5. Debugging user complaints
If users say a feature is slow, small timing logs can reveal where time is spent.
Example:
printf("Loading config...\n");
gettimeofday(&start, NULL);
load_config();
gettimeofday(&end, NULL);
6. Batch and data-processing scripts
For scripts that process many records, it helps to know:
- total runtime
- time per batch
- time per major step
Real Codebase Usage
In real projects, developers usually do more than print two timestamps.
Common patterns
Guard timing around specific blocks
gettimeofday(&start, NULL);
if (load_data() != 0) {
return 1;
}
gettimeofday(&end, NULL);
This isolates the time for one operation.
Log both total time and stage time
Large programs often measure phases separately:
- initialization
- processing
- cleanup
Wrap timing in helper functions or macros
Instead of repeating timing code everywhere, teams often create utilities.
double elapsed_ms(struct timeval start, struct timeval end) {
return (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
}
Use early returns carefully
If your function has multiple exits, make sure timing still gets recorded when needed.
Prefer monotonic clocks for elapsed time
In production code, clock_gettime(CLOCK_MONOTONIC, ...) is often better than gettimeofday() because it is not affected by wall-clock adjustments.
Example helper-based usage
#include <stdio.h>
#include <sys/time.h>
double elapsed_ms(struct timeval start, struct timeval end) {
return (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
}
int main(void) {
struct timeval start, end;
gettimeofday(&start, NULL);
/* run task */
gettimeofday(&end, NULL);
printf("Task took %.3f ms\n", elapsed_ms(start, end));
return 0;
}
This style is easier to reuse and read.
Common Mistakes
1. Confusing microseconds with nanoseconds
tv_usec means microseconds, not nanoseconds.
Broken interpretation:
/* Wrong idea: 26339 means nanoseconds */
Correct interpretation:
26339 usec=26.339 ms
2. Expecting time() to measure fast code
time() only has second-level resolution.
Broken expectation:
printf("%ld\n", time(NULL));
fast_function();
printf("%ld\n", time(NULL));
If fast_function() takes under one second, both values may match.
3. Printing elapsed time in a confusing format
This output:
printf("%ld %ld\n", diff.tv_sec, diff.tv_usec);
is technically correct, but easy to misread.
Prefer:
printf("%ld.%06ld seconds\n", diff.tv_sec, diff.tv_usec);
4. Forgetting zero padding for microseconds
Broken:
printf("%ld.%ld\n", diff.tv_sec, diff.tv_usec);
If tv_usec is 25, this prints something like 4.25, which looks like 4.25 seconds, but really means 4.000025 seconds.
Correct:
printf("%ld.%06ld\n", diff.tv_sec, diff.tv_usec);
5. Using wall-clock time for precise benchmarking
gettimeofday() can be affected by system time changes.
For better elapsed timing on POSIX systems, use monotonic clocks when available.
6. Doing manual subtraction incorrectly
If you subtract microseconds yourself, crossing a second boundary can be tricky.
Broken code:
long sec = end.tv_sec - start.tv_sec;
long usec = end.tv_usec - start.tv_usec;
If end.tv_usec < start.tv_usec, usec becomes negative.
Safer:
timersub(&end, &start, &diff);
7. Timing code only once
A single run can be noisy because of:
- OS scheduling
- cache warmup
- background tasks
For rough benchmarking, run several times and average the results.
Comparisons
| Method | Resolution | Good for | Limitation |
|---|---|---|---|
time() | Seconds | Logging wall time, coarse timestamps | Too coarse for fast operations |
gettimeofday() | Microseconds | Simple elapsed timing, debugging | Affected by system clock changes |
clock() | CPU time ticks | Measuring CPU time used by the process | Not the same as real elapsed time |
clock_gettime(CLOCK_MONOTONIC) | Usually very fine | Best choice for elapsed timing on POSIX | Slightly more setup |
time() vs gettimeofday()
- Use
time()when whole seconds are enough. - Use
gettimeofday()when you need sub-second detail.
gettimeofday() vs clock_gettime()
gettimeofday()measures wall-clock time.clock_gettime(CLOCK_MONOTONIC, ...)measures steadily increasing time for elapsed duration.
clock() vs wall-clock time
clock() measures CPU time consumed by the program, not necessarily the real time a user waits.
If your program sleeps or waits on I/O, clock() may not reflect that delay.
Cheat Sheet
time(NULL)returns current time in seconds.- Fast code may show the same
time()value before and after. gettimeofday()fills astruct timeval.tv_sec= secondstv_usec= microseconds1 ms = 1000 usec1 usec = 1000 ns
Read these results
0 26339=0 seconds + 26339 microseconds0 26339=26.339 ms4 45025=4 seconds + 45025 microseconds4 45025=4.045025 s
Typical timing pattern
struct timeval start, end, diff;
gettimeofday(&start, NULL);
/* code */
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
printf("%ld.%06ld seconds\n", diff.tv_sec, diff.tv_usec);
Convert to milliseconds
double ms = diff.tv_sec * 1000.0 + diff.tv_usec / 1000.0;
Better display format
printf("%ld.%06ld seconds\n", diff.tv_sec, diff.tv_usec);
Watch out for
tv_usecis microseconds, not nanosecondstime()is too coarse for short tasks%06ldis important for proper formattinggettimeofday()is wall-clock basedclock_gettime(CLOCK_MONOTONIC, ...)is often better for elapsed timing
FAQ
Why are time() values before and after my function the same?
Because time() only measures whole seconds. If your code runs in less than one second, both calls can return the same value.
Does tv_usec mean nanoseconds?
No. tv_usec means microseconds.
How many milliseconds is 26339 microseconds?
26339 microseconds is 26.339 milliseconds.
What does 4 45025 mean in struct timeval output?
It means 4 seconds and 45,025 microseconds, which is 4.045025 seconds total.
Is gettimeofday() good for benchmarking?
It is good for simple measurements, but for more reliable elapsed timing, clock_gettime(CLOCK_MONOTONIC, ...) is usually better.
Should I print tv_sec and tv_usec separately?
You can, but printing as seconds.microseconds is usually easier to read.
Why use %06ld when printing microseconds?
Because microseconds should always take 6 digits. This preserves the correct decimal-style format.
Can I use clock() instead?
Yes, but clock() measures CPU time, not real elapsed wall time. That can give different results.
Mini Project
Description
Build a small C program that times several tasks and prints the elapsed duration in both microseconds and milliseconds. This demonstrates how to measure code execution more clearly than using time() alone.
Goal
Create a timing utility that measures short operations and prints human-readable elapsed times.
Requirements
- Use
gettimeofday()to capture a start and end time. - Measure at least two separate operations.
- Print the elapsed result as seconds, microseconds, and milliseconds.
- Format microseconds correctly using zero padding.
- Include one short operation and one longer operation for comparison.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.