View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.logging.log4j.perf.jmh;
19  
20  import java.nio.ByteBuffer;
21  import java.text.SimpleDateFormat;
22  import java.util.Calendar;
23  import java.util.Date;
24  import java.util.concurrent.TimeUnit;
25  
26  import org.apache.logging.log4j.core.util.Charsets;
27  import org.openjdk.jmh.annotations.BenchmarkMode;
28  import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
29  import org.openjdk.jmh.annotations.Mode;
30  import org.openjdk.jmh.annotations.OutputTimeUnit;
31  import org.openjdk.jmh.annotations.Scope;
32  import org.openjdk.jmh.annotations.State;
33  
34  /**
35   * Tests performance of various time format implementation.
36   */
37  // ============================== HOW TO RUN THIS TEST: ====================================
38  //
39  // single thread:
40  // java -jar log4j-perf/target/microbenchmarks.jar ".*TimeFormat.*" -f 1 -wi 5 -i 5
41  //
42  // multiple threads (for example, 4 threads):
43  // java -jar log4j-perf/target/microbenchmarks.jar ".*TimeFormat.*" -f 1 -wi 5 -i 5 -t 4 -si true
44  //
45  // Usage help:
46  // java -jar log4j-perf/target/microbenchmarks.jar -help
47  //
48  @State(Scope.Thread)
49  public class TimeFormatBenchmark {
50  
51      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
52      long midnightToday = 0;
53      long midnightTomorrow = 0;
54  
55      @State(Scope.Thread)
56      public static class BufferState {
57          ByteBuffer buffer = ByteBuffer.allocate(12);
58      }
59  
60      private long millisSinceMidnight(long now) {
61          if (now >= midnightTomorrow) {
62              midnightToday = calcMidnightMillis(0);
63              midnightTomorrow = calcMidnightMillis(1);
64          }
65          return now - midnightToday;
66      }
67  
68      private long calcMidnightMillis(int addDays) {
69          //Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UCT"));
70          Calendar cal = Calendar.getInstance();
71          cal.set(Calendar.HOUR_OF_DAY, 0);
72          cal.set(Calendar.MINUTE, 0);
73          cal.set(Calendar.SECOND, 0);
74          cal.set(Calendar.MILLISECOND, 0);
75          cal.add(Calendar.DATE, addDays);
76          return cal.getTimeInMillis();
77      }
78  
79      public static void main(String[] args) {
80          System.out.println(new TimeFormatBenchmark().customFastFormatString(new BufferState()));
81          System.out.println(new TimeFormatBenchmark().customFormatString(new BufferState()));
82      }
83  
84      @GenerateMicroBenchmark
85      @BenchmarkMode(Mode.SampleTime)
86      @OutputTimeUnit(TimeUnit.NANOSECONDS)
87      public void baseline() {
88      }
89  
90      @GenerateMicroBenchmark
91      @BenchmarkMode(Mode.SampleTime)
92      @OutputTimeUnit(TimeUnit.NANOSECONDS)
93      public String simpleDateFormatString() {
94          return simpleDateFormat.format(new Date());
95      }
96  
97      @GenerateMicroBenchmark
98      @BenchmarkMode(Mode.SampleTime)
99      @OutputTimeUnit(TimeUnit.NANOSECONDS)
100     public int simpleDateFormatBytes(BufferState state) {
101         String str = simpleDateFormat.format(new Date());
102         byte[] bytes = str.getBytes(Charsets.UTF_8);
103         state.buffer.clear();
104         state.buffer.put(bytes);
105         return state.buffer.position();
106     }
107 
108     @GenerateMicroBenchmark
109     @BenchmarkMode(Mode.SampleTime)
110     @OutputTimeUnit(TimeUnit.NANOSECONDS)
111     public String customFastFormatString(BufferState state) {
112         state.buffer.clear();
113         fastFormat(System.currentTimeMillis(), state.buffer);
114         return new String(state.buffer.array(), 0, state.buffer.position(), Charsets.UTF_8);
115     }
116 
117     @GenerateMicroBenchmark
118     @BenchmarkMode(Mode.SampleTime)
119     @OutputTimeUnit(TimeUnit.NANOSECONDS)
120     public int customFastFormatBytes(BufferState state) {
121         state.buffer.clear();
122         fastFormat(System.currentTimeMillis(), state.buffer);
123         return state.buffer.position();
124     }
125 
126     @GenerateMicroBenchmark
127     @BenchmarkMode(Mode.SampleTime)
128     @OutputTimeUnit(TimeUnit.NANOSECONDS)
129     public String customFormatString(BufferState state) {
130         state.buffer.clear();
131         format(System.currentTimeMillis(), state.buffer);
132         return new String(state.buffer.array(), 0, state.buffer.position(), Charsets.UTF_8);
133     }
134 
135     @GenerateMicroBenchmark
136     @BenchmarkMode(Mode.SampleTime)
137     @OutputTimeUnit(TimeUnit.NANOSECONDS)
138     public int customFormatBytes(BufferState state) {
139         state.buffer.clear();
140         format(System.currentTimeMillis(), state.buffer);
141         return state.buffer.position();
142     }
143 
144     public ByteBuffer fastFormat(long time, ByteBuffer buffer) {
145         // Calculate values by getting the ms values first and do then
146         // shave off the hour minute and second values with multiplications
147         // and bit shifts instead of simple but expensive divisions.
148 
149         // Get daytime in ms which does fit into an int
150         // int ms = (int) (time % 86400000);
151         int ms = (int) (millisSinceMidnight(time));
152 
153         // well ... it works
154         int hour = (int) (((ms >> 7) * 9773437L) >> 38);
155         ms -= 3600000 * hour;
156 
157         int minute = (int) (((ms >> 5) * 2290650L) >> 32);
158         ms -= 60000 * minute;
159 
160         int second = ((ms >> 3) * 67109) >> 23;
161         ms -= 1000 * second;
162 
163         // Hour
164         // 13/128 is nearly the same as /10 for values up to 65
165         int temp = (hour * 13) >> 7;
166         buffer.put((byte) (temp + '0'));
167 
168         // Do subtract to get remainder instead of doing % 10
169         buffer.put((byte) (hour - 10 * temp + '0'));
170         buffer.put((byte) ':');
171 
172         // Minute
173         // 13/128 is nearly the same as /10 for values up to 65
174         temp = (minute * 13) >> 7;
175         buffer.put((byte) (temp + '0'));
176 
177         // Do subtract to get remainder instead of doing % 10
178         buffer.put((byte) (minute - 10 * temp + '0'));
179         buffer.put((byte) ':');
180 
181         // Second
182         // 13/128 is nearly the same as /10 for values up to 65
183         temp = (second * 13) >> 7;
184         buffer.put((byte) (temp + '0'));
185         buffer.put((byte) (second - 10 * temp + '0'));
186         buffer.put((byte) '.');
187 
188         // Millisecond
189         // 41/4096 is nearly the same as /100
190         temp = (ms * 41) >> 12;
191         buffer.put((byte) (temp + '0'));
192 
193         ms -= 100 * temp;
194         temp = (ms * 205) >> 11; // 205/2048 is nearly the same as /10
195         buffer.put((byte) (temp + '0'));
196 
197         ms -= 10 * temp;
198         buffer.put((byte) (ms + '0'));
199         return buffer;
200     }
201 
202     public ByteBuffer format(long time, ByteBuffer buffer) {
203         // Calculate values by getting the ms values first and do then
204         // calculate the hour minute and second values divisions.
205 
206         // Get daytime in ms which does fit into an int
207         // int ms = (int) (time % 86400000);
208         int ms = (int) (millisSinceMidnight(time));
209 
210         int hours = ms / 3600000;
211         ms -= 3600000 * hours;
212         
213         int minutes = ms / 60000;
214         ms -= 60000 * minutes;
215 
216         int seconds = ms / 1000;
217         ms -= 1000 * seconds;
218 
219         // Hour
220         int temp = hours / 10;
221         buffer.put((byte) (temp + '0'));
222 
223         // Do subtract to get remainder instead of doing % 10
224         buffer.put((byte) (hours - 10 * temp + '0'));
225         buffer.put((byte) ':');
226 
227         // Minute
228         temp = minutes / 10;
229         buffer.put((byte) (temp + '0'));
230 
231         // Do subtract to get remainder instead of doing % 10
232         buffer.put((byte) (minutes - 10 * temp + '0'));
233         buffer.put((byte) ':');
234 
235         // Second
236         temp = seconds / 10;
237         buffer.put((byte) (temp + '0'));
238         buffer.put((byte) (seconds - 10 * temp + '0'));
239         buffer.put((byte) '.');
240 
241         // Millisecond
242         temp = ms / 100;
243         buffer.put((byte) (temp + '0'));
244 
245         ms -= 100 * temp;
246         temp = ms / 10;
247         buffer.put((byte) (temp + '0'));
248 
249         ms -= 10 * temp;
250         buffer.put((byte) (ms + '0'));
251         return buffer;
252     }
253 }