1/*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20/*
21    auto_perf.c
22    Performance utilities.
23    Copyright (c) 2010-2011 Apple Inc. All rights reserved.
24 */
25
26#include <stdio.h>
27#include "auto_zone.h"
28#include <mach/mach_time.h>
29#include <mach/clock_types.h>
30#include <time.h>
31
32auto_zone_t *azone;
33mach_timebase_info_data_t timebase;
34
35uint64_t duration(uint64_t start, uint64_t end) {
36    return (end - start) * timebase.numer / timebase.denom;
37}
38
39char *duration_description(uint64_t time, char *buf, int bufsz) {
40    static char *suffixes[] = { "ns", "us", "ms", "s"};
41    int divisor = 1;
42    int suffix = 0;
43
44    while (time/divisor > 1000 && suffix < sizeof(suffixes)/sizeof(void *)) {
45        divisor *= 1000;
46        suffix++;
47    }
48
49    snprintf(buf, bufsz, "%3.3g %s", (float)time/divisor, suffixes[suffix]);
50    return buf;
51}
52
53void test_duration_description() {
54    char buf[32];
55    int d=1;
56    uint64_t start, end;
57    struct timespec sleep_time;
58
59    for (int i=0; i<10; i++) {
60        sleep_time.tv_sec = d/NSEC_PER_SEC;
61        sleep_time.tv_nsec = d % NSEC_PER_SEC;
62        start = mach_absolute_time();
63        nanosleep(&sleep_time, NULL);
64        end = mach_absolute_time();
65        printf("sleep of %d ns ==> %s\n", d, duration_description(duration(start, end), buf, sizeof(buf)));
66        d *= 10;
67    }
68}
69
70void initialize() {
71    azone = auto_zone_create("perf test zone");
72    mach_timebase_info(&timebase);
73}
74
75// executes test many times and returns the speed in iterations per second
76void measure(uint64_t *reps, uint64_t *time, void (^test)()) {
77    const uint64_t MIN_MEASURE_TIME = ((uint64_t)NSEC_PER_SEC);
78    uint64_t start, end;
79    uint64_t repetitions = 1, d, new_reps;
80
81    do {
82        start = mach_absolute_time();
83        for (uint64_t i=0; i<repetitions; i++) {
84            test();
85        }
86        end = mach_absolute_time();
87        d = duration(start, end);
88        if (d < MIN_MEASURE_TIME) {
89            if (d < MIN_MEASURE_TIME/1000) {
90                new_reps = repetitions * 10;
91            } else {
92                new_reps = (MIN_MEASURE_TIME+MIN_MEASURE_TIME/100) * repetitions / d;
93            }
94            //printf("d = %llu, reps = %llu, new_reps = %llu\n", d, repetitions, new_reps);
95            repetitions = new_reps;
96        }
97    } while (d < MIN_MEASURE_TIME);
98    *reps = repetitions;
99    *time = d;
100}
101
102void log_result(char *name, uint64_t repetitions, uint64_t time) {
103    char buf[32];
104    printf("%s: %ld repetitions in %s = %ld per second\n", name, (long)repetitions, duration_description(time, buf, sizeof(buf)), (long)(repetitions * NSEC_PER_SEC / time));
105}
106
107void measure_auto_zone_set_write_barrier() {
108    void *object1 = auto_zone_allocate_object(azone, sizeof(void *), AUTO_MEMORY_SCANNED, 0, 0);
109    void *object2 = auto_zone_allocate_object(azone, sizeof(void *), AUTO_MEMORY_SCANNED, 0, 0);
110    uint64_t reps, time;
111    measure(&reps, &time, ^{
112        auto_zone_set_write_barrier(azone, object1, object2);
113    });
114    log_result("auto_zone_set_write_barrier", reps, time);
115}
116
117void measure_subzone_refcounting() {
118    void *o = auto_zone_allocate_object(azone, sizeof(void *), AUTO_MEMORY_SCANNED, 0, 0);
119    uint64_t reps, time;
120
121    measure(&reps, &time, ^{
122        auto_zone_retain(azone, o);
123    });
124    log_result("auto_zone_retain(subzone)", reps, time);
125
126    // retain a lot so we can do the release test without underflow
127    for (int i=0; i<reps; i++)
128        auto_zone_retain(azone, 0);
129
130    measure(&reps, &time, ^{
131        auto_zone_retain_count(azone, o);
132    });
133    log_result("auto_zone_retain_count(subzone)", reps, time);
134
135    measure(&reps, &time, ^{
136        auto_zone_release(azone, o);
137    });
138    log_result("auto_zone_release(subzone)", reps, time);
139}
140
141int main(int argc, char *argv[])
142{
143    initialize();
144
145    //test_duration_description();
146    measure_auto_zone_set_write_barrier();
147    measure_subzone_refcounting();
148
149    return 0;
150}