This is an automated email from the ASF dual-hosted git repository.

mboehm7 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/systemds.git


The following commit(s) were added to refs/heads/main by this push:
     new 69ae571988 [SYSTEMDS-3552] Cleanup and perf tests DCSR sparse block
69ae571988 is described below

commit 69ae5719883ae47ab2b6da704ca255737533655d
Author: Jaybit0 <[email protected]>
AuthorDate: Tue Jan 23 18:12:09 2024 +0100

    [SYSTEMDS-3552] Cleanup and perf tests DCSR sparse block
    
    Closes #1989.
---
 docs/performance/SparseFormats.md                  |  73 +++++++
 .../apache/sysds/runtime/data/SparseBlockCOO.java  |  15 ++
 .../apache/sysds/runtime/data/SparseBlockCSR.java  |  15 ++
 .../apache/sysds/runtime/data/SparseBlockDCSR.java |  23 ++-
 .../apache/sysds/runtime/data/SparseBlockMCSR.java |  29 ++-
 .../java/org/apache/sysds/performance/Main.java    | 134 +++++++++++++
 .../performance/matrix/MatrixMulPerformance.java   | 195 ++++++++++++++++++
 .../sysds/performance/matrix/MatrixStorage.java    | 219 +++++++++++++++++++++
 8 files changed, 698 insertions(+), 5 deletions(-)

diff --git a/docs/performance/SparseFormats.md 
b/docs/performance/SparseFormats.md
new file mode 100644
index 0000000000..bf81b9019f
--- /dev/null
+++ b/docs/performance/SparseFormats.md
@@ -0,0 +1,73 @@
+<!--
+{% comment %}
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+-->
+
+# Sparse block performance comparison
+
+This file contains a short evaluation of different sparse blocks. We included 
one dense block (DenseBlockFP64) as a baseline.
+
+## Execution time
+We measured the performance of two matrices being multiplied, both of the 
corresponding block type.
+Before each measurement, we performed `30` warmup runs (performing `30` matrix 
multiplications to random matrices without measuring).
+Then, we measured the delta nanos of the operation below `100` times and took 
the average.
+For this, we selected a block size of `1024x1024`.
+
+```java 
+MatrixBlock m3 = m1.aggregateBinaryOperations(m1, m2,
+                new MatrixBlock(), InstructionUtils.getMatMultOperator(1));
+```
+
+![](./img/PerformanceSparsity.png)
+
+## Storage
+
+The image below shows the memory consumption of each block type for different 
sparsities. This was evaluated using blocks with 1024 rows and 1024 columns.
+
+![Memory consumption](./img/MemSparsity.png)
+
+We also evaluated on different matrix shapes by fixing the number of rows and 
manipulating the number of columns.
+These tests were repeated for different sparsities.
+
+![](./img/MemCols04.png)
+
+![](./img/MemCols02.png)
+
+![](./img/MemCols01.png)
+
+![](./img/MemCols001.png)
+
+![](./img/MemCols0001.png)
+
+To get more meaningful results, the next evaluation fixes the number of 
entries in a matrix to `1024x1024` and adapts the column-to-row `ratio` (and 
vice versa) accordingly.
+We define the ratio as `cl = (1 + ratio) * rl` if `ratio >= 0`, otherwise as 
`rl = (1 - ratio) * cl`. 
+If we have for example a `ratio=1`, this means that there are twice as many 
columns as rows. If the `ratio=-1`, this means that there are twice as many 
rows as columns.
+Each test with a specific `ratio` was repeated `10` times with different 
random matrices and we took the average memory consumption.
+The images below show our results for different sparsities.
+
+![](./img/MemBalanced041.png)
+![](./img/MemBalanced042.png)
+![](./img/MemBalanced021.png)
+![](./img/MemBalanced022.png)
+![](./img/MemBalanced011.png)
+![](./img/MemBalanced012.png)
+![](./img/MemBalanced0011.png)
+![](./img/MemBalanced0012.png)
+![](./img/MemBalanced00011.png)
+![](./img/MemBalanced00012.png)
+![](./img/MemBalanced000011.png)
+![](./img/MemBalanced000012.png)
\ No newline at end of file
diff --git a/src/main/java/org/apache/sysds/runtime/data/SparseBlockCOO.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlockCOO.java
index aea34f955b..124c942122 100644
--- a/src/main/java/org/apache/sysds/runtime/data/SparseBlockCOO.java
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlockCOO.java
@@ -157,6 +157,21 @@ public class SparseBlockCOO extends SparseBlock
                //robustness for long overflows
                return (long) Math.min(size, Long.MAX_VALUE);
        }
