Skip to content

Commit 0b72b55

Browse files
Eliminate allocs - Cache formatted Strings (PhilJay#1892)
ValueFormatter objects now rely on a FormattedStringCache to return already-formatted Strings. We might want to create primitive-enabled versions since all our values to format are float or double primitives, and the keys are also all primitives. This will eliminate auto-boxing penalties.
1 parent bd45d73 commit 0b72b55

13 files changed

Lines changed: 136 additions & 49 deletions

MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.github.mikephil.charting.data.BarEntry;
1717
import com.github.mikephil.charting.data.Entry;
1818
import com.github.mikephil.charting.formatter.AxisValueFormatter;
19+
import com.github.mikephil.charting.formatter.FormattedStringCache;
1920
import com.github.mikephil.charting.utils.ViewPortHandler;
2021
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
2122

@@ -164,15 +165,15 @@ public Data(float xValue, float yValue, String xAxisValue) {
164165

165166
private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter {
166167

167-
private DecimalFormat mFormat;
168+
private FormattedStringCache<Integer, Float> mFormattedStringCache;
168169

169170
public ValueFormatter() {
170-
mFormat = new DecimalFormat("######.0");
171+
mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("######.0"));
171172
}
172173

173174
@Override
174175
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
175-
return mFormat.format(value);
176+
return mFormattedStringCache.getFormattedString(value, dataSetIndex);
176177
}
177178
}
178179
}

MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.github.mikephil.charting.data.LineData;
2222
import com.github.mikephil.charting.data.LineDataSet;
2323
import com.github.mikephil.charting.formatter.AxisValueFormatter;
24+
import com.github.mikephil.charting.formatter.FormattedStringCache;
2425
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
2526
import com.github.mikephil.charting.utils.ColorTemplate;
2627
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
@@ -91,11 +92,12 @@ protected void onCreate(Bundle savedInstanceState) {
9192
xAxis.setGranularity(60000L); // one minute in millis
9293
xAxis.setValueFormatter(new AxisValueFormatter() {
9394

94-
private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm");
95+
private FormattedStringCache<Long, Date> mFormattedStringCache = new FormattedStringCache<Long, Date>(new SimpleDateFormat("dd MMM HH:mm"));
9596

9697
@Override
9798
public String getFormattedValue(float value, AxisBase axis) {
98-
return mFormat.format(new Date((long) value));
99+
Long v = (long) value;
100+
return mFormattedStringCache.getFormattedString(new Date(v), v);
99101
}
100102

101103
@Override

MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.github.mikephil.charting.data.BarDataSet;
2121
import com.github.mikephil.charting.data.BarEntry;
2222
import com.github.mikephil.charting.data.Entry;
23+
import com.github.mikephil.charting.formatter.FormattedStringCache;
2324
import com.github.mikephil.charting.formatter.ValueFormatter;
2425
import com.github.mikephil.charting.formatter.AxisValueFormatter;
2526
import com.github.mikephil.charting.highlight.Highlight;
@@ -79,11 +80,12 @@ protected void onCreate(Bundle savedInstanceState) {
7980
xAxis.setGranularity(10f);
8081
xAxis.setValueFormatter(new AxisValueFormatter() {
8182

82-
private DecimalFormat format = new DecimalFormat("###");
83+
private FormattedStringCache<Float, Float> format = new FormattedStringCache<Float, Float>(new DecimalFormat("###"));
8384

8485
@Override
8586
public String getFormattedValue(float value, AxisBase axis) {
86-
return format.format(value) + "-" + format.format(value + 10);
87+
Float key = value + 10;
88+
return format.getFormattedString(value, value) + "-" + format.getFormattedString(value, key);
8789
}
8890

8991
@Override
@@ -222,22 +224,25 @@ public void onNothingSelected() {
222224

223225
private class CustomFormatter implements ValueFormatter, AxisValueFormatter {
224226

225-
private DecimalFormat mFormat;
227+
private FormattedStringCache<Integer, Float> mFormatValue;
228+
private FormattedStringCache<Float, Float> mFormatAxis;
226229

227230
public CustomFormatter() {
228-
mFormat = new DecimalFormat("###");
231+
mFormatValue = new FormattedStringCache<>(new DecimalFormat("###"));
232+
mFormatAxis = new FormattedStringCache<>(new DecimalFormat("###"));
229233
}
230234

231235
// data
232236
@Override
233237
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
234-
return mFormat.format(Math.abs(value)) + "m";
238+
return mFormatValue.getFormattedString(value, dataSetIndex) + "m";
235239
}
236240

237241
// YAxis
238242
@Override
239243
public String getFormattedValue(float value, AxisBase axis) {
240-
return mFormat.format(Math.abs(value)) + "m";
244+
Float v = Math.abs(value);
245+
return mFormatAxis.getFormattedString(v,v) + "m";
241246
}
242247

243248
@Override

MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@
22

33
import com.github.mikephil.charting.components.AxisBase;
44
import com.github.mikephil.charting.formatter.AxisValueFormatter;
5+
import com.github.mikephil.charting.formatter.FormattedStringCache;
56

67
import java.text.DecimalFormat;
78

89
public class MyAxisValueFormatter implements AxisValueFormatter {
910

1011
private DecimalFormat mFormat;
12+
private FormattedStringCache<Float, Float> mFormattedStringCache;
1113

1214
public MyAxisValueFormatter() {
13-
mFormat = new DecimalFormat("###,###,###,##0.0");
15+
mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0"));
1416
}
1517

1618
@Override
1719
public String getFormattedValue(float value, AxisBase axis) {
18-
return mFormat.format(value) + " $";
20+
return mFormattedStringCache.getFormattedString(value, value) + " $";
1921
}
2022

2123
@Override

MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.mikephil.charting.components.AxisBase;
44
import com.github.mikephil.charting.formatter.AxisValueFormatter;
5+
import com.github.mikephil.charting.formatter.FormattedStringCache;
56
import com.github.mikephil.charting.utils.ViewPortHandler;
67

78
import java.text.DecimalFormat;
@@ -11,13 +12,13 @@
1112
*/
1213
public class MyCustomXAxisValueFormatter implements AxisValueFormatter {
1314

14-
private DecimalFormat mFormat;
15+
private FormattedStringCache<Float, Float> mFormattedStringCache;
1516
private ViewPortHandler mViewPortHandler;
1617

1718
public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) {
1819
mViewPortHandler = viewPortHandler;
1920
// maybe do something here or provide parameters in constructor
20-
mFormat = new DecimalFormat("###,###,###,##0.0");
21+
mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0"));
2122
}
2223

2324
@Override
@@ -26,14 +27,15 @@ public String getFormattedValue(float value, AxisBase axis) {
2627
//Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY());
2728

2829
// e.g. adjust the x-axis values depending on scale / zoom level
29-
if (mViewPortHandler.getScaleX() > 5)
30+
final float xScale = mViewPortHandler.getScaleX();
31+
if (xScale > 5)
3032
return "4";
31-
else if (mViewPortHandler.getScaleX() > 3)
33+
else if (xScale > 3)
3234
return "3";
33-
else if (mViewPortHandler.getScaleX() > 1)
35+
else if (xScale > 1)
3436
return "2";
3537
else
36-
return mFormat.format(value);
38+
return mFormattedStringCache.getFormattedString(value, value);
3739
}
3840

3941
@Override
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
package com.xxmassdeveloper.mpchartexample.custom;
22

33
import com.github.mikephil.charting.data.Entry;
4+
import com.github.mikephil.charting.formatter.FormattedStringCache;
45
import com.github.mikephil.charting.formatter.ValueFormatter;
56
import com.github.mikephil.charting.utils.ViewPortHandler;
67

78
import java.text.DecimalFormat;
89

910
public class MyValueFormatter implements ValueFormatter {
1011

11-
private DecimalFormat mFormat;
12+
private FormattedStringCache<Integer, Float> mFormattedStringCache;
1213

1314
public MyValueFormatter() {
14-
mFormat = new DecimalFormat("###,###,###,##0.0");
15+
mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0"));
1516
}
1617

1718
@Override
1819
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
19-
return mFormat.format(value) + " $";
20+
return mFormattedStringCache.getFormattedString(value, dataSetIndex) + " $";
2021
}
2122
}

MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.github.mikephil.charting.components.MarkerView;
99
import com.github.mikephil.charting.data.CandleEntry;
1010
import com.github.mikephil.charting.data.Entry;
11+
import com.github.mikephil.charting.formatter.FormattedStringCache;
1112
import com.github.mikephil.charting.highlight.Highlight;
1213
import com.github.mikephil.charting.utils.Utils;
1314
import com.xxmassdeveloper.mpchartexample.R;
@@ -22,7 +23,7 @@
2223
public class RadarMarkerView extends MarkerView {
2324

2425
private TextView tvContent;
25-
private DecimalFormat format = new DecimalFormat("##0");
26+
private FormattedStringCache<Float, Float> mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("##0"));
2627

2728
public RadarMarkerView(Context context, int layoutResource) {
2829
super(context, layoutResource);
@@ -35,7 +36,8 @@ public RadarMarkerView(Context context, int layoutResource) {
3536
// content (user-interface)
3637
@Override
3738
public void refreshContent(Entry e, Highlight highlight) {
38-
tvContent.setText(format.format(e.getY()) + " %");
39+
float value = e.getY();
40+
tvContent.setText(mFormattedStringCache.getFormattedString(value, value) + " %");
3941
}
4042

4143
@Override

MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
public class DefaultAxisValueFormatter implements AxisValueFormatter {
1111

1212
/**
13-
* decimalformat for formatting
13+
* FormattedStringFormat for formatting and caching
1414
*/
15-
protected DecimalFormat mFormat;
15+
protected FormattedStringCache<Float, Float> mFormattedStringCache;
1616

1717
/**
1818
* the number of decimal digits this formatter uses
@@ -35,13 +35,15 @@ public DefaultAxisValueFormatter(int digits) {
3535
b.append("0");
3636
}
3737

38-
mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
38+
mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0" + b.toString()));
3939
}
4040

4141
@Override
4242
public String getFormattedValue(float value, AxisBase axis) {
43-
// avoid memory allocations here (for performance)
44-
return mFormat.format(value);
43+
44+
// TODO: There should be a better way to do this. Floats are not the best keys...
45+
return mFormattedStringCache.getFormattedString(value, value);
46+
4547
}
4648

4749
@Override

MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
public class DefaultValueFormatter implements ValueFormatter {
1616

1717
/**
18-
* DecimalFormat for formatting
18+
* FormattedStringCache for formatting and caching.
1919
*/
20-
protected DecimalFormat mFormat;
20+
protected FormattedStringCache<Integer, Float> mFormattedStringCache;
2121

2222
protected int mDecimalDigits;
2323

@@ -47,16 +47,15 @@ public void setup(int digits) {
4747
b.append("0");
4848
}
4949

50-
mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
50+
mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0" + b.toString()));
51+
5152
}
5253

5354
@Override
5455
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
5556

56-
// put more logic here ...
57-
// avoid memory allocations here (for performance reasons)
57+
return mFormattedStringCache.getFormattedString(value, dataSetIndex);
5858

59-
return mFormat.format(value);
6059
}
6160

6261
/**
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.github.mikephil.charting.formatter;
2+
3+
import java.text.Format;
4+
import java.util.HashMap;
5+
6+
/**
7+
* Created by Tony Patino on 6/29/16.
8+
*
9+
* COST : Frequently the V type, and the K type will often be passed as primitives (float / int / double)
10+
* This will incur an auto-boxing penalty, and an instantiation on each call.
11+
*
12+
* BENEFIT : Formatting of Strings is one of the costliest operations remaining, and it is larger than the boxing penalty.
13+
* Eliminating redundant formats helps more than boxing hurts.
14+
*
15+
* Possibly want to make some explicit primitive enabled caches, though they can be ugly.
16+
*
17+
*/
18+
public class FormattedStringCache<K, V> {
19+
20+
private Format mFormat;
21+
private HashMap<K, String> mCachedStringsHashMap = new HashMap<>();
22+
private HashMap<K, V> mCachedValuesHashMap = new HashMap<>();
23+
24+
public Format getFormat(){
25+
return mFormat;
26+
}
27+
28+
public FormattedStringCache(Format format){
29+
this.mFormat = format;
30+
}
31+
32+
public String getFormattedString(V value, K key){
33+
34+
// If we can't find the value at all, create an entry for it, and format the string.
35+
if(!mCachedValuesHashMap.containsKey(key)){
36+
mCachedStringsHashMap.put(key, mFormat.format(value));
37+
mCachedValuesHashMap.put(key, value);
38+
}
39+
40+
// If the old value and the new one don't match, format the string and store it, because the string's value will be different now.
41+
if(!value.equals(mCachedValuesHashMap.get(key))){
42+
mCachedStringsHashMap.put(key, mFormat.format(value));
43+
mCachedValuesHashMap.put(key, value);
44+
}
45+
46+
String result = mCachedStringsHashMap.get(key);
47+
48+
return result;
49+
}
50+
51+
}

0 commit comments

Comments
 (0)