Skip to content

Commit

Permalink
Fieldmap length and checksum optimization (#42)
Browse files Browse the repository at this point in the history
* Create NumbersCache.java
A cache for strings of frequently used integers

* Optimized message checksum and length calculation
Avoided some temporary IntField creation to compute length.
In fact, the "isStringEquivalent" branch that I added is useless with previous commit on Message class, which directly compute length & checksum if possible.
Change some 'for each' list iteration by old loop over index to avoid iterator allocations, as the jvm will not always do it for us.

* - minor renaming
- changed NumbersCache to only hold numbers up to 99999 since it should be sufficient for normal use cases
  • Loading branch information
charlesbr1 authored and chrjohn committed May 9, 2018
1 parent 6229d07 commit 9921529
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 23 deletions.
57 changes: 41 additions & 16 deletions quickfixj-core/src/main/java/quickfix/FieldMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import quickfix.field.converter.UtcDateOnlyConverter;
import quickfix.field.converter.UtcTimeOnlyConverter;
import quickfix.field.converter.UtcTimestampConverter;
import org.quickfixj.CharsetSupport;

import java.io.Serializable;
import java.math.BigDecimal;
Expand Down Expand Up @@ -471,8 +472,9 @@ protected void calculateString(StringBuilder buffer, int[] preFields, int[] post
} else if (isGroupField(tag) && isOrderedField(tag, fieldOrder)
&& getGroupCount(tag) > 0) {
appendField(buffer, field);
for (Group group : getGroups(tag)) {
group.calculateString(buffer, preFields, postFields);
List<Group> groups = getGroups(tag);
for (int i = 0; i < groups.size(); i++) {
groups.get(i).calculateString(buffer, preFields, postFields);
}
}
}
Expand All @@ -483,10 +485,10 @@ && getGroupCount(tag) > 0) {
final List<Group> groups = entry.getValue();
int groupCount = groups.size();
if (groupCount > 0) {
final IntField countField = new IntField(groupCountTag.intValue(), groupCount);
appendField(buffer, countField);
for (Group group : groups) {
group.calculateString(buffer, preFields, postFields);
buffer.append(NumbersCache.get(groupCountTag)).append('=');
buffer.append(NumbersCache.get(groupCount)).append('\001');
for (int i = 0; i < groups.size(); i++) {
groups.get(i).calculateString(buffer, preFields, postFields);
}
}
}
Expand All @@ -499,6 +501,8 @@ && getGroupCount(tag) > 0) {
}
}

private static final boolean IS_STRING_EQUIVALENT = CharsetSupport.isStringEquivalent(CharsetSupport.getCharsetInstance());

int calculateLength() {
int result = 0;
for (final Field<?> field : fields.values()) {
Expand All @@ -512,17 +516,27 @@ int calculateLength() {
for (Entry<Integer, List<Group>> entry : groups.entrySet()) {
final List<Group> groupList = entry.getValue();
if (!groupList.isEmpty()) {
final IntField groupField = new IntField(entry.getKey());
groupField.setValue(groupList.size());
result += groupField.getLength();
for (final Group group : groupList) {
result += group.calculateLength();
if(IS_STRING_EQUIVALENT) {
result += getStringLength(entry.getKey()) + getStringLength(groupList.size()) + 2;
} else {
result += MessageUtils.length(CharsetSupport.getCharsetInstance(), NumbersCache.get(entry.getKey()));
result += MessageUtils.length(CharsetSupport.getCharsetInstance(), NumbersCache.get(groupList.size()));
result += 2;
}
for (int i = 0; i < groupList.size(); i++) {
result += groupList.get(i).calculateLength();
}
}
}
return result;
}

private static int getStringLength(int num) {
if(num == 0)
return 1;
return (int)(num > 0 ? Math.log10(num) + 1 : Math.log10(-num) + 2);
}

int calculateChecksum() {
int result = 0;
for (final Field<?> field : fields.values()) {
Expand All @@ -534,11 +548,21 @@ int calculateChecksum() {
for (Entry<Integer, List<Group>> entry : groups.entrySet()) {
final List<Group> groupList = entry.getValue();
if (!groupList.isEmpty()) {
final IntField groupField = new IntField(entry.getKey());
groupField.setValue(groupList.size());
result += groupField.getChecksum();
for (final Group group : groupList) {
result += group.calculateChecksum();
if(IS_STRING_EQUIVALENT) {
String value = NumbersCache.get(entry.getKey());
for (int i = value.length(); i-- != 0;)
result += value.charAt(i);
value = NumbersCache.get(groupList.size());
for (int i = value.length(); i-- != 0;)
result += value.charAt(i);
result += '=' + 1;
} else {
final IntField groupField = new IntField(entry.getKey());
groupField.setValue(groupList.size());
result += groupField.getChecksum();
}
for (int i = 0; i < groupList.size(); i++) {
result += groupList.get(i).calculateChecksum();
}
}
}
Expand Down Expand Up @@ -662,4 +686,5 @@ public boolean hasGroup(Group group) {
return hasGroup(group.getFieldTag());
}


}
16 changes: 9 additions & 7 deletions quickfixj-core/src/main/java/quickfix/NumbersCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,18 @@ public static String get(int i) {
return String.valueOf(i);
}

/**
* Get the string representing the given double if it's an integer
*
* @param d the double to convert
* @return the String representing the double or null if the double is not an integer
*/

/**
* Get the string representing the given double if it's an integer
*
* @param d the double to convert
* @return the String representing the double or null if the double is not an integer
*/
public static String get(double d) {
long l = (long)d;
if (d == (double)l)
return get(l);
return null;
}
}

}

0 comments on commit 9921529

Please sign in to comment.