+
+       /**
+        * Computes the exact size in memory of the materialized block
+        * @return the exact size in memory
+        */
+       public long getExactSizeInMemory() {
+               //32B overhead per array, int/int/double arr in nnz
+               double size = 16 + 8;   //object + 2 int fields
+               size += MemoryEstimates.intArrayCost(_rindexes.length); 
//rindexes array (row indexes)
+               size += MemoryEstimates.intArrayCost(_cindexes.length); 
//cindexes array (column indexes)
+               size += MemoryEstimates.doubleArrayCost(_values.length); 
//values array (non-zero values)
+
+               //robustness for long overflows
+               return (long) Math.min(size, Long.MAX_VALUE);
+       }
        
        ///////////////////
        //SparseBlock implementation
diff --git a/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSR.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSR.java
index 30ea45c80d..0aff4e08bb 100644
--- a/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSR.java
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlockCSR.java
@@ -283,6 +283,21 @@ public class SparseBlockCSR extends SparseBlock
                //robustness for long overflows
                return (long) Math.min(size, Long.MAX_VALUE);
        }
+
+       /**
+        * Computes the exact size in memory of the materialized block
+        * @return the exact size in memory
+        */
+       public long getExactSizeInMemory() {
+               //32B overhead per array, int arr in nrows, int/double arr in 
nnz
+               double size = 16 + 4 + 4;                                
//object + int field + padding
+               size += MemoryEstimates.intArrayCost(_ptr.length);       //ptr 
array (row pointers)
+               size += MemoryEstimates.intArrayCost(_indexes.length);   
//indexes array (column indexes)
+               size += MemoryEstimates.doubleArrayCost(_values.length); 
//values array (non-zero values)
+
+               //robustness for long overflows
+               return (long) Math.min(size, Long.MAX_VALUE);
+       }
        
 
        /**
diff --git a/src/main/java/org/apache/sysds/runtime/data/SparseBlockDCSR.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlockDCSR.java
index fd187f3064..447781a520 100644
--- a/src/main/java/org/apache/sysds/runtime/data/SparseBlockDCSR.java
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlockDCSR.java
@@ -168,10 +168,10 @@ public class SparseBlockDCSR extends SparseBlock
                double lnnz = Math.max(INIT_CAPACITY, 
Math.ceil(sparsity*nrows*ncols));
 
                //32B overhead per array, int arr in nrows, int/double arr in 
nnz
-               double size = 16;                                               
                        // Memory overhead of the object
-               size += 4 + 4 + 4 + 4;                                          
                  // 3x int field + 0 (padding not necessary)
-               size += MemoryEstimates.intArrayCost(nrows);             // 
rowidx array (row indices)
-               size += MemoryEstimates.intArrayCost(nrows+1); // rowptr array 
(row pointers)
+               double size = 16;                                    // Memory 
overhead of the object
+               size += 4 + 4 + 4 + 4;                               // 3x int 
field + 0 (padding not necessary)
+               size += MemoryEstimates.intArrayCost(nrows);         // rowidx 
array (row indices)
+               size += MemoryEstimates.intArrayCost(nrows+1);       // rowptr 
array (row pointers)
                size += MemoryEstimates.intArrayCost((long) lnnz);   // colidx 
array (column indexes)
                size += MemoryEstimates.doubleArrayCost((long) lnnz);// values 
array (non-zero values)
 
@@ -179,6 +179,21 @@ public class SparseBlockDCSR extends SparseBlock
                return (long) Math.min(size, Long.MAX_VALUE);
        }
 
+       /**
+        * Computes the exact size in memory of the materialized block
+        * @return the exact size in memory
+        */
+       public long getExactSizeInMemory() {
+               double size = 16;
+               size += 4 + 4 + 4 + 4;
+               size += MemoryEstimates.intArrayCost(_rowidx.length);
+               size += MemoryEstimates.intArrayCost(_rowptr.length);
+               size += MemoryEstimates.intArrayCost(_colidx.length);
+               size += MemoryEstimates.doubleArrayCost(_values.length);
+
+               return (long) Math.min(size, Long.MAX_VALUE);
+       }
+
        ///////////////////
        //SparseBlock implementation
 
