2 * Copyright (c) 2020 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #import <TargetConditionals.h>
25 #include <os/collections.h>
29 #include <darwintest.h>
31 #include <sys/resource.h>
33 #include <sys/fcntl.h>
36 // 11400714819323198485 is coprime with 2^64, so this will touch every number
37 // before looping. It's also the closest number to 2^64 / the golden ratio, so
38 // it will give a pretty even distribution
39 static inline uint64_t _seq_next(uint64_t prev
) {
40 return prev
+ 11400714819323198485llu;
43 #define MAPS_COUNT 4096
44 #define ACTIONS_PER_MEASUREMENT_COUNT 512
45 #define INSERT_TO_FIND_RATIO 64
46 #define TOTAL_ITERATIONS 16
48 #define PERF_OUTPUT_FILENAME "/tmp/libcollection_perf_data.csv"
50 // This is fairly arbitrary, and just makes sure that any value bias is fairly
52 static inline uint64_t _value_for_key(uint64_t key
) {
53 return key
^ 0xFFFF0000FF0000FF;
56 static uint64_t _insert_n_entries_to_maps(os_map_64_t
*maps
, uint64_t seed
) {
57 uint64_t current_seq
= seed
;
58 for ( int i
= 0; i
< ACTIONS_PER_MEASUREMENT_COUNT
; i
++){
59 void *value
= (void *)_value_for_key(current_seq
);
60 for (int j
= 0; j
< MAPS_COUNT
; j
++) {
61 os_map_insert(&maps
[j
], current_seq
, value
);
63 current_seq
= _seq_next(current_seq
);
68 static uint64_t _find_n_entries_in_maps(os_map_64_t
*maps
, uint64_t seed
) {
69 uint64_t current_seq
= seed
;
70 for ( int i
= 0; i
< ACTIONS_PER_MEASUREMENT_COUNT
; i
++){
71 for (int j
= 0; j
< MAPS_COUNT
; j
++) {
72 (void)os_map_find(&maps
[j
], current_seq
);
74 current_seq
= _seq_next(current_seq
);
79 uint64_t absoluteTimeFromMachTime(uint64_t machTime
) {
80 // get the mach timescale
81 static double __TimeScale
= 0.0;
82 if (__TimeScale
== 0.0) {
83 struct mach_timebase_info info
;
84 if (mach_timebase_info(&info
) == KERN_SUCCESS
) {
85 __TimeScale
= ((double)info
.numer
/ (double)info
.denom
);
88 return (uint64_t)(machTime
* __TimeScale
);
95 struct rusage_info_v4 rusage
;
96 if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4
, (rusage_info_t
)&rusage
)) {
98 T_FAIL("rusage failed with %d", rval
);
101 return rusage
.ri_phys_footprint
;
105 T_DECL(map_perf
, "Test map performance for small hash maps",
106 T_META("owner", "Core Darwin Daemons & Tools"),
107 T_META_CHECK_LEAKS(false),
110 #if !(TARGET_OS_IOS | TARGET_OS_OSX)
111 T_PASS("Map_perf doesn't run on this platform");
115 os_map_64_t perf_maps
[MAPS_COUNT
];
117 for (int i
= 0; i
< MAPS_COUNT
; i
++) {
118 os_map_init(&perf_maps
[i
], NULL
);
121 uint64_t current_seq
= _seq_next(0);
124 int output_fd
= creat(PERF_OUTPUT_FILENAME
, S_IWUSR
);
126 FILE *output_file
= fdopen(output_fd
, "w");
130 "MAP_SIZE,INSERT_TIME,FIND_TIME,FIND_MISSING_TIME,MEM_USAGE\n");
132 uint64_t baseline_memory_usage
= _get_dirtymemory();
133 T_LOG("Baseline memory usage is %llu", baseline_memory_usage
);
135 for (int i
= 0; i
< TOTAL_ITERATIONS
; i
++) {
136 uint64_t map_size
= ACTIONS_PER_MEASUREMENT_COUNT
* (i
+ 1);
137 T_LOG("Starting performance testing with map size %llu",
140 uint64_t insert_start_mach_time
= mach_absolute_time();
141 uint64_t next_seq
= _insert_n_entries_to_maps(perf_maps
,
143 uint64_t insert_end_mach_time
= mach_absolute_time();
145 uint64_t insert_start_time
= absoluteTimeFromMachTime(insert_start_mach_time
);
146 uint64_t insert_end_time
= absoluteTimeFromMachTime(insert_end_mach_time
);
148 T_LOG("Insertions complete, with start time %llu, end time %llu",
149 insert_start_time
, insert_end_time
);
151 uint64_t insert_average_time
= ((insert_end_time
- insert_start_time
)) /
152 (MAPS_COUNT
* ACTIONS_PER_MEASUREMENT_COUNT
);
154 T_LOG("DATA: INSERTION TIME %llu, %llu", map_size
,
155 insert_average_time
);
157 uint64_t find_start_mach_time
= mach_absolute_time();
158 for (int j
= 0; j
< INSERT_TO_FIND_RATIO
; j
++) {
159 _find_n_entries_in_maps(perf_maps
, current_seq
);
161 uint64_t find_end_mach_time
= mach_absolute_time();
163 uint64_t find_start_time
= absoluteTimeFromMachTime(find_start_mach_time
);
164 uint64_t find_end_time
= absoluteTimeFromMachTime(find_end_mach_time
);
166 T_LOG("Finds complete, with start time %llu, end time %llu",
167 find_start_time
, find_end_time
);
169 uint64_t find_average_time
= ((find_end_time
- find_start_time
)) /
170 (MAPS_COUNT
* ACTIONS_PER_MEASUREMENT_COUNT
* INSERT_TO_FIND_RATIO
);
172 T_LOG("DATA: FIND TIME FOR %llu, %llu", map_size
,
175 uint64_t no_find_start_mach_time
= mach_absolute_time();
176 for (int j
= 0; j
< INSERT_TO_FIND_RATIO
; j
++) {
177 _find_n_entries_in_maps(perf_maps
, next_seq
);
179 uint64_t no_find_end_mach_time
= mach_absolute_time();
181 uint64_t no_find_start_time
= absoluteTimeFromMachTime(no_find_start_mach_time
);
182 uint64_t no_find_end_time
= absoluteTimeFromMachTime(no_find_end_mach_time
);
184 T_LOG("Find not present complete, with start time %llu, end time %llu",
185 no_find_start_time
, no_find_end_time
);
187 uint64_t no_find_average_time
= ((no_find_end_time
- no_find_start_time
)) /
188 (MAPS_COUNT
* ACTIONS_PER_MEASUREMENT_COUNT
* INSERT_TO_FIND_RATIO
);
190 T_LOG("DATA: NO-FIND TIME FOR %llu, %llu",
191 map_size
, no_find_average_time
);
194 uint64_t latest_memory_usage
= _get_dirtymemory();
195 T_LOG("New memory usage is %llu", latest_memory_usage
);
197 uint64_t average_memory_usage
= latest_memory_usage
/ MAPS_COUNT
;
198 T_LOG("DATA: MEMORY USAGE FOR %llu, %llu", map_size
,
199 average_memory_usage
);
201 fprintf(output_file
, "%llu,%llu,%llu,%llu,%llu\n", map_size
,
202 insert_average_time
, find_average_time
,
203 no_find_average_time
, average_memory_usage
);
205 current_seq
= next_seq
;
211 T_PASS("Finished, output generated at: " PERF_OUTPUT_FILENAME
);