CardStorageReader uses a threadPool to load all cards in several threads, got 3x faster execution on i7-2600.

This commit is contained in:
Maxmtg
2013-03-20 21:06:12 +00:00
parent 8f35ae4961
commit 1fb5479e4f
5 changed files with 145 additions and 69 deletions

View File

@@ -33,8 +33,6 @@ import forge.card.mana.ManaCost;
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $ * @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
*/ */
public final class CardRules implements ICardCharacteristics { public final class CardRules implements ICardCharacteristics {
private final static EditionCollection editions = new EditionCollection(); // create a copy here, Singletons.model... is not initialized yet.
private final CardSplitType splitType; private final CardSplitType splitType;
private final ICardFace mainPart; private final ICardFace mainPart;
private final ICardFace otherPart; private final ICardFace otherPart;
@@ -51,7 +49,7 @@ public final class CardRules implements ICardCharacteristics {
//System.out.print(faces[0].getName()); //System.out.print(faces[0].getName());
for (Entry<String, CardInSet> cs : sets.entrySet()) { for (Entry<String, CardInSet> cs : sets.entrySet()) {
if( editions.get(cs.getKey()) != null ) if( CardRulesReader.editions.get(cs.getKey()) != null )
setsPrinted.put(cs.getKey(), cs.getValue()); setsPrinted.put(cs.getKey(), cs.getValue());
} }

View File

@@ -38,6 +38,7 @@ import forge.card.mana.ManaCostShard;
* @version $Id$ * @version $Id$
*/ */
public class CardRulesReader { public class CardRulesReader {
public final static EditionCollection editions = new EditionCollection(); // create a copy here, Singletons.model... is not initialized yet.
// fields to build // fields to build
private CardFace[] faces = new CardFace[] { null, null }; private CardFace[] faces = new CardFace[] { null, null };

View File

@@ -27,13 +27,21 @@ import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.apache.commons.lang.time.StopWatch;
import forge.card.CardRules; import forge.card.CardRules;
import forge.card.CardRulesReader; import forge.card.CardRulesReader;
import forge.control.FControl;
import forge.error.BugReporter; import forge.error.BugReporter;
import forge.gui.toolbox.FProgressBar; import forge.gui.toolbox.FProgressBar;
import forge.util.FileUtil; import forge.util.FileUtil;
@@ -54,19 +62,12 @@ public class CardStorageReader {
/** Default charset when loading from files. */ /** Default charset when loading from files. */
public static final String DEFAULT_CHARSET_NAME = "US-ASCII"; public static final String DEFAULT_CHARSET_NAME = "US-ASCII";
/** Special value for estimatedFilesRemaining. */
private static final int UNKNOWN_NUMBER_OF_FILES_REMAINING = -1;
private transient File cardsfolder; private transient File cardsfolder;
private transient ZipFile zip; private transient ZipFile zip;
private transient Charset charset; private transient Charset charset;
private transient CardRulesReader rulesReader; private transient CardRulesReader rulesReader;
private transient Enumeration<? extends ZipEntry> zipEnum;
private transient long estimatedFilesRemaining = CardStorageReader.UNKNOWN_NUMBER_OF_FILES_REMAINING;
// 8/18/11 10:56 PM // 8/18/11 10:56 PM
@@ -108,17 +109,7 @@ public class CardStorageReader {
try { try {
this.zip = new ZipFile(zipFile); this.zip = new ZipFile(zipFile);
} catch (final Exception exn) { } catch (final Exception exn) {
System.err.println("Error reading zip file \"" System.err.printf("Error reading zip file \"%s\": %s. Defaulting to txt files in \"%s\".%n", zipFile.getAbsolutePath(), exn, theCardsFolder.getAbsolutePath());
// Braids on
// 8/18/11 10:53
// PM
+ zipFile.getAbsolutePath() + "\": " + exn + ". " + "Defaulting to txt files in \""
+ theCardsFolder.getAbsolutePath() + "\".");
}
if (this.zip != null) {
this.zipEnum = this.zip.entries();
this.estimatedFilesRemaining = this.zip.size();
} }
} }
@@ -126,6 +117,19 @@ public class CardStorageReader {
} // CardReader() } // CardReader()
private final List<CardRules> loadCardsInRange(final List<File> files, int from, int to) {
CardRulesReader rulesReader = new CardRulesReader();
List<CardRules> result = new ArrayList<CardRules>();
for(int i = from; i < to; i++) {
File cardTxtFile = files.get(i);
if (cardTxtFile.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION))
result.add(this.loadCard(rulesReader, cardTxtFile));
}
return result;
}
/** /**
* Starts reading cards into memory until the given card is found. * Starts reading cards into memory until the given card is found.
@@ -143,41 +147,18 @@ public class CardStorageReader {
// Iterate through txt files or zip archive. // Iterate through txt files or zip archive.
// Report relevant numbers to progress monitor model. // Report relevant numbers to progress monitor model.
if (this.zip == null) { if (this.zip == null) {
List<File> allFiles = new ArrayList<File>(); result = loadAllCardsFromFolder(barProgress);
if (this.estimatedFilesRemaining == CardStorageReader.UNKNOWN_NUMBER_OF_FILES_REMAINING) {
fillFilesArray(allFiles, this.cardsfolder);
this.estimatedFilesRemaining = allFiles.size();
}
if (barProgress != null) {
barProgress.setMaximum((int) this.estimatedFilesRemaining);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
barProgress.setDescription("Loading card data: ");
}
});
}
for (final File cardTxtFile : allFiles) {
if (!cardTxtFile.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION)) {
barProgress.increment();
continue;
}
//System.out.println(cardTxtFile.getName());
result.add(this.loadCard(cardTxtFile));
barProgress.increment();
} // endfor
} else { } else {
barProgress.setMaximum((int) this.estimatedFilesRemaining);
Enumeration<? extends ZipEntry> zipEnum = this.zip.entries();
int estimatedFilesRemaining = this.zip.size();
barProgress.setMaximum(estimatedFilesRemaining);
ZipEntry entry; ZipEntry entry;
// zipEnum was initialized in the constructor. // zipEnum was initialized in the constructor.
while (this.zipEnum.hasMoreElements()) { while (zipEnum.hasMoreElements()) {
entry = this.zipEnum.nextElement(); entry = zipEnum.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION)) { if (entry.isDirectory() || !entry.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION)) {
barProgress.increment(); barProgress.increment();
@@ -189,9 +170,74 @@ public class CardStorageReader {
} }
} // endif } // endif
barProgress.setPercentMode(false);
return result; return result;
} // loadCardsUntilYouFind(String) } // loadCardsUntilYouFind(String)
private List<CardRules> loadAllCardsFromFolder(final FProgressBar barProgress) {
List<CardRules> result = new ArrayList<CardRules>();
StopWatch sw = new StopWatch();
sw.start();
final List<File> allFiles = new ArrayList<File>();
fillFilesArray(allFiles, this.cardsfolder);
long estimatedFilesRemaining = allFiles.size();
final int NUMBER_OF_PARTS = 20;
sw.split();
if (barProgress != null) {
barProgress.setMaximum(NUMBER_OF_PARTS);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
barProgress.setPercentMode(true);
barProgress.setDescription("Loading card data: ");
}
});
}
final CountDownLatch cdl = new CountDownLatch(NUMBER_OF_PARTS);
int totalFiles = allFiles.size();
int filesPerPart = totalFiles / NUMBER_OF_PARTS;
final List<Callable<List<CardRules>>> tasks = new ArrayList<Callable<List<CardRules>>>();
for (int iPart = 0; iPart < NUMBER_OF_PARTS; iPart++) {
final int from = iPart * filesPerPart;
final int till = iPart == NUMBER_OF_PARTS - 1 ? totalFiles : from + filesPerPart;
tasks.add(new Callable<List<CardRules>>() {
@Override
public List<CardRules> call() throws Exception{
List<CardRules> res = loadCardsInRange(allFiles, from, till);
barProgress.increment();
cdl.countDown();
return res;
}
});
}
try {
final ExecutorService executor = FControl.getComputingPool(0.5f);
final List<Future<List<CardRules>>> parts = executor.invokeAll(tasks);
executor.shutdown();
cdl.await();
for(Future<List<CardRules>> pp : parts) {
result.addAll(pp.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
long fs = sw.getSplitTime();
sw.stop();
System.out.println("Processed " + estimatedFilesRemaining + " file objects in " + sw.getTime() + " ms, (folder scan took " + fs + " ms.)");
return result;
}
/** /**
* TODO: Write javadoc for this method. * TODO: Write javadoc for this method.
* @param allFiles * @param allFiles
@@ -225,13 +271,13 @@ public class CardStorageReader {
* *
* @return the card loaded from the stream * @return the card loaded from the stream
*/ */
protected final CardRules loadCard(final InputStream inputStream) { protected final CardRules loadCard(CardRulesReader reader, final InputStream inputStream) {
this.rulesReader.reset(); reader.reset();
InputStreamReader isr = new InputStreamReader(inputStream, this.charset); InputStreamReader isr = new InputStreamReader(inputStream, this.charset);
List<String> allLines = FileUtil.readAllLines(isr, true); List<String> allLines = FileUtil.readAllLines(isr, true);
return rulesReader.readCard(allLines); return reader.readCard(allLines);
} }
/** /**
@@ -242,11 +288,11 @@ public class CardStorageReader {
* *
* @return a new Card instance * @return a new Card instance
*/ */
protected final CardRules loadCard(final File pathToTxtFile) { protected final CardRules loadCard(final CardRulesReader reader, final File pathToTxtFile) {
FileInputStream fileInputStream = null; FileInputStream fileInputStream = null;
try { try {
fileInputStream = new FileInputStream(pathToTxtFile); fileInputStream = new FileInputStream(pathToTxtFile);
return this.loadCard(fileInputStream); return this.loadCard(reader, fileInputStream);
} catch (final FileNotFoundException ex) { } catch (final FileNotFoundException ex) {
BugReporter.reportException(ex, "File \"%s\" exception", pathToTxtFile.getAbsolutePath()); BugReporter.reportException(ex, "File \"%s\" exception", pathToTxtFile.getAbsolutePath());
throw new RuntimeException("CardReader : run error -- file exception -- filename is " throw new RuntimeException("CardReader : run error -- file exception -- filename is "
@@ -273,7 +319,7 @@ public class CardStorageReader {
InputStream zipInputStream = null; InputStream zipInputStream = null;
try { try {
zipInputStream = this.zip.getInputStream(entry); zipInputStream = this.zip.getInputStream(entry);
return this.loadCard(zipInputStream); return this.loadCard(rulesReader, zipInputStream);
} catch (final IOException exn) { } catch (final IOException exn) {
throw new RuntimeException(exn); throw new RuntimeException(exn);
// PM // PM

View File

@@ -25,6 +25,8 @@ import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import java.awt.event.WindowListener;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
@@ -83,6 +85,8 @@ public enum FControl {
DRAFTING_PROCESS DRAFTING_PROCESS
} }
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private final SoundSystem soundSystem = new SoundSystem(); private final SoundSystem soundSystem = new SoundSystem();
/** /**
@@ -104,6 +108,8 @@ public enum FControl {
} }
}; };
// "Close" button override during match // "Close" button override during match
this.waConcede = new WindowAdapter() { this.waConcede = new WindowAdapter() {
@Override @Override
@@ -307,4 +313,13 @@ public enum FControl {
public SoundSystem getSoundSystem() { public SoundSystem getSoundSystem() {
return soundSystem; return soundSystem;
} }
public ExecutorService getThreadPool() {
return threadPool;
}
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
public final static ExecutorService getComputingPool(float loadFactor) {
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
}
} }

View File

@@ -22,6 +22,8 @@ public class FProgressBar extends JProgressBar {
private boolean showETA = true; private boolean showETA = true;
private boolean showCount = true; private boolean showCount = true;
private boolean percentMode = false;
/** */ /** */
public FProgressBar() { public FProgressBar() {
super(); super();
@@ -40,6 +42,14 @@ public class FProgressBar extends JProgressBar {
this.setString(s0); this.setString(s0);
} }
private final Runnable barIncrementor = new Runnable() {
@Override
public void run() {
FProgressBar.this.setValue(tempVal);
FProgressBar.this.setString(message);
}
};
/** Increments bar, thread safe. Calculations executed on separate thread. */ /** Increments bar, thread safe. Calculations executed on separate thread. */
public void increment() { public void increment() {
//GuiUtils.checkEDT("FProgressBar$increment", false); //GuiUtils.checkEDT("FProgressBar$increment", false);
@@ -48,7 +58,11 @@ public class FProgressBar extends JProgressBar {
// String.format leads to StringBuilder anyway. Direct calls will be faster // String.format leads to StringBuilder anyway. Direct calls will be faster
StringBuilder sb = new StringBuilder(desc); StringBuilder sb = new StringBuilder(desc);
if (showCount) { if (showCount) {
sb.append(" ").append(tempVal).append(" of ").append(getMaximum()); sb.append(" ");
if (percentMode)
sb.append(100 * tempVal / getMaximum()).append("%");
else
sb.append(tempVal).append(" of ").append(getMaximum());
} }
if (showETA) { if (showETA) {
@@ -58,13 +72,7 @@ public class FProgressBar extends JProgressBar {
message = sb.toString(); message = sb.toString();
// When calculations finished; EDT can be used. // When calculations finished; EDT can be used.
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(barIncrementor);
@Override
public void run() {
FProgressBar.this.setValue(tempVal);
FProgressBar.this.setString(message);
}
});
} }
/** Resets the various values required for this class. Must be called from EDT. */ /** Resets the various values required for this class. Must be called from EDT. */
@@ -96,4 +104,12 @@ public class FProgressBar extends JProgressBar {
etaSecs = (int) ((this.getMaximum() - v0) * timePerUnit) / 1000; etaSecs = (int) ((this.getMaximum() - v0) * timePerUnit) / 1000;
} }
public boolean isPercentMode() {
return percentMode;
}
public void setPercentMode(boolean value) {
this.percentMode = value;
}
} }