diff --git a/src/main/java/org/apache/sysds/runtime/data/SparseBlockMCSR.java 
b/src/main/java/org/apache/sysds/runtime/data/SparseBlockMCSR.java
index c7e79b8dbc..f6fa157448 100644
--- a/src/main/java/org/apache/sysds/runtime/data/SparseBlockMCSR.java
+++ b/src/main/java/org/apache/sysds/runtime/data/SparseBlockMCSR.java
@@ -124,7 +124,7 @@ public class SparseBlockMCSR extends SparseBlock
                double size = 16; //object
                size += MemoryEstimates.objectArrayCost(nrows); //references
                long sparseRowSize = 16; // object
-               sparseRowSize += 4*4; // 3 integers + padding
+               sparseRowSize += 2*4; // 2 integers + padding
                sparseRowSize += MemoryEstimates.intArrayCost(0);
                sparseRowSize += MemoryEstimates.doubleArrayCost(0);
                sparseRowSize += 12*Math.max(1, cnnz); //avoid bias by down 
cast for ultra-sparse
@@ -134,6 +134,33 @@ public class SparseBlockMCSR extends SparseBlock
                return (long) Math.min(size, Long.MAX_VALUE);
        }
 
+       /**
+        * Computes the exact size in memory of the materialized block
+        * @return the exact size in memory
+        */
+       public long getExactSizeInMemory() {
+               double size = 16; //object
+               size += MemoryEstimates.objectArrayCost(_rows.length); 
//references
+
+               for (SparseRow sr : _rows) {
+                       if (sr == null)
+                               continue;
+                       long sparseRowSize = 16; // object
+                       if( sr instanceof SparseRowScalar )
+                               sparseRowSize += 12;
+                       else { //SparseRowVector
+                               sparseRowSize += 2*4; // 2 integers
+                               sparseRowSize += 
MemoryEstimates.intArrayCost(0);
+                               sparseRowSize += 
MemoryEstimates.doubleArrayCost(0);
+                               sparseRowSize += 
12*((SparseRowVector)sr).capacity();
+                       }
+                       size += sparseRowSize; //sparse rows
+               }
+
+               // robustness for long overflows
+               return (long) Math.min(size, Long.MAX_VALUE);
+       }
+
        ///////////////////
        //SparseBlock implementation
 
diff --git a/src/test/java/org/apache/sysds/performance/Main.java 
b/src/test/java/org/apache/sysds/performance/Main.java
index 185a43e2c3..2fed0d7144 100644
--- a/src/test/java/org/apache/sysds/performance/Main.java
+++ b/src/test/java/org/apache/sysds/performance/Main.java
@@ -30,6 +30,9 @@ import 
org.apache.sysds.performance.generators.FrameTransformFile;
 import org.apache.sysds.performance.generators.GenMatrices;
 import org.apache.sysds.performance.generators.IGenerate;
 import org.apache.sysds.performance.generators.MatrixFile;
+import org.apache.sysds.performance.matrix.MatrixMulPerformance;
+import org.apache.sysds.performance.matrix.MatrixStorage;
+import org.apache.sysds.runtime.data.SparseBlock;
 import org.apache.sysds.runtime.frame.data.FrameBlock;
 import org.apache.sysds.runtime.matrix.data.MatrixBlock;
 import org.apache.sysds.runtime.util.CommonThreadPool;
@@ -100,6 +103,18 @@ public class Main {
                        case 16:
                                run16(args);
                                break;
+                       case 1000:
+                               run1000(args);
+                               break;
+                       case 1001:
+                               run1001(args);
+                               break;
+                       case 1002:
+                               run1002(args);
+                               break;
+                       case 1003:
+                               run1003(args);
+                               break;
                        default:
                                break;
                }
@@ -185,6 +200,125 @@ public class Main {
                System.out.println(mb);
        }
 
