diff --git a/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java b/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java index 29ff4dc920fb22f8f92d94ae19b93872ac4ef40e..1c34ee22611baa3730624531cf98c76d0273b144 100644 --- a/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java +++ b/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java @@ -85,7 +85,7 @@ public class VectorQuantizationBenchmark extends BenchmarkBase { } final int[][] refPlaneData = getPlaneVectors(plane, qVector); - LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(refPlaneData, codebookSize); + LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(refPlaneData, codebookSize, 1); final LBGResult vqResult = vqInitializer.findOptimalCodebook(); quantizer = new VectorQuantizer(vqResult.getCodebook()); System.out.println("Created reference quantizer."); @@ -106,7 +106,7 @@ public class VectorQuantizationBenchmark extends BenchmarkBase { if (!hasGeneralQuantizer) { - LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeData, codebookSize); + LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeData, codebookSize,1); LBGResult vqResult = vqInitializer.findOptimalCodebook(); quantizer = new VectorQuantizer(vqResult.getCodebook()); System.out.println("Created plane quantizer."); diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java index fe3d027130aafc6d51284e3302220e90a0c7036f..ffb6c22ea763ffcc270cc61e09251e3d2b4c6c78 100644 --- a/src/main/java/azgracompress/compression/VQImageCompressor.java +++ b/src/main/java/azgracompress/compression/VQImageCompressor.java @@ -29,7 +29,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm * @return Trained vector quantizer with codebook of set size. */ private VectorQuantizer trainVectorQuantizerFromPlaneVectors(final int[][] planeVectors) { - LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeVectors, codebookSize); + LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeVectors, codebookSize, options.getWorkerCount()); LBGResult vqResult = vqInitializer.findOptimalCodebook(false); return new VectorQuantizer(vqResult.getCodebook()); } @@ -185,7 +185,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm throw new ImageCompressionException("Failed to load reference image data.", e); } } else { - Log(options.hasPlaneRangeSet() ? "Loading plane range data." : "Loading all planes data."); + Log(options.hasPlaneRangeSet() ? "VQ: Loading plane range data." : "VQ: Loading all planes data."); final int[] planeIndices = getPlaneIndicesForCompression(); final int chunkCountPerPlane = Chunk2D.calculateRequiredChunkCountPerPlane( @@ -198,7 +198,6 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm int[][] planeVectors; int planeCounter = 0; for (final int planeIndex : planeIndices) { - Log("Loading plane %d vectors", planeIndex); try { planeVectors = loadPlaneQuantizationVectors(planeIndex); assert (planeVectors.length == chunkCountPerPlane) : "Wrong chunk count per plane"; @@ -220,7 +219,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm public void trainAndSaveCodebook() throws ImageCompressionException { final int[][] trainingData = loadConfiguredPlanesData(); - LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(trainingData, codebookSize); + LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(trainingData, codebookSize, options.getWorkerCount()); Log("Starting LBG optimization."); LBGResult lbgResult = vqInitializer.findOptimalCodebook(options.isVerbose()); Log("Learned the optimal codebook."); diff --git a/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java b/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java index b850a9e6e17df4ccab37bbe2b3e5349a63065aa5..09675021c21898252690de7332e412b83ea389e8 100644 --- a/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java +++ b/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java @@ -122,13 +122,8 @@ public class LloydMaxU16ScalarQuantization { boundaryPoints, codebookSize); workers[wId] = new Thread(runnables[wId]); - } - - for (int wId = 0; wId < workerCount; wId++) { workers[wId].start(); } - - try { for (int wId = 0; wId < workerCount; wId++) { workers[wId].join(); diff --git a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java index d851c6e5e2575f2614a9e9954ad17a4adc1b55ce..3c6892e785927074b682ef9dce3915354bdc4630 100644 --- a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java +++ b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java @@ -17,14 +17,16 @@ public class LBGVectorQuantizer { private final VectorDistanceMetric metric = VectorDistanceMetric.Euclidean; boolean verbose = false; + private final int workerCount; - public LBGVectorQuantizer(final int[][] trainingVectors, final int codebookSize) { + public LBGVectorQuantizer(final int[][] trainingVectors, final int codebookSize, final int workerCount) { assert (trainingVectors.length > 0) : "No training vectors provided"; this.trainingVectors = trainingVectors; this.vectorSize = trainingVectors[0].length; this.codebookSize = codebookSize; + this.workerCount = workerCount; } public LBGResult findOptimalCodebook() { @@ -212,7 +214,6 @@ public class LBGVectorQuantizer { double previousDistortion = Double.POSITIVE_INFINITY; - int iteration = 1; Stopwatch innerLoopStopwatch = new Stopwatch("LBG inner loop"); Stopwatch findingClosestEntryStopwatch = new Stopwatch("FindingClosestEntry"); Stopwatch distCalcStopwatch = new Stopwatch("DistortionCalc"); @@ -222,37 +223,20 @@ public class LBGVectorQuantizer { innerLoopStopwatch.restart(); // Step 1 - // Speedup - speed the finding of the closest codebook entry. - findingClosestEntryStopwatch.restart(); - for (final int[] trainingVec : trainingVectors) { - double minDist = Double.POSITIVE_INFINITY; - LearningCodebookEntry closestEntry = null; - for (LearningCodebookEntry entry : codebook) { - double entryDistance = VectorQuantizer.distanceBetweenVectors(entry.getVector(), - trainingVec, - metric); - if (entryDistance < minDist) { - minDist = entryDistance; - closestEntry = entry; - } - } + findingClosestEntryStopwatch.restart(); + + assignVectorsToClosestEntry(codebook); - if (closestEntry != null) { - closestEntry.addTrainingVector(trainingVec, minDist); - } else { - assert (false) : "Did not found closest entry."; - System.err.println("Did not found closest entry."); - } - } findingClosestEntryStopwatch.stop(); + System.out.println(findingClosestEntryStopwatch); - fixEmptyStopwatch.restart(); + // fixEmptyStopwatch.restart(); fixEmptyEntries(codebook, verbose); - fixEmptyStopwatch.stop(); - System.out.println(fixEmptyStopwatch); + // fixEmptyStopwatch.stop(); + // System.out.println(fixEmptyStopwatch); // Step 2 distCalcStopwatch.restart(); @@ -285,7 +269,7 @@ public class LBGVectorQuantizer { } innerLoopStopwatch.stop(); - System.out.println(innerLoopStopwatch); + // System.out.println(innerLoopStopwatch); System.out.println("================"); } @@ -293,6 +277,83 @@ public class LBGVectorQuantizer { System.out.println(totalLbgFun); } + private void assignVectorsToClosestEntry(ArrayList<LearningCodebookEntry> codebook) { + if (workerCount > 1) { + Thread[] workers = new Thread[workerCount]; + + final int workSize = trainingVectors.length / workerCount; + + for (int wId = 0; wId < workerCount; wId++) { + final int fromIndex = wId * workSize; + final int toIndex = (wId == workerCount - 1) ? trainingVectors.length : (workSize + (wId * workSize)); + + workers[wId] = new Thread(() -> { + double minimalDistance, entryDistance; + for (int vecIndex = fromIndex; vecIndex < toIndex; vecIndex++) { + minimalDistance = Double.POSITIVE_INFINITY; + LearningCodebookEntry closestEntry = null; + + for (LearningCodebookEntry entry : codebook) { + entryDistance = VectorQuantizer.distanceBetweenVectors(entry.getVector(), + trainingVectors[vecIndex], + metric); + + if (entryDistance < minimalDistance) { + minimalDistance = entryDistance; + closestEntry = entry; + } + } + + if (closestEntry != null) { + closestEntry.addTrainingVector(trainingVectors[vecIndex], + minimalDistance); + } else { + assert (false) : "Did not found closest entry."; + System.err.println("Did not found closest entry."); + } + } + }); + workers[wId].start(); + } + + try { + for (int wId = 0; wId < workerCount; wId++) { + workers[wId].join(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + assert (false) : "Failed parallel join"; + } + + } else { + + ////////////////////////////////////////////////////////////////////////// + // Speedup - speed the finding of the closest codebook entry. + for (final int[] trainingVec : trainingVectors) { + double minDist = Double.POSITIVE_INFINITY; + LearningCodebookEntry closestEntry = null; + + for (LearningCodebookEntry entry : codebook) { + double entryDistance = VectorQuantizer.distanceBetweenVectors(entry.getVector(), + trainingVec, + metric); + + if (entryDistance < minDist) { + minDist = entryDistance; + closestEntry = entry; + } + } + + if (closestEntry != null) { + closestEntry.addTrainingVector(trainingVec, minDist); + } else { + assert (false) : "Did not found closest entry."; + System.err.println("Did not found closest entry."); + } + } + } + } + private void fixEmptyEntries(ArrayList<LearningCodebookEntry> codebook, final boolean verbose) { LearningCodebookEntry emptyEntry = null; @@ -318,9 +379,10 @@ public class LBGVectorQuantizer { private void fixSingleEmptyEntry(ArrayList<LearningCodebookEntry> codebook, final LearningCodebookEntry emptyEntry, final boolean verbose) { - if (verbose) { - System.out.println("******** FOUND EMPTY ENTRY ********"); - } + // if (verbose) { + // System.out.println("******** FOUND EMPTY ENTRY ********"); + // } + // Remove empty entry from codebook. codebook.remove(emptyEntry); diff --git a/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java b/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java index decb5c7bd536cc6559d9ac430dfe18fa02300390..38ed138d3d2a6a137efa214a06fc2c75a5e7e360 100644 --- a/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java +++ b/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java @@ -42,7 +42,7 @@ public class LearningCodebookEntry extends CodebookEntry { this.trainingVectors = trainingVectors; } - public void addTrainingVector(final int[] trainingVec, final double vecDist) { + public synchronized void addTrainingVector(final int[] trainingVec, final double vecDist) { trainingVectors.add(trainingVec); trainingVectorsDistances.add(vecDist); }