Upload
dmitriy-dumanskiy
View
203
Download
0
Embed Size (px)
Citation preview
Micro optimizations
Dmitriy DumanskiyBlynk, CTO
Java blog : https://habrahabr.ru/users/doom369/topicsDOU : https://dou.ua/users/DOOM/articles/
Makers problem
+ = ?
Blynk
10000 req/sec3 VM * 2 cores, 60$
25% load10k of local installations
Typical Env.
Worst case 256 RAM (~40Mb for heap)700 MHz, 1 core, ARM
● Typical code● Plain java● System is under “highload”● All “issues” are from open-source
Today talk
Problems everybody knows about
● Primitive wrappers
Typical issues
● Primitive wrappers● Boxing / Unboxing
Typical issues
● Primitive wrappers● Boxing / Unboxing● Regex / Pattern matching
Typical issues
● Primitive wrappers● Boxing / Unboxing● Regex / Pattern matching● String concatenation in loops
Typical issues
● Primitive wrappers● Boxing / Unboxing● Regex / Pattern matching● String concatenation in loops● Reflection
Typical issues
class User { Date createdAt;}
What is wrong here?
class User { Date createdAt;}
Memory consumption
class Date { private transient long fastTime; private BaseCalendar.Date cdate;}
More memory
class User { long createdAt;}
Solution
for (String name : list) { //do something}
What is wrong here?
for (String name : list) { //do something}
Iterator allocation
Iterator<String> iter = list.iterator();while (iter.hasNext()) { String name = iter.next();}
Iterator allocation
Scalar replacement. Could be.
Or could be not.
Iterator allocation
for (int i = 0; i < list.size(); i++) { Data data = list.get(i);}
Solution 1
for (String s : array) { //do something}
Solution 2
iterateArray thrpt 168298 ops/siterateListWGet thrpt 132292 ops/siterateList thrpt 110188 ops/s
Iterator
+50%
List<Device> devices = ...;return devices.get(0);
What is wrong here?
List<Device> devices = ...;return devices.get(0);
Polymorphism
invokeInterface
Polymorphism
ArrayList<Device> devices = ...;return devices.get(0);
Solution
arrayListGet thrpt 268418 ops/slistGet thrpt 249005 ops/s
Polymorphism
+7%
class User { Device[] devices = {}; }
What is wrong here?
class User { Device[] devices = {}; }
Allocations
{} is new Device[0]
Allocations
class User { final static Device[] EMPTY = {}; Device[] devices = EMPTY; }
Solution
preAllocatedArray thrpt 209423 ops/snewArrayAllocation thrpt 104293 ops/s
Allocations
+50%
void make(String… params)
What is wrong here?
void make(String… params)
Allocations
String… is new String[] {params}
Allocations
void make(String p1)void make(String p1, String p2)void make(String p1, String p2, Str p3)
Solution
noVarArgs thrpt 7947138 ops/svarArgs thrpt 4265333 ops/s
Allocations
+2x
If (email == null || email.equals(“”)) { ...}
What is wrong here?
If (email == null || email.equals(“”)) { ...}
Solution
If (email == null || email.isEmpty()) { ...}
Slow equals
isEmpty thrpt 364123 ops/sequals thrpt 231075 ops/s
Slow equals
+60%
val.equals(input.toLowerCase());
What is wrong here?
val.equals(input.toLowerCase());
Allocations
val.equalsIgnoreCase(input);
Solution
ignoreCase thrpt 47550 ops/stoLowerCase thrpt 20339 ops/s
Allocations
+2.3x
val.startsWith(input.toLowerCase());
One more case
val.regionMatches(true, 0, input, 0, len);
Solution
regionMatch thrpt 54924 ops/sstartWithToLowerCase thrpt 14447 ops/s
Allocations
+4x
Use strict equals
Solution 2
val.split(“\0”);
What is wrong here?
val.split(“\0”);
Split is slow
Utils.split(val, ‘\0’);
Solution
String[] split2(char separator, String body) { final int i1 = body.indexOf(separator, 1); if (i1 == -1) { return new String[] {body}; }
return new String[] { body.substring(0, i1), body.substring(i1 + 1, body.length()) }; }
Solution
customSplit avgt 46 ns/opsplit avgt 115 ns/op
Split is slow
+3x
return body1.trim() + SEPARATOR + body2.trim();
What is wrong here?
return new StringBuilder().append(body1.trim()).append(SEPARATOR).append(body2.trim()).toString();
What is wrong here?
return new StringBuilder().append(body1.trim()).append(SEPARATOR).append(body2.trim()).toString();
Concat optimization
String trimmed1 = body1.trim();String trimmed2 = body2.trim();return trimmed1 + SEPARATOR + trimmed2;
Solution 1
String trimmed1 = body1.trim();String trimmed2 = body2.trim();return new StringBuilder().append(trimmed1).append(SEPARATOR).append(trimmed2).toString();
Solution 2
optimizedConcat thrpt 44888 ops/snaiveConcat thrpt 12866 ops/s
Concat optimization
+4x
StringBuilder sb = new StringBuilder();sb.append(body1)sb.append(SEPARATOR)sb.append(body2)return sb.toString();
What is wrong here?
StringBuilder sb = new StringBuilder();sb.append(body1)sb.append(SEPARATOR)sb.append(body2)return sb.toString();
No chaining
return new StringBuilder().append(body1).append(SEPARATOR).append(body2).toString();
Solution
chained thrpt 46279 ops/snotChained thrpt 27746 ops/s
Chaining
+70%
String data = …;data.getBytes(charset);
What is wrong here?
String data = …;data.getBytes(charset);
Generic Encoding
if (charset == UTF_8) { return decodeUTF8(data);} else if (charset == US_ASCII) { return decodeASCII(data);} else { return data.getBytes(charset);}
Solution
customGetBytes avgt 155 ns/opjavaGetBytes avgt 233 ns/op
Generic Encoding
+30%
if (data == null) { throw new NoDataException();}
What is wrong here?
if (data == null) { throw new NoDataException();}
Exception is expensive
Double.parseDouble thrpt 22968 ops/sDouble.parseErr thrpt 737 ops/s
Exception Cost
+30x
public Throwable(String message) { fillInStackTrace(); detailMessage = message;}
Exception Cost
Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)
Exception Cost
class MyException extends Exception { MyException(String message) {
super(message, null, true, false); }}
Solution 1
final static MyException cache = new MyException();
Solution 2
if (data == null) { return; //or return code}
Solution 3
try { double d = Double.parseDouble(s);} catch (NumberFormatException e) {}
What is wrong here?
try { double d = Double.parseDouble(s);} catch (NumberFormatException e) {}
Allocations
● Does trim()● Spams ASCIIToBinaryConverter● Spams exceptions● Slow
Allocations
try { double d = Utils.parseDouble(s);} catch (NumberFormatException e) {}
Solution
parseDouble thrpt 22889 ops/sparseDoubleUtils thrpt 48331 ops/s
Custom parseDouble
+2.2x
double d = 11.2321D;return Math.round(val);
What is wrong here?
double d = 11.2321D;return Math.round(val);
Slow round
double d = 11.2321D;return (int) (val + 0.5);
Solution
Slow round
optimizedRound thrpt 383251 ops/smathRound thrpt 258123 ops/s
+50%
public static int bitCount(int i) { i = i - ((i >>> 1) & 0x55555555); i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); i = (i + (i >>> 4)) & 0x0f0f0f0f; i = i + (i >>> 8); i = i + (i >>> 16); return i & 0x3f; }
What is wrong here?
Integer.bitCount(val);
Solution
javaBitCount thrpt 413996 ops/smanualBitCount thrpt 232490 ops/s
Intrinsics
+80%
http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/classfile/vmSymbols.hpp
Intrinsics
int[] ar = new int[] {1, -32, 1, 1, 5, 6, 7};return IntStream.of(ar).anyMatch(i -> i == val)
What is wrong here?
int[] ar = new int[] {1, -32, 1, 1, 5, 6, 7};return IntStream.of(ar).anyMatch(i -> i == val)
Lambda/Stream cost
naive thrpt 228002 ops/slambda thrpt 22983 ops/s
Lambda/Stream cost
~10x
boolean contains(int[] ar, int value) { for (int arVal : ar) { if (arVal == value) { return true; } } return false; }
Solution
E get(int index) { if (index >= size) { throw new IndexOutOfBoundsException(); } return (E) elementData[index];}
What is wrong here?
E get(int index) { if (index >= size) { throw new IndexOutOfBoundsException(); } return (E) elementData[index];}
Bounds-checking
noCheck thrpt 388332 ops/sboundCheck thrpt 341232 ops/s
Bounds-checking
+12%
E get(int index) { return (E) elementData[index];}
Solution
● Know your data (jmap, logs, db)● Know your flows (metrics, jstack)● Don’t rely on JIT● Don’t trust java :)● Always stress test your code● 1% vs 99%
Summary
https://github.com/blynkkk/blynk-server
https://github.com/doom369/java-microbenchmarks