+       private static void run1000(String[] args) {
+               MatrixMulPerformance perf;
+               if (args.length < 3) {
+                       perf = new MatrixMulPerformance();
+               } else {
+                       // ... <rl> <cl> [resolution] [maxSparsity] 
[resolution] [warmupRuns] [repetitions]
+                       int rl = Integer.parseInt(args[1]);
+                       int cl = Integer.parseInt(args[2]);
+                       int resolution = 18;
+                       float maxSparsity = .4f;
+                       int warmupRuns = 30;
+                       int repetitions = 100;
+
+                       if (args.length > 3)
+                               resolution = Integer.parseInt(args[3]);
+                       if (args.length > 4)
+                               maxSparsity = Float.parseFloat(args[4]);
+                       if (args.length > 5)
+                               warmupRuns = Integer.parseInt(args[5]);
+                       if (args.length > 6)
+                               repetitions = Integer.parseInt(args[6]);
+
+                       perf = new MatrixMulPerformance(rl, cl, warmupRuns, 
repetitions, resolution, maxSparsity, 2f);
+               }
+
+               perf.testSparseFormat(null, null);
+               perf.testSparseFormat(SparseBlock.Type.MCSR, 
SparseBlock.Type.MCSR);
+               perf.testSparseFormat(SparseBlock.Type.CSR, 
SparseBlock.Type.CSR);
+               perf.testSparseFormat(SparseBlock.Type.COO, 
SparseBlock.Type.COO);
+               perf.testSparseFormat(SparseBlock.Type.DCSR, 
SparseBlock.Type.DCSR);
+       }
+
+       private static void run1001(String[] args) {
+               // ... [rl] [cl] [repetitions] [resolution] [maxSparsity]
+               MatrixStorage ms;
+               int rl = 1024;
+               int cl = 1024;
+               int repetitions = 10;
+               int resolution = 18;
+               float maxSparsity = 0.4f;
+
+               if (args.length > 1)
+                       rl = Integer.parseInt(args[1]);
+               if (args.length > 2)
+                       cl = Integer.parseInt(args[2]);
+               if (args.length > 3)
+                       repetitions = Integer.parseInt(args[3]);
+               if (args.length > 4)
+                       resolution = Integer.parseInt(args[4]);
+               if (args.length > 5)
+                       maxSparsity = Float.parseFloat(args[5]);
+
+               ms = new MatrixStorage(resolution, 2f, maxSparsity);
+
+               ms.testSparseFormat(null, rl, cl, repetitions);
+               ms.testSparseFormat(SparseBlock.Type.MCSR, rl, cl, repetitions);
+               ms.testSparseFormat(SparseBlock.Type.CSR, rl, cl, repetitions);
+               ms.testSparseFormat(SparseBlock.Type.COO, rl, cl, repetitions);
+               ms.testSparseFormat(SparseBlock.Type.DCSR, rl, cl, repetitions);
+       }
+
+       private static void run1002(String[] args) {
+               // ... [sparsity] [rl] [minCl] [maxCl] [resolution] 
[repetitions]
+               MatrixStorage ms = new MatrixStorage();
+               float sparsity = 0.1f;
+               int rl = 1024;
+               int minCl = 50;
+               int maxCl = 2048;
+               int resolution = 21;
+               int repetitions = 10;
+
+               if (args.length > 1)
+                       sparsity = Float.parseFloat(args[1]);
+               if (args.length > 2)
+                       rl = Integer.parseInt(args[2]);
+               if (args.length > 3)
+                       minCl = Integer.parseInt(args[3]);
+               if (args.length > 4)
+                       maxCl = Integer.parseInt(args[4]);
+               if (args.length > 5)
+                       resolution = Integer.parseInt(args[5]);
+               if (args.length > 6)
+                       repetitions = Integer.parseInt(args[6]);
+
+               ms.testChangingDims(null, sparsity, rl, minCl, maxCl, 
resolution, repetitions);
+               ms.testChangingDims(SparseBlock.Type.MCSR, sparsity, rl, minCl, 
maxCl, resolution, repetitions);
+               ms.testChangingDims(SparseBlock.Type.CSR, sparsity, rl, minCl, 
maxCl, resolution, repetitions);
+               ms.testChangingDims(SparseBlock.Type.COO, sparsity, rl, minCl, 
maxCl, resolution, repetitions);
+               ms.testChangingDims(SparseBlock.Type.DCSR, sparsity, rl, minCl, 
maxCl, resolution, repetitions);
+       }
+
+       private static void run1003(String[] args) {
+               // ... [sparsity] [resolution] [repetitions] [maxRowColRatio] 
[numMatrixEntries]
+               MatrixStorage ms = new MatrixStorage();
+
+               float sparsity = 0.1f;
+               int resolution = 21;
+               int repetitions = 10;
+               float maxRowColRatio = 10f;
+               int numEntries = 1024 * 1024;
+
+               if (args.length > 1)
+                       sparsity = Float.parseFloat(args[1]);
+               if (args.length > 2)
+                       resolution = Integer.parseInt(args[2]);
+               if (args.length > 3)
+                       repetitions = Integer.parseInt(args[3]);
+               if (args.length > 4)
+                       maxRowColRatio = Float.parseFloat(args[4]);
+               if (args.length > 5)
+                       numEntries = Integer.parseInt(args[5]);
+
+               ms.testBalancedDims(null, sparsity, numEntries, resolution, 
maxRowColRatio, repetitions);
+               ms.testBalancedDims(SparseBlock.Type.MCSR, sparsity, 
numEntries, resolution, maxRowColRatio, repetitions);
+               ms.testBalancedDims(SparseBlock.Type.CSR, sparsity, numEntries, 
resolution, maxRowColRatio, repetitions);
+               ms.testBalancedDims(SparseBlock.Type.COO, sparsity, numEntries, 
resolution, maxRowColRatio, repetitions);
+               ms.testBalancedDims(SparseBlock.Type.DCSR, sparsity, 
numEntries, resolution, maxRowColRatio, repetitions);
+       }
+
        public static void main(String[] args) {
                try {
                        exec(Integer.parseInt(args[0]), args);
diff --git 
a/src/test/java/org/apache/sysds/performance/matrix/MatrixMulPerformance.java 
b/src/test/java/org/apache/sysds/performance/matrix/MatrixMulPerformance.java
new file mode 100644
index 0000000000..05b91d0ebe
--- /dev/null
+++ 
b/src/test/java/org/apache/sysds/performance/matrix/MatrixMulPerformance.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.performance.matrix;
+
+import org.apache.sysds.runtime.data.SparseBlock;
+import org.apache.sysds.runtime.data.SparseBlockCOO;
+import org.apache.sysds.runtime.data.SparseBlockCSR;
+import org.apache.sysds.runtime.data.SparseBlockDCSR;
+import org.apache.sysds.runtime.data.SparseBlockMCSR;
+import org.apache.sysds.runtime.instructions.InstructionUtils;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.util.DataConverter;
+import org.apache.sysds.test.TestUtils;
+
+import java.util.Arrays;
+
+public class MatrixMulPerformance {
+
+       private final int _rl;
+       private final int _cl;
+
+       private final int warmupRuns;
+       private final int repetitions;
+       private final int resolution;
+       private final float resolutionDivisor;
+       private final float maxSparsity;
+
+       public MatrixMulPerformance() {
+               this(1024, 1024, 15, 50, 18, .4f, 2f);
+       }
+
+       public MatrixMulPerformance(int rl, int cl, int warmupRuns, int 
repetitions,
+               int resolution, float maxSparsity, float stepDivisor)
+       {
+               _rl = rl;
+               _cl = cl;
+               this.warmupRuns = warmupRuns;
+               this.repetitions = repetitions;
+               this.resolution = resolution;
+               this.resolutionDivisor = stepDivisor;
+               this.maxSparsity = maxSparsity;
+       }
+
+       private float[] sparsityProvider() {
+               float[] sparsities = new float[resolution];
+               float currentValue = maxSparsity;
+
+               for (int i = 0; i < resolution; i++) {
+                       sparsities[i] = currentValue;
+                       currentValue /= resolutionDivisor;
+               }
+
+               return sparsities;
+       }
+
+       private static String printAsPythonList(float[] list) {
+               StringBuilder sb = new StringBuilder();
+               sb.append("[");
+
+               for (float el : list)
+                       sb.append(el + ",");
+
+               if (list.length > 0)
+                       sb.deleteCharAt(sb.length() - 1);
+
+               sb.append("]");
+               return sb.toString();
+       }
+
+       private static String printAsPythonList(double[] list) {
+               StringBuilder sb = new StringBuilder();
+               sb.append("[");
+
+               for (double el : list)
+                       sb.append(el + ",");
+
+               if (list.length > 0)
+                       sb.deleteCharAt(sb.length() - 1);
+
+               sb.append("]");
+               return sb.toString();
+       }
+
+       public void testSparseFormat(SparseBlock.Type btype1, SparseBlock.Type 
btype2) {
+               float[] sparsities = this.sparsityProvider();
+               double[] avgNanosPerSparsity = new double[sparsities.length];
+               long[] results = new long[repetitions];
+               for (int sparsityIndex = 0; sparsityIndex < sparsities.length; 
sparsityIndex++) {
+                       // Warmup the JVM
+                       for (int i = 0; i < warmupRuns; i++)
+                               runSparsityEstimateTest(btype1, btype2, 
sparsities[sparsityIndex]);
+
+                       for (int i = 0; i < repetitions; i++)
+                               results[i] = runSparsityEstimateTest(btype1, 
btype2, sparsities[sparsityIndex]);
+
+                       avgNanosPerSparsity[sparsityIndex] = 
Arrays.stream(results).average().getAsDouble();
+               }
+
+               System.out.println("sparsities" + (btype1 == null ? "Dense" : 
btype1.name()) + " = " + printAsPythonList(sparsities));
+               System.out.println("avgNanos" + (btype2 == null ? "Dense" : 
btype2.name()) + " =  " + printAsPythonList(avgNanosPerSparsity));
+       }
+
+       private long runSparsityEstimateTest(SparseBlock.Type btype1, 
SparseBlock.Type btype2, float sparsity) {
+               double[][] A = TestUtils.generateTestMatrix(_rl, _cl, -10, 10, 
sparsity, 7654321);
+               double[][] B = TestUtils.generateTestMatrix(_rl, _cl, -10, 10, 
sparsity, 7654322);
+
+               MatrixBlock mbtmp1 = DataConverter.convertToMatrixBlock(A);
+               MatrixBlock mbtmp2 = DataConverter.convertToMatrixBlock(B);
+
+               MatrixBlock m1;
+               MatrixBlock m2;
+
+               if (btype1 == null && btype2 == null) {
+                       if (mbtmp1.isInSparseFormat())
+                               mbtmp1.sparseToDense();
+                       if (mbtmp2.isInSparseFormat())
+                               mbtmp2.sparseToDense();
+
+                       m1 = mbtmp1;
+                       m2 = mbtmp2;
+               } else {
+                       // Ensure that these are sparse blocks
+                       if (!mbtmp1.isInSparseFormat())
+                               mbtmp1.denseToSparse(true);
+                       if (!mbtmp2.isInSparseFormat())
+                               mbtmp2.denseToSparse(true);
+
+                       SparseBlock srtmp1 = mbtmp1.getSparseBlock();
+                       SparseBlock srtmp2 = mbtmp2.getSparseBlock();
+                       SparseBlock sblock1;
+                       SparseBlock sblock2;
+
+                       switch (btype1) {
+                               case MCSR:
+                                       sblock1 = new SparseBlockMCSR(srtmp1);
+                                       break;
+                               case CSR:
+                                       sblock1 = new SparseBlockCSR(srtmp1);
+                                       break;
+                               case COO:
+                                       sblock1 = new SparseBlockCOO(srtmp1);
+                                       break;
+                               case DCSR:
+                                       sblock1 = new SparseBlockDCSR(srtmp1);
+                                       break;
+                               default:
+                                       throw new 
IllegalArgumentException("Unknown sparse block type");
+                       }
+
+                       switch (btype2) {
+                               case MCSR:
+                                       sblock2 = new SparseBlockMCSR(srtmp2);
+                                       break;
+                               case CSR:
+                                       sblock2 = new SparseBlockCSR(srtmp2);
+                                       break;
+                               case COO:
+                                       sblock2 = new SparseBlockCOO(srtmp2);
+                                       break;
+                               case DCSR:
+                                       sblock2 = new SparseBlockDCSR(srtmp2);
+                                       break;
+                               default:
+                                       throw new 
IllegalArgumentException("Unknown sparse block type");
+                       }
+
+                       m1 = new MatrixBlock(_rl, _cl, sblock1.size(), sblock1);
+                       m2 = new MatrixBlock(_rl, _cl, sblock2.size(), sblock2);
+               }
+
+               long nanos = System.nanoTime();
+
+               MatrixBlock m3 = m1.aggregateBinaryOperations(m1, m2, 
InstructionUtils.getMatMultOperator(1));
+               m3.sum(); // forced execution
+               
+               return System.nanoTime() - nanos;
+       }
+}
diff --git 
a/src/test/java/org/apache/sysds/performance/matrix/MatrixStorage.java 
b/src/test/java/org/apache/sysds/performance/matrix/MatrixStorage.java
new file mode 100644
index 0000000000..1a911fed7b
--- /dev/null
+++ b/src/test/java/org/apache/sysds/performance/matrix/MatrixStorage.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.performance.matrix;
+
+import org.apache.sysds.runtime.data.DenseBlockFP64;
+import org.apache.sysds.runtime.data.SparseBlock;
+import org.apache.sysds.runtime.data.SparseBlockCOO;
+import org.apache.sysds.runtime.data.SparseBlockCSR;
+import org.apache.sysds.runtime.data.SparseBlockDCSR;
+import org.apache.sysds.runtime.data.SparseBlockMCSR;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.util.DataConverter;
+import org.apache.sysds.test.TestUtils;
+
+public class MatrixStorage {
+
+       private final int resolution;
+       private final float resolutionDivisor;
+       private final float maxSparsity;
+
+       public MatrixStorage() {
+               this(18, 2f, .2f);
+       }
+
+       public MatrixStorage(int resolution, float resolutionDivisor, float 
maxSparsity) {
+               this.resolution = resolution;
+               this.resolutionDivisor = resolutionDivisor;
+               this.maxSparsity = maxSparsity;
+       }
+
+       private float[] sparsityProvider() {
+               float[] sparsities = new float[resolution];
+               float currentValue = maxSparsity;
+
+               for (int i = 0; i < resolution; i++) {
+                       sparsities[i] = currentValue;
+                       currentValue /= resolutionDivisor;
+               }
+
+               return sparsities;
+       }
+
+       private int[][] dimsProvider(int rl, int maxCl, int minCl, int 
resolution) {
+               int[][] dims = new int[2][resolution];
+               for (int i = 0; i < resolution; i++) {
+                       dims[0][i] = rl;
+                       dims[1][i] = (int)(minCl + i * 
((maxCl-minCl)/((float)resolution-1)));
+               }
+
+               return dims;
+       }
+
+       int[][] balancedDimsProvider(int numEntries, float[] ratio, float qMax) 
{
+               int resolution = ratio.length;
+               int[][] dims = new int[2][resolution];
+
+               for (int i = 0; i < resolution; i++) {
+                       ratio[i] = -qMax + 2 * qMax * (i / 
((float)resolution-1));
+                       if (ratio[i] < 0) {
+                               // Then columns are bigger than rows
+                               // r * c = numEntries
+                               // r = (1 + abs(ratio[i])) * c
+                               // => numEntries = (1 + abs(ratio[i])) * c^2
+                               // => c = sqrt(numEntries / (1 + abs(ratio[i])))
+                               dims[1][i] = 
Math.round((float)Math.sqrt(numEntries / (1 - ratio[i])));
+                               dims[0][i] = Math.round(numEntries / 
(float)dims[1][i]);
+                       } else {
+                               dims[0][i] = 
Math.round((float)Math.sqrt(numEntries / (1 + ratio[i])));
+                               dims[1][i] = Math.round(numEntries / 
(float)dims[0][i]);
+                       }
+               }
+
+               return dims;
+       }
+
+       private static String printAsPythonList(float[] list) {
+               StringBuilder sb = new StringBuilder();
+               sb.append("[");
+
+               for (float el : list)
+                       sb.append(el + ",");
+
+               if (list.length > 0)
+                       sb.deleteCharAt(sb.length() - 1);
+
+               sb.append("]");
+               return sb.toString();
+       }
+
+       private static String printAsPythonList(int[] list) {
+               StringBuilder sb = new StringBuilder();
+               sb.append("[");
+
+               for (long el : list)
+                       sb.append(el + ",");
+
+               if (list.length > 0)
+                       sb.deleteCharAt(sb.length() - 1);
+
+               sb.append("]");
+               return sb.toString();
+       }
+
+       private static String printAsPythonList(long[] list) {
+               StringBuilder sb = new StringBuilder();
+               sb.append("[");
+
+               for (long el : list)
+                       sb.append(el + ",");
+
+               if (list.length > 0)
+                       sb.deleteCharAt(sb.length() - 1);
+
+               sb.append("]");
+               return sb.toString();
+       }
+
+       public void testSparseFormat(SparseBlock.Type btype, int rl, int cl, 
int repetitions) {
+               float[] sparsities = sparsityProvider();
+               long[][] results = new long[repetitions][sparsities.length];
+
+               for (int repetition = 0; repetition < repetitions; repetition++)
+                       for (int sparsityIndex = 0; sparsityIndex < 
sparsities.length; sparsityIndex++)
+                               results[repetition][sparsityIndex] = 
evaluateMemoryConsumption(btype, sparsities[sparsityIndex], rl, cl);
+
+
+               System.out.println("sparsities" + (btype == null ? "Dense" : 
btype.name()) + " = " + printAsPythonList(sparsities));
+               System.out.println("memory" + (btype == null ? "Dense" : 
btype.name()) + " =  " + printAsPythonList(buildAverage(results)));
+       }
+
+       public void testChangingDims(SparseBlock.Type btype, double sparsity, 
int rl, int minCl, int maxCl, int resolution, int repetitions) {
+               int[][] dims = dimsProvider(rl, minCl, maxCl, resolution);
+               long[][] results = new long[repetitions][resolution];
+
+               for (int repetition = 0; repetition < repetitions; repetition++)
+                       for (int dimIndex = 0; dimIndex < resolution; 
dimIndex++)
+                               results[repetition][dimIndex] = 
evaluateMemoryConsumption(btype, sparsity, dims[0][dimIndex], 
dims[1][dimIndex]);
+
+
+               System.out.println("dims" + (btype == null ? "Dense" : 
btype.name()) + " = " + printAsPythonList(dims[1]));
+               System.out.println("dimMemory" + (btype == null ? "Dense" : 
btype.name()) + " =  " + printAsPythonList(buildAverage(results)));
+       }
+
+       public void testBalancedDims(SparseBlock.Type btype, double sparsity, 
int numEntries, int resolution, float qMax /*The maximum / minimum row-column 
ratio*/, int repetitions) {
+               float[] ratios = new float[resolution];
+               int[][] dims = balancedDimsProvider(numEntries, ratios, qMax);
+               long[][] results = new long[repetitions][resolution];
+
+               for (int repetition = 0; repetition < repetitions; repetition++)
+                       for (int ratioIndex = 0; ratioIndex < resolution; 
ratioIndex++)
+                               results[repetition][ratioIndex] = 
evaluateMemoryConsumption(btype, sparsity, dims[0][ratioIndex], 
dims[1][ratioIndex]);
+
+               System.out.println("ratio" + (btype == null ? "Dense" : 
btype.name()) + " = " + printAsPythonList(ratios) + "");
+               System.out.println("ratioMemory" + (btype == null ? "Dense" : 
btype.name()) + " =  " + printAsPythonList(buildAverage(results)) + "");
+       }
+
+       private long[] buildAverage(long[][] results) {
+               long[] mResults = new long[results[0].length];
+               for (int i = 0; i < results[0].length; i++) {
+                       for (int j = 0; j < results.length; j++)
+                               mResults[i] += results[j][i];
+                       mResults[i] /= results.length;
+               }
+
+               return mResults;
+       }
+
+       private long evaluateMemoryConsumption(SparseBlock.Type btype, double 
sparsity, int rl, int cl) {
+               try
+               {
+                       if (btype == null)
+                               return Math.min(Long.MAX_VALUE, (long) 
DenseBlockFP64.estimateMemory(rl, cl));
+
+                       double[][] A = TestUtils.generateTestMatrix(rl, cl, 
-10, 10, sparsity, 7654321);
+
+                       MatrixBlock mbtmp = 
DataConverter.convertToMatrixBlock(A);
+
+                       if (!mbtmp.isInSparseFormat())
+                               mbtmp.denseToSparse(true);
+
+                       SparseBlock srtmp = mbtmp.getSparseBlock();
+                       switch (btype) {
+                               case MCSR:
+                                       SparseBlockMCSR mcsr = new 
SparseBlockMCSR(srtmp);
+                                       return mcsr.getExactSizeInMemory();
+                               case CSR:
+                                       SparseBlockCSR csr = new 
SparseBlockCSR(srtmp);
+                                       return csr.getExactSizeInMemory();
+                               case COO:
+                                       SparseBlockCOO coo = new 
SparseBlockCOO(srtmp);
+                                       return coo.getExactSizeInMemory();
+                               case DCSR:
+                                       SparseBlockDCSR dcsr = new 
SparseBlockDCSR(srtmp);
+                                       return dcsr.getExactSizeInMemory();
+                       }
+               } catch(Exception ex) {
+                       ex.printStackTrace();
+                       throw new RuntimeException(ex);
+               }
+               throw new IllegalArgumentException();
+       }
+}

Reply via email to