Browse Source

Improve the speed of the styles-optimiser when there are many styles in a Workbook

Profiling showed that the call to isUserDefined() took most of the time,
so we now fetch this only once for each style and remember the result and
use it in the inner loop.

The attached sample-file took aprox. 1m30s on a decent machine, now it is down
to around 2s.

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1884917 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_5_0_0
Dominik Stadler 3 years ago
parent
commit
a7c1d84c5d

+ 36
- 28
src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java View File

* Excel can get cranky if you give it files containing too * Excel can get cranky if you give it files containing too
* many (especially duplicate) objects, and this class can * many (especially duplicate) objects, and this class can
* help to avoid those. * help to avoid those.
* In general, it's much better to make sure you don't
* In general, it's much better to make sure you don't
* duplicate the objects in your code, as this is likely * duplicate the objects in your code, as this is likely
* to be much faster than creating lots and lots of * to be much faster than creating lots and lots of
* excel objects+records, only to optimise them down to * excel objects+records, only to optimise them down to
public static void optimiseFonts(HSSFWorkbook workbook) { public static void optimiseFonts(HSSFWorkbook workbook) {
// Where each font has ended up, and if we need to // Where each font has ended up, and if we need to
// delete the record for it. Start off with no change // delete the record for it. Start off with no change
short[] newPos =
short[] newPos =
new short[workbook.getWorkbook().getNumberOfFontRecords()+1]; new short[workbook.getWorkbook().getNumberOfFontRecords()+1];
boolean[] zapRecords = new boolean[newPos.length]; boolean[] zapRecords = new boolean[newPos.length];
for(int i=0; i<newPos.length; i++) { for(int i=0; i<newPos.length; i++) {
newPos[i] = (short)i; newPos[i] = (short)i;
zapRecords[i] = false; zapRecords[i] = false;
} }
// Get each font record, so we can do deletes // Get each font record, so we can do deletes
// without getting confused // without getting confused
FontRecord[] frecs = new FontRecord[newPos.length];
FontRecord[] frecs = new FontRecord[newPos.length];
for(int i=0; i<newPos.length; i++) { for(int i=0; i<newPos.length; i++) {
// There is no 4! // There is no 4!
if(i == 4) continue; if(i == 4) continue;
frecs[i] = workbook.getWorkbook().getFontRecordAt(i); frecs[i] = workbook.getWorkbook().getFontRecordAt(i);
} }
// Loop over each font, seeing if it is the same // Loop over each font, seeing if it is the same
// as an earlier one. If it is, point users of the // as an earlier one. If it is, point users of the
// later duplicate copy to the earlier one, and
// later duplicate copy to the earlier one, and
// mark the later one as needing deleting // mark the later one as needing deleting
// Note - don't change built in fonts (those before 5) // Note - don't change built in fonts (those before 5)
for(int i=5; i<newPos.length; i++) { for(int i=5; i<newPos.length; i++) {
int earlierDuplicate = -1; int earlierDuplicate = -1;
for(int j=0; j<i && earlierDuplicate == -1; j++) { for(int j=0; j<i && earlierDuplicate == -1; j++) {
if(j == 4) continue; if(j == 4) continue;
FontRecord frCheck = workbook.getWorkbook().getFontRecordAt(j); FontRecord frCheck = workbook.getWorkbook().getFontRecordAt(j);
if(frCheck.sameProperties(frecs[i])) { if(frCheck.sameProperties(frecs[i])) {
earlierDuplicate = j; earlierDuplicate = j;
} }
} }
// If we got a duplicate, mark it as such // If we got a duplicate, mark it as such
if(earlierDuplicate != -1) { if(earlierDuplicate != -1) {
newPos[i] = (short)earlierDuplicate; newPos[i] = (short)earlierDuplicate;
zapRecords[i] = true; zapRecords[i] = true;
} }
} }
// Update the new positions based on // Update the new positions based on
// deletes that have occurred between // deletes that have occurred between
// the start and them // the start and them
for(int j=0; j<preDeletePos; j++) { for(int j=0; j<preDeletePos; j++) {
if(zapRecords[j]) newPosition--; if(zapRecords[j]) newPosition--;
} }
// Update the new position // Update the new position
newPos[i] = newPosition; newPos[i] = newPosition;
} }
// Zap the un-needed user font records // Zap the un-needed user font records
for(int i=5; i<newPos.length; i++) { for(int i=5; i<newPos.length; i++) {
if(zapRecords[i]) { if(zapRecords[i]) {
); );
} }
} }
// Tell HSSFWorkbook that it needs to // Tell HSSFWorkbook that it needs to
// re-start its HSSFFontCache // re-start its HSSFFontCache
workbook.resetFontCache(); workbook.resetFontCache();
// Update the cell styles to point at the
// Update the cell styles to point at the
// new locations of the fonts // new locations of the fonts
for(int i=0; i<workbook.getWorkbook().getNumExFormats(); i++) { for(int i=0; i<workbook.getWorkbook().getNumExFormats(); i++) {
ExtendedFormatRecord xfr = workbook.getWorkbook().getExFormatAt(i); ExtendedFormatRecord xfr = workbook.getWorkbook().getExFormatAt(i);
newPos[ xfr.getFontIndex() ] newPos[ xfr.getFontIndex() ]
); );
} }
// Update the rich text strings to point at // Update the rich text strings to point at
// the new locations of the fonts // the new locations of the fonts
// Remember that one underlying unicode string // Remember that one underlying unicode string
if(cell.getCellType() == CellType.STRING) { if(cell.getCellType() == CellType.STRING) {
HSSFRichTextString rtr = (HSSFRichTextString)cell.getRichStringCellValue(); HSSFRichTextString rtr = (HSSFRichTextString)cell.getRichStringCellValue();
UnicodeString u = rtr.getRawUnicodeString(); UnicodeString u = rtr.getRawUnicodeString();
// Have we done this string already? // Have we done this string already?
if(! doneUnicodeStrings.contains(u)) { if(! doneUnicodeStrings.contains(u)) {
// Update for each new position // Update for each new position
u.swapFontUse(i, newPos[i]); u.swapFontUse(i, newPos[i]);
} }
} }
// Mark as done // Mark as done
doneUnicodeStrings.add(u); doneUnicodeStrings.add(u);
} }
} }
} }
} }
/** /**
* Goes through the Wokrbook, optimising the cell styles * Goes through the Wokrbook, optimising the cell styles
* by removing duplicate ones, and ones that aren't used. * by removing duplicate ones, and ones that aren't used.
short[] newPos = new short[workbook.getWorkbook().getNumExFormats()]; short[] newPos = new short[workbook.getWorkbook().getNumExFormats()];
boolean[] isUsed = new boolean[newPos.length]; boolean[] isUsed = new boolean[newPos.length];
boolean[] zapRecords = new boolean[newPos.length]; boolean[] zapRecords = new boolean[newPos.length];

// to speed up the optimisation for workbooks with a large number of
// styles we perform the isUserDefined() check only once as it is
// costly according to some profiling
boolean[] userDefined = new boolean[newPos.length];

// Get each style record, so we can do deletes
// without getting confused
ExtendedFormatRecord[] xfrs = new ExtendedFormatRecord[newPos.length];

for(int i=0; i<newPos.length; i++) { for(int i=0; i<newPos.length; i++) {
isUsed[i] = false; isUsed[i] = false;
newPos[i] = (short)i; newPos[i] = (short)i;
zapRecords[i] = false; zapRecords[i] = false;
}


// Get each style record, so we can do deletes
// without getting confused
ExtendedFormatRecord[] xfrs = new ExtendedFormatRecord[newPos.length];
for(int i=0; i<newPos.length; i++) {
xfrs[i] = workbook.getWorkbook().getExFormatAt(i);
userDefined[i] = isUserDefined(workbook, i);

xfrs[i] = workbook.getWorkbook().getExFormatAt(i);
} }


// Loop over each style, seeing if it is the same // Loop over each style, seeing if it is the same
// Check this one for being a duplicate // Check this one for being a duplicate
// of an earlier one // of an earlier one
int earlierDuplicate = -1; int earlierDuplicate = -1;
for (int j = 0; j < i && earlierDuplicate == -1; j++) {
for (int j = 0; j < i; j++) {
ExtendedFormatRecord xfCheck = workbook.getWorkbook().getExFormatAt(j); ExtendedFormatRecord xfCheck = workbook.getWorkbook().getExFormatAt(j);
if (xfCheck.equals(xfrs[i]) && if (xfCheck.equals(xfrs[i]) &&
// newer duplicate user defined styles
!isUserDefined(workbook, j)) {
// never duplicate user defined styles
!userDefined[j]) {
earlierDuplicate = j; earlierDuplicate = j;
break;
} }
} }



BIN
test-data/spreadsheet/styles-3563.xls View File


Loading…
Cancel
Save