Skip to content

Commit cea0dd8

Browse files
author
Mitchell Perilstein
committed
format, doc
1 parent 6033eca commit cea0dd8

File tree

5 files changed

+105
-36
lines changed

5 files changed

+105
-36
lines changed

Makefile

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#
44
# Usage: make [test|bench|clean]
55
#
6+
# Copyright (C) 2014 Mitchell Perilstein
7+
# Licensed under GNU LGPL Version 3. See LICENSING file for details.
8+
#
69

710
LIB = libfast-mktime.so.1
811
ALL = $(LIB) benchmark unittest
@@ -20,7 +23,7 @@ test: $(ALL)
2023
./unittest
2124
@echo
2225
LD_PRELOAD=./$(LIB) ./unittest
23-
26+
2427
bench:
2528
@echo
2629
@echo '## Benchmarking once with stock mktime and once with wrapper.'
@@ -29,6 +32,6 @@ bench:
2932
./benchmark 3 1000000
3033
@echo
3134
LD_PRELOAD=./$(LIB) ./benchmark 3 1000000
32-
35+
3336
clean:
3437
@rm -f $(ALL)

README.md

+56-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,69 @@
11
libfast-mktime
22
==============
33

4-
A C99 mktime(3) library interposer optimized for repeated calls in the same day.
4+
This is a micro-optimization for mktime(3) that will speed it up
5+
around 86x in the best case and have a slight impact in the worse.
6+
7+
Motivation
8+
----------
9+
10+
The system mktime(3) handles time zones, tzset(), leap years, input
11+
normalization, plus the obvious date calculations, all of which can
12+
take some cycles. If your use case entails many mktime() calls within
13+
the same calendar day, there is an easy optimization which can show
14+
substantial savings.
15+
16+
Algorithm
17+
---------
18+
19+
The first call, call system mktime(), memoizing the date and the
20+
beginning of that date in epoch time. Then for subsequent calls, if
21+
they are on the same date, add the requested H:M:S to that saved
22+
midnight, skipping all the date calculations. If the date is
23+
different, use the system mktime() again and memoize it.
24+
25+
This package provides a libfast-mktime.so.1, defining only mktime()
26+
and intended to be interpose system mktime() as above, using
27+
LD_PRELOAD.
28+
29+
Platforms
30+
---------
31+
32+
So far, tested on i686 and x86_64 GNU/Linux.
533

634
Building
735
--------
836

937
$ make
1038
$ make test
11-
$ make bench
39+
$ make bench
1240

1341
Usage
1442
-----
1543

16-
$ gcc your_prog.c
17-
$ ./a.out ## regular
18-
$ LD_PRELOAD=./libfast-mktime.so.1 ./a.out ## faster?
44+
$ LD_PRELOAD=./libfast-mktime.so.1 /path/to/your/program
45+
46+
Benchmark
47+
---------
48+
49+
## Benchmarking once with stock mktime and once with wrapper.
50+
## 3 trials of 1e6 sequential dates will be run.
51+
52+
./benchmark 3 1000000
53+
0: 0.868179 s
54+
1: 0.865169 s
55+
2: 0.866598 s
56+
avg: 0.866649 s
57+
58+
LD_PRELOAD=./libfast-mktime.so.1 ./benchmark 3 1000000
59+
0: 0.014434 s
60+
1: 0.014622 s
61+
2: 0.015013 s
62+
avg: 0.014690 s
63+
64+
License
65+
-------
66+
67+
Copyright (C) 2014 Mitchell Perilstein. Licensed under GNU LGPL
68+
Version 3. See LICENSING file for details.
69+

benchmark.c

+15-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright (C) 2014 Mitchell Perilstein
3+
* Licensed under GNU LGPL Version 3. See LICENSING file for details.
4+
*/
5+
16
#include <time.h>
27
#include <stdlib.h>
38
#include <stdio.h>
@@ -15,14 +20,14 @@ struct tm * makeit(long ndates)
1520
struct tm * date_array = (struct tm *)malloc(ndates * sizeof(struct tm));
1621
if (!date_array)
1722
{
18-
fprintf(stderr, "Too much malloc\n");
19-
exit(1);
23+
fprintf(stderr, "Too much malloc\n");
24+
exit(1);
2025
}
2126

2227
long s;
2328
for (s=0; s<ndates; s++)
2429
{
25-
time_t t = s + 1; // start at year 1
30+
time_t t = s + 1; // start at year 1
2631
gmtime_r(&t, &date_array[s]);
2732
}
2833

@@ -37,24 +42,24 @@ float timeit(struct tm *dates, long ndates)
3742
long s;
3843
for (s=0; s<ndates; s++)
3944
{
40-
time_t made = mktime(&dates[s]);
45+
time_t made = mktime(&dates[s]);
4146
}
4247

4348
gettimeofday(&tend, NULL);
4449
return ((tend.tv_sec * 1e6 + tend.tv_usec)
45-
- (tbeg.tv_sec * 1e6 + tbeg.tv_usec)) / 1e6;
50+
- (tbeg.tv_sec * 1e6 + tbeg.tv_usec)) / 1e6;
4651
}
4752

48-
int main(int argc, char **argv)
53+
int main(int argc, char **argv)
4954
{
50-
if (argc < 3)
51-
usage();
55+
if (argc < 3)
56+
usage();
5257

5358
long trials = atol(argv[1]);
5459
long ndates = atol(argv[2]);
5560

5661
if (ndates<1 || trials<1)
57-
usage();
62+
usage();
5863

5964
struct tm *dates = makeit(ndates);
6065
float total = 0.0;
@@ -63,7 +68,7 @@ int main(int argc, char **argv)
6368
for (i=0; i<trials; i++)
6469
{
6570
float secs = timeit(dates, ndates);
66-
printf("%3d: %f s\n", (int)i, secs);
71+
printf("%3d: %f s\n", (int)i, secs);
6772
total += secs;
6873
}
6974

fast-mktime.c

+15-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright (C) 2014 Mitchell Perilstein
3+
* Licensed under GNU LGPL Version 3. See LICENSING file for details.
4+
*/
5+
16
#include <time.h>
27
#include <stdlib.h>
38
#include <stdio.h>
@@ -16,27 +21,27 @@ time_t mktime(struct tm *tm)
1621
/* Load real mktime once into a static */
1722
if (!mktime_real)
1823
{
19-
void *handle;
24+
void *handle;
2025

21-
/* To forgive this cast, please see man dlopen(3). */
22-
dlerror();
23-
handle = dlsym(RTLD_NEXT, "mktime");
24-
*(void **) (&mktime_real) = handle;
26+
/* To forgive this cast, please see man dlopen(3). */
27+
dlerror();
28+
handle = dlsym(RTLD_NEXT, "mktime");
29+
*(void **) (&mktime_real) = handle;
2530

26-
if (!mktime_real)
31+
if (!mktime_real)
2732
{
2833
fprintf(stderr, "loading mktime: %s\n", dlerror());
2934
exit(EXIT_FAILURE);
3035
}
3136
}
3237

3338
/* the epoch time portion of the request */
34-
hmsarg = 3600 * tm->tm_hour
35-
+ 60 * tm->tm_min
39+
hmsarg = 3600 * tm->tm_hour
40+
+ 60 * tm->tm_min
3641
+ tm->tm_sec;
3742

38-
if ( cache.tm_mday == tm->tm_mday
39-
&& cache.tm_mon == tm->tm_mon
43+
if ( cache.tm_mday == tm->tm_mday
44+
&& cache.tm_mon == tm->tm_mon
4045
&& cache.tm_year == tm->tm_year )
4146
{
4247
/* cached - just add h,m,s from request to midnight */

unittest.c

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright (C) 2014 Mitchell Perilstein
3+
* Licensed under GNU LGPL Version 3. See LICENSING file for details.
4+
*/
5+
16
#include <time.h>
27
#include <stdlib.h>
38
#include <stdio.h>
@@ -6,23 +11,23 @@
611
#include <assert.h>
712

813
void ok(int sec, int min, int hour, int day, int month, int year,
9-
time_t expect, const char *title)
14+
time_t expect, const char *title)
1015
{
1116
struct tm s;
12-
s.tm_sec = sec;
13-
s.tm_min = min;
17+
s.tm_sec = sec;
18+
s.tm_min = min;
1419
s.tm_hour = hour;
15-
s.tm_mday = day;
16-
s.tm_mon = month;
20+
s.tm_mday = day;
21+
s.tm_mon = month;
1722
s.tm_year = year;
1823
s.tm_isdst = 0;
1924

2025
time_t result = mktime(&s);
2126

22-
printf(" %s: got %10d for %s\n",
23-
(result == expect ? "okay" : "fail"),
24-
result,
25-
title);
27+
printf(" %s: got %10lu for %s\n",
28+
(result == expect ? "okay" : "fail"),
29+
(unsigned long)result,
30+
title);
2631
}
2732

2833
int main()

0 commit comments

Comments
 (0)