This is an automated email from the ASF dual-hosted git repository.
lindong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-ml.git
The following commit(s) were added to refs/heads/master by this push:
new 1a7337a [Flink 26443] Add benchmark framework
1a7337a is described below
commit 1a7337ac49da531c29b9d210b5b34174d6afa623
Author: yunfengzhou-hub <[email protected]>
AuthorDate: Wed Apr 6 22:38:00 2022 +0800
[Flink 26443] Add benchmark framework
This closes #71.
---
README.md | 6 +
flink-ml-benchmark/README.md | 196 +++++++++++++++++++++
flink-ml-benchmark/pom.xml | 105 +++++++++++
.../org/apache/flink/ml/benchmark/Benchmark.java | 126 +++++++++++++
.../apache/flink/ml/benchmark/BenchmarkResult.java | 58 ++++++
.../apache/flink/ml/benchmark/BenchmarkUtils.java | 183 +++++++++++++++++++
.../ml/benchmark/datagenerator/DataGenerator.java | 22 +--
.../datagenerator/InputDataGenerator.java | 53 ++++++
.../clustering/KMeansModelDataGenerator.java | 79 +++++++++
.../common/DenseVectorArrayGenerator.java | 116 ++++++++++++
.../datagenerator/common/DenseVectorGenerator.java | 102 +++++++++++
.../datagenerator/param/HasArraySize.java | 26 ++-
.../datagenerator/param/HasVectorDim.java | 26 ++-
.../src/main/resources/benchmark-conf.json | 49 ++++++
.../apache/flink/ml/benchmark/BenchmarkTest.java | 105 +++++++++++
.../main/java/org/apache/flink/ml/api/Stage.java | 2 +
.../param/{LongArrayParam.java => ArrayParam.java} | 24 ++-
.../apache/flink/ml/param/DoubleArrayParam.java | 2 +-
.../org/apache/flink/ml/param/FloatArrayParam.java | 2 +-
.../java/org/apache/flink/ml/param/FloatParam.java | 10 ++
.../org/apache/flink/ml/param/IntArrayParam.java | 2 +-
.../org/apache/flink/ml/param/LongArrayParam.java | 2 +-
.../java/org/apache/flink/ml/param/LongParam.java | 10 ++
.../main/java/org/apache/flink/ml/param/Param.java | 21 ++-
.../apache/flink/ml/param/StringArrayParam.java | 2 +-
.../org/apache/flink/ml/util/ReadWriteUtils.java | 119 ++++++++-----
.../java/org/apache/flink/ml/api/StageTest.java | 54 +++++-
flink-ml-dist/pom.xml | 94 ++++++++++
flink-ml-dist/src/main/assemblies/bin.xml | 80 +++++++++
flink-ml-dist/src/main/flink-ml-bin/README.txt | 32 ++++
.../main/flink-ml-bin/bin/flink-ml-benchmark.sh | 42 +++++
flink-ml-uber/pom.xml | 7 +
pom.xml | 4 +
33 files changed, 1657 insertions(+), 104 deletions(-)
diff --git a/README.md b/README.md
index 27633bc..d935484 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,12 @@ Then you will find a JAR file that contains your
application, plus any libraries
that you may have added as dependencies to the application:
`target/<artifact-id>-<version>.jar`.
+## <a name="benchmark"></a>Benchmark
+
+Flink ML provides functionalities to benchmark its machine learning algorithms.
+For detailed information, please check the [Benchmark
+Getting Started](./flink-ml-benchmark/README.md).
+
## <a name="documentation"></a>Documentation
The documentation of Flink ML is located on the website:
diff --git a/flink-ml-benchmark/README.md b/flink-ml-benchmark/README.md
new file mode 100644
index 0000000..ac57722
--- /dev/null
+++ b/flink-ml-benchmark/README.md
@@ -0,0 +1,196 @@
+# Flink ML Benchmark Getting Started
+
+This document provides instructions on how to run benchmarks on Flink ML's
+stages in a Linux/MacOS environment.
+
+## Prerequisites
+
+### Install Flink
+
+Please make sure Flink 1.14 or higher version has been installed in your local
+environment. You can refer to the [local
+installation](https://nightlies.apache.org/flink/flink-docs-master/docs/try-flink/local_installation/)
+instruction on Flink's document website for how to achieve this.
+
+### Set Up Flink Environment Variables
+
+After having installed Flink, please register `$FLINK_HOME` as an environment
+variable into your local environment.
+
+```bash
+cd ${path_to_flink}
+export FLINK_HOME=`pwd`
+```
+
+[//]: # (TODO: Add instructions to download binary distribution when release is
+ available)
+### Build Flink ML library
+
+In order to use Flink ML's CLI you need to have the latest binary distribution
+of Flink ML. You can acquire the distribution by building Flink ML's source
code
+locally with the following command.
+
+```bash
+cd ${path_to_flink_ml}
+mvn clean package -DskipTests
+cd ./flink-ml-dist/target/flink-ml-*-bin/flink-ml*/
+```
+
+### Add Flink ML binaries to Flink
+
+You need to copy Flink ML's binary distribution files to Flink's folder for
+proper initialization. Please run the following command from Flink ML's binary
+distribution's folder.
+
+```bash
+cp ./lib/*.jar $FLINK_HOME/lib/
+```
+
+## Run Benchmark Example
+
+Please start a Flink standalone session in your local environment with the
+following command.
+
+```bash
+$FLINK_HOME/bin/start-cluster.sh
+```
+
+You should be able to navigate to the web UI at
+[localhost:8081](http://localhost:8081/) to view the Flink dashboard and see
+that the cluster is up and running.
+
+Then in Flink ML's binary distribution's folder, execute the following command
+to run the default benchmarks.
+
+```bash
+./bin/flink-ml-benchmark.sh ./conf/benchmark-conf.json --output-file
results.json
+```
+
+You will notice that some Flink jobs are submitted to your Flink cluster, and
+the following information is printed out in your terminal. This means that you
+have successfully executed the default benchmarks.
+
+```
+Found benchmarks [KMeansModel-1, KMeans-1]
+...
+Benchmarks execution completed.
+Benchmark results summary:
+[ {
+ "name" : "KMeansModel-1",
+ "totalTimeMs" : 782.0,
+ "inputRecordNum" : 10000,
+ "inputThroughput" : 12787.723785166241,
+ "outputRecordNum" : 10000,
+ "outputThroughput" : 12787.723785166241
+}, {
+ "name" : "KMeans-1",
+ "totalTimeMs" : 7022.0,
+ "inputRecordNum" : 10000,
+ "inputThroughput" : 1424.0956992309884,
+ "outputRecordNum" : 1,
+ "outputThroughput" : 0.14240956992309883
+} ]
+Benchmark results saved as json in results.json.
+```
+
+The command above would save the results into `results.json` as below.
+
+```json
+[ {
+ "name" : "KMeansModel-1",
+ "totalTimeMs" : 782.0,
+ "inputRecordNum" : 10000,
+ "inputThroughput" : 12787.723785166241,
+ "outputRecordNum" : 10000,
+ "outputThroughput" : 12787.723785166241
+}, {
+ "name" : "KMeans-1",
+ "totalTimeMs" : 7022.0,
+ "inputRecordNum" : 10000,
+ "inputThroughput" : 1424.0956992309884,
+ "outputRecordNum" : 1,
+ "outputThroughput" : 0.14240956992309883
+} ]
+```
+
+## Customize Benchmark Configuration
+
+`flink-ml-benchmark.sh` parses benchmarks to be executed according to the input
+configuration file, like `./conf/benchmark-conf.json`. It can also parse your
+custom configuration file so long as it contains a JSON object in the following
+format.
+
+- The file should contain the following as the metadata of the JSON object.
+ - `"version"`: The version of the json format. Currently its value must be 1.
+- Keys in the JSON object, except `"version"`, are regarded as the names of the
+ benchmarks.
+- The value of each benchmark name should be a JSON object containing the
+ following keys.
+ - `"stage"`: The stage to be benchmarked.
+ - `"inputs"`: The input data of the stage to be benchmarked.
+ - `"modelData"`(Optional): The model data of the stage to be benchmarked, if
+ the stage is a `Model` and needs to have its model data explicitly set.
+- The value of `"stage"`, `"inputs"` or `"modelData"` should be a JSON object
+ containing the following keys.
+ - `"className"`: The full classpath of a `WithParams` subclass. For
`"stage"`,
+ the class should be a subclass of `Stage`. For `"inputs"` or `"modelData"`,
+ the class should be a subclass of `DataGenerator`.
+ - `"paramMap"`: A JSON object containing the parameters related to the
+ specific `Stage` or `DataGenerator`.
+
+Combining the format requirements above, the example configuration in
+`./conf/benchmark-conf.json` is as follows. This configuration contains two
+benchmarks. The first benchmark name is "KMeans-1", and is executed on `KMeans`
+stage. The stage is benchmarked against 10000 randomly generated vectors.
+
+```json
+{
+ "version": 1,
+ "KMeans-1": {
+ "stage": {
+ "className": "org.apache.flink.ml.clustering.kmeans.KMeans",
+ "paramMap": {
+ "featuresCol": "features",
+ "predictionCol": "prediction"
+ }
+ },
+ "inputData": {
+ "className":
"org.apache.flink.ml.benchmark.datagenerator.common.DenseVectorGenerator",
+ "paramMap": {
+ "seed": 2,
+ "colNames": ["features"],
+ "numValues": 10000,
+ "vectorDim": 10
+ }
+ }
+ },
+ "KMeansModel-1": {
+ "stage": {
+ "className": "org.apache.flink.ml.clustering.kmeans.KMeansModel",
+ "paramMap": {
+ "featuresCol": "features",
+ "k": 2,
+ "distanceMeasure": "euclidean",
+ "predictionCol": "prediction"
+ }
+ },
+ "modelData": {
+ "className":
"org.apache.flink.ml.benchmark.datagenerator.clustering.KMeansModelDataGenerator",
+ "paramMap": {
+ "seed": 1,
+ "arraySize": 2,
+ "vectorDim": 10
+ }
+ },
+ "inputData": {
+ "className":
"org.apache.flink.ml.benchmark.datagenerator.common.DenseVectorGenerator",
+ "paramMap": {
+ "seed": 2,
+ "colNames": ["features"],
+ "numValues": 10000,
+ "vectorDim": 10
+ }
+ }
+ }
+}
+```
diff --git a/flink-ml-benchmark/pom.xml b/flink-ml-benchmark/pom.xml
new file mode 100644
index 0000000..5b704e8
--- /dev/null
+++ b/flink-ml-benchmark/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>flink-ml-parent</artifactId>
+ <groupId>org.apache.flink</groupId>
+ <version>2.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>flink-ml-benchmark_${scala.binary.version}</artifactId>
+ <name>Flink ML : Benchmark</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-ml-iteration_${scala.binary.version}</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-ml-core_${scala.binary.version}</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-ml-lib_${scala.binary.version}</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-table-api-java</artifactId>
+ <version>${flink.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+
<artifactId>flink-table-api-java-bridge_${scala.binary.version}</artifactId>
+ <version>${flink.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-clients_${scala.binary.version}</artifactId>
+ <version>${flink.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+
<artifactId>flink-streaming-java_${scala.binary.version}</artifactId>
+ <version>${flink.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-test-utils-junit</artifactId>
+ <version>${flink.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+
<artifactId>flink-table-planner_${scala.binary.version}</artifactId>
+ <version>${flink.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-test-utils_${scala.binary.version}</artifactId>
+ <version>${flink.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/Benchmark.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/Benchmark.java
new file mode 100644
index 0000000..f870dc6
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/Benchmark.java
@@ -0,0 +1,126 @@
+/*
+ * 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.flink.ml.benchmark;
+
+import org.apache.flink.ml.util.ReadWriteUtils;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/** Entry class for benchmark execution. */
+public class Benchmark {
+ private static final Logger LOG = LoggerFactory.getLogger(Benchmark.class);
+
+ static final String VERSION_KEY = "version";
+
+ static final Option HELP_OPTION =
+ Option.builder("h")
+ .longOpt("help")
+ .desc("Show the help message for the command line
interface.")
+ .build();
+
+ static final Option OUTPUT_FILE_OPTION =
+ Option.builder()
+ .longOpt("output-file")
+ .desc("The output file name to save benchmark results.")
+ .hasArg()
+ .build();
+
+ static final Options OPTIONS =
+ new Options().addOption(HELP_OPTION).addOption(OUTPUT_FILE_OPTION);
+
+ public static void printHelp() {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.setLeftPadding(5);
+ formatter.setWidth(80);
+
+ System.out.println("./flink-ml-benchmark.sh <config-file-path>
[OPTIONS]");
+ System.out.println();
+ formatter.setSyntaxPrefix("The following options are available:");
+ formatter.printHelp(" ", OPTIONS);
+
+ System.out.println();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void executeBenchmarks(CommandLine commandLine) throws
Exception {
+ String configFile = commandLine.getArgs()[0];
+ Map<String, ?> benchmarks = BenchmarkUtils.parseJsonFile(configFile);
+ System.out.println("Found benchmarks " + benchmarks.keySet());
+
+ StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
+ StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+ List<BenchmarkResult> results = new ArrayList<>();
+ for (Map.Entry<String, ?> benchmark : benchmarks.entrySet()) {
+ LOG.info("Running benchmark " + benchmark.getKey() + ".");
+
+ BenchmarkResult result =
+ BenchmarkUtils.runBenchmark(
+ tEnv, benchmark.getKey(), (Map<String, ?>)
benchmark.getValue());
+
+ results.add(result);
+ LOG.info(BenchmarkUtils.getResultsMapAsJson(result));
+ }
+
+ String benchmarkResultsJson =
+ BenchmarkUtils.getResultsMapAsJson(results.toArray(new
BenchmarkResult[0]));
+ System.out.println("Benchmarks execution completed.");
+ System.out.println("Benchmark results summary:");
+ System.out.println(benchmarkResultsJson);
+
+ if (commandLine.hasOption(OUTPUT_FILE_OPTION.getLongOpt())) {
+ String saveFile =
commandLine.getOptionValue(OUTPUT_FILE_OPTION.getLongOpt());
+ ReadWriteUtils.saveToFile(saveFile, benchmarkResultsJson, true);
+ System.out.println("Benchmark results saved as json in " +
saveFile + ".");
+ }
+ }
+
+ public static void printInvalidError(String[] args) {
+ System.out.println("Invalid command line arguments " +
Arrays.toString(args));
+ System.out.println();
+ System.out.println("Specify the help option (-h or --help) to get help
on the command.");
+ }
+
+ public static void main(String[] args) throws Exception {
+ CommandLineParser parser = new DefaultParser();
+ CommandLine commandLine = parser.parse(OPTIONS, args);
+
+ if (commandLine.hasOption(HELP_OPTION.getLongOpt())) {
+ printHelp();
+ } else if (commandLine.getArgs().length == 1) {
+ executeBenchmarks(commandLine);
+ } else {
+ printInvalidError(args);
+ }
+ }
+}
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/BenchmarkResult.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/BenchmarkResult.java
new file mode 100644
index 0000000..4efdec0
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/BenchmarkResult.java
@@ -0,0 +1,58 @@
+/*
+ * 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.flink.ml.benchmark;
+
+import org.apache.flink.util.Preconditions;
+
+/** The result of executing a benchmark. */
+public class BenchmarkResult {
+ /** The benchmark name. */
+ public final String name;
+
+ /** The total execution time of the benchmark in milliseconds. */
+ public final Double totalTimeMs;
+
+ /** The total number of input records. */
+ public final Long inputRecordNum;
+
+ /** The average input throughput in number of records per second. */
+ public final Double inputThroughput;
+
+ /** The total number of output records. */
+ public final Long outputRecordNum;
+
+ /** The average output throughput in number of records per second. */
+ public final Double outputThroughput;
+
+ public BenchmarkResult(
+ String name,
+ Double totalTimeMs,
+ Long inputRecordNum,
+ Double inputThroughput,
+ Long outputRecordNum,
+ Double outputThroughput) {
+ Preconditions.checkNotNull(name);
+ this.name = name;
+ this.totalTimeMs = totalTimeMs;
+ this.inputRecordNum = inputRecordNum;
+ this.inputThroughput = inputThroughput;
+ this.outputRecordNum = outputRecordNum;
+ this.outputThroughput = outputThroughput;
+ }
+}
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/BenchmarkUtils.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/BenchmarkUtils.java
new file mode 100644
index 0000000..f21b43b
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/BenchmarkUtils.java
@@ -0,0 +1,183 @@
+/*
+ * 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.flink.ml.benchmark;
+
+import org.apache.flink.api.common.JobExecutionResult;
+import org.apache.flink.api.common.accumulators.LongCounter;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.ml.api.AlgoOperator;
+import org.apache.flink.ml.api.Estimator;
+import org.apache.flink.ml.api.Model;
+import org.apache.flink.ml.api.Stage;
+import org.apache.flink.ml.benchmark.datagenerator.DataGenerator;
+import org.apache.flink.ml.benchmark.datagenerator.InputDataGenerator;
+import org.apache.flink.ml.common.datastream.TableUtils;
+import org.apache.flink.ml.util.ReadWriteUtils;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.util.Preconditions;
+
+import
org.apache.flink.shaded.jackson2.com.fasterxml.jackson.core.JsonProcessingException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/** Utility methods for benchmarks. */
+public class BenchmarkUtils {
+ /** Loads benchmark configuration maps from the provided json file. */
+ @SuppressWarnings("unchecked")
+ public static Map<String, ?> parseJsonFile(String path) throws IOException
{
+ InputStream inputStream = new FileInputStream(path);
+ Map<String, ?> jsonMap =
ReadWriteUtils.OBJECT_MAPPER.readValue(inputStream, Map.class);
+ Preconditions.checkArgument(
+ jsonMap.containsKey(Benchmark.VERSION_KEY)
+ && jsonMap.get(Benchmark.VERSION_KEY).equals(1));
+
+ return jsonMap.entrySet().stream()
+ .filter(x -> !x.getKey().equals(Benchmark.VERSION_KEY))
+ .collect(Collectors.toMap(Map.Entry::getKey,
Map.Entry::getValue));
+ }
+
+ /**
+ * Instantiates a benchmark from its parameter map and executes the
benchmark in the provided
+ * environment.
+ *
+ * @return Results of the executed benchmark.
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public static BenchmarkResult runBenchmark(
+ StreamTableEnvironment tEnv, String name, Map<String, ?> params)
throws Exception {
+ Stage stage = ReadWriteUtils.instantiateWithParams((Map<String, ?>)
params.get("stage"));
+ InputDataGenerator inputDataGenerator =
+ ReadWriteUtils.instantiateWithParams((Map<String, ?>)
params.get("inputData"));
+ DataGenerator modelDataGenerator = null;
+ if (params.containsKey("modelData")) {
+ modelDataGenerator =
+ ReadWriteUtils.instantiateWithParams((Map<String, ?>)
params.get("modelData"));
+ }
+
+ return runBenchmark(tEnv, name, stage, inputDataGenerator,
modelDataGenerator);
+ }
+
+ /**
+ * Executes a benchmark from a stage with its inputDataGenerator and
modelDataGenerator in the
+ * provided environment.
+ *
+ * @return Results of the executed benchmark.
+ */
+ private static BenchmarkResult runBenchmark(
+ StreamTableEnvironment tEnv,
+ String name,
+ Stage<?> stage,
+ InputDataGenerator<?> inputDataGenerator,
+ DataGenerator<?> modelDataGenerator)
+ throws Exception {
+ StreamExecutionEnvironment env =
TableUtils.getExecutionEnvironment(tEnv);
+
+ Table[] inputTables = inputDataGenerator.getData(tEnv);
+ if (modelDataGenerator != null) {
+ ((Model<?>) stage).setModelData(modelDataGenerator.getData(tEnv));
+ }
+
+ Table[] outputTables;
+ if (stage instanceof Estimator) {
+ outputTables = ((Estimator<?, ?>)
stage).fit(inputTables).getModelData();
+ } else if (stage instanceof AlgoOperator) {
+ outputTables = ((AlgoOperator<?>) stage).transform(inputTables);
+ } else {
+ throw new IllegalArgumentException("Unsupported Stage class " +
stage.getClass());
+ }
+
+ for (Table table : outputTables) {
+ tEnv.toDataStream(table).addSink(new
CountingAndDiscardingSink<>());
+ }
+
+ JobExecutionResult executionResult = env.execute("Flink ML Benchmark
Job " + name);
+
+ double totalTimeMs = (double)
executionResult.getNetRuntime(TimeUnit.MILLISECONDS);
+ long inputRecordNum = inputDataGenerator.getNumValues();
+ double inputThroughput = inputRecordNum * 1000.0 / totalTimeMs;
+ long outputRecordNum =
+
executionResult.getAccumulatorResult(CountingAndDiscardingSink.COUNTER_NAME);
+ double outputThroughput = outputRecordNum * 1000.0 / totalTimeMs;
+
+ return new BenchmarkResult(
+ name,
+ totalTimeMs,
+ inputRecordNum,
+ inputThroughput,
+ outputRecordNum,
+ outputThroughput);
+ }
+
+ /** Converts the benchmark results to a json string as a map. */
+ public static String getResultsMapAsJson(BenchmarkResult... results)
+ throws JsonProcessingException {
+ List<Map<String, ?>> resultsMap = new ArrayList<>();
+ for (BenchmarkResult result : results) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("name", result.name);
+ map.put("totalTimeMs", result.totalTimeMs);
+ map.put("inputRecordNum", result.inputRecordNum);
+ map.put("inputThroughput", result.inputThroughput);
+ map.put("outputRecordNum", result.outputRecordNum);
+ map.put("outputThroughput", result.outputThroughput);
+ resultsMap.add(map);
+ }
+ return ReadWriteUtils.OBJECT_MAPPER
+ .writerWithDefaultPrettyPrinter()
+ .writeValueAsString(resultsMap);
+ }
+
+ /**
+ * A stream sink that counts the number of all elements. The counting
result is stored in an
+ * {@link org.apache.flink.api.common.accumulators.Accumulator} specified
by {@link
+ * #COUNTER_NAME} and can be acquired by {@link
+ *
org.apache.flink.api.common.JobExecutionResult#getAccumulatorResult(String)}.
+ *
+ * @param <T> The type of elements received by the sink.
+ */
+ private static class CountingAndDiscardingSink<T> extends
RichSinkFunction<T> {
+ public static final String COUNTER_NAME = "numElements";
+
+ private static final long serialVersionUID = 1L;
+
+ private final LongCounter numElementsCounter = new LongCounter();
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ getRuntimeContext().addAccumulator(COUNTER_NAME,
numElementsCounter);
+ }
+
+ @Override
+ public void invoke(T value, Context context) {
+ numElementsCounter.add(1L);
+ }
+ }
+}
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/DataGenerator.java
similarity index 60%
copy from flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
copy to
flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/DataGenerator.java
index 0de890c..846a36f 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/DataGenerator.java
@@ -16,17 +16,17 @@
* limitations under the License.
*/
-package org.apache.flink.ml.param;
+package org.apache.flink.ml.benchmark.datagenerator;
-/** Class for the float parameter. */
-public class FloatParam extends Param<Float> {
+import org.apache.flink.ml.common.param.HasSeed;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
- public FloatParam(
- String name, String description, Float defaultValue,
ParamValidator<Float> validator) {
- super(name, Float.class, description, defaultValue, validator);
- }
-
- public FloatParam(String name, String description, Float defaultValue) {
- this(name, description, defaultValue, ParamValidators.alwaysTrue());
- }
+/** Interface for generating data as table arrays. */
+public interface DataGenerator<T extends DataGenerator<T>> extends HasSeed<T> {
+ /**
+ * Gets an array of Tables containing the data generated in the provided
stream table
+ * environment.
+ */
+ Table[] getData(StreamTableEnvironment tEnv);
}
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/InputDataGenerator.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/InputDataGenerator.java
new file mode 100644
index 0000000..e13c1a8
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/InputDataGenerator.java
@@ -0,0 +1,53 @@
+/*
+ * 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.flink.ml.benchmark.datagenerator;
+
+import org.apache.flink.ml.param.LongParam;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.param.ParamValidators;
+import org.apache.flink.ml.param.StringArrayParam;
+
+/** Interface for generating data as input table arrays. */
+public interface InputDataGenerator<T extends InputDataGenerator<T>> extends
DataGenerator<T> {
+ Param<Long> NUM_VALUES =
+ new LongParam(
+ "numValues", "Number of data to be generated.", 10L,
ParamValidators.gt(0));
+
+ Param<String[]> COL_NAMES =
+ new StringArrayParam(
+ "colNames",
+ "An array of common-separated strings representing field
names of data tables.",
+ null);
+
+ default long getNumValues() {
+ return get(NUM_VALUES);
+ }
+
+ default T setNumValues(long value) {
+ return set(NUM_VALUES, value);
+ }
+
+ default String[] getColNames() {
+ return get(COL_NAMES);
+ }
+
+ default T setColNames(String... value) {
+ return set(COL_NAMES, value);
+ }
+}
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/clustering/KMeansModelDataGenerator.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/clustering/KMeansModelDataGenerator.java
new file mode 100644
index 0000000..38ef523
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/clustering/KMeansModelDataGenerator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.flink.ml.benchmark.datagenerator.clustering;
+
+import org.apache.flink.api.common.functions.MapFunction;
+import org.apache.flink.ml.benchmark.datagenerator.DataGenerator;
+import org.apache.flink.ml.benchmark.datagenerator.InputDataGenerator;
+import
org.apache.flink.ml.benchmark.datagenerator.common.DenseVectorArrayGenerator;
+import org.apache.flink.ml.benchmark.datagenerator.param.HasArraySize;
+import org.apache.flink.ml.benchmark.datagenerator.param.HasVectorDim;
+import org.apache.flink.ml.clustering.kmeans.KMeansModelData;
+import org.apache.flink.ml.linalg.DenseVector;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.util.ParamUtils;
+import org.apache.flink.ml.util.ReadWriteUtils;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A DataGenerator which creates a table containing one {@link
+ * org.apache.flink.ml.clustering.kmeans.KMeansModel} instance.
+ */
+public class KMeansModelDataGenerator
+ implements DataGenerator<KMeansModelDataGenerator>,
+ HasVectorDim<KMeansModelDataGenerator>,
+ HasArraySize<KMeansModelDataGenerator> {
+ private final Map<Param<?>, Object> paramMap = new HashMap<>();
+
+ public KMeansModelDataGenerator() {
+ ParamUtils.initializeMapWithDefaultValues(paramMap, this);
+ }
+
+ @Override
+ public Table[] getData(StreamTableEnvironment tEnv) {
+ InputDataGenerator<?> vectorArrayGenerator = new
DenseVectorArrayGenerator();
+ ReadWriteUtils.updateExistingParams(vectorArrayGenerator, paramMap);
+ vectorArrayGenerator.setNumValues(1);
+
+ Table vectorArrayTable = vectorArrayGenerator.getData(tEnv)[0];
+ DataStream<KMeansModelData> modelDataStream =
+ tEnv.toDataStream(vectorArrayTable, DenseVector[].class)
+ .map(new GenerateKMeansModelDataFunction());
+
+ return new Table[] {tEnv.fromDataStream(modelDataStream)};
+ }
+
+ private static class GenerateKMeansModelDataFunction
+ implements MapFunction<DenseVector[], KMeansModelData> {
+ @Override
+ public KMeansModelData map(DenseVector[] vectors) {
+ return new KMeansModelData(vectors, new
DenseVector(vectors.length));
+ }
+ }
+
+ @Override
+ public Map<Param<?>, Object> getParamMap() {
+ return paramMap;
+ }
+}
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/common/DenseVectorArrayGenerator.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/common/DenseVectorArrayGenerator.java
new file mode 100644
index 0000000..91c079d
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/common/DenseVectorArrayGenerator.java
@@ -0,0 +1,116 @@
+/*
+ * 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.flink.ml.benchmark.datagenerator.common;
+
+import org.apache.flink.api.common.functions.RichMapFunction;
+import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
+import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.ml.benchmark.datagenerator.InputDataGenerator;
+import org.apache.flink.ml.benchmark.datagenerator.param.HasArraySize;
+import org.apache.flink.ml.benchmark.datagenerator.param.HasVectorDim;
+import org.apache.flink.ml.common.datastream.TableUtils;
+import org.apache.flink.ml.linalg.DenseVector;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.util.ParamUtils;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.api.Schema;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.util.NumberSequenceIterator;
+import org.apache.flink.util.Preconditions;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/** A DataGenerator which creates a table of DenseVector array. */
+public class DenseVectorArrayGenerator
+ implements InputDataGenerator<DenseVectorArrayGenerator>,
+ HasArraySize<DenseVectorArrayGenerator>,
+ HasVectorDim<DenseVectorArrayGenerator> {
+ private final Map<Param<?>, Object> paramMap = new HashMap<>();
+
+ public DenseVectorArrayGenerator() {
+ ParamUtils.initializeMapWithDefaultValues(paramMap, this);
+ }
+
+ @Override
+ public Table[] getData(StreamTableEnvironment tEnv) {
+ StreamExecutionEnvironment env =
TableUtils.getExecutionEnvironment(tEnv);
+
+ DataStream<DenseVector[]> dataStream =
+ env.fromParallelCollection(
+ new NumberSequenceIterator(1L, getNumValues()),
+ BasicTypeInfo.LONG_TYPE_INFO)
+ .map(
+ new
GenerateRandomContinuousVectorArrayFunction(
+ getSeed(), getVectorDim(),
getArraySize()));
+
+ Schema schema = Schema.newBuilder().column("f0",
DataTypes.of(DenseVector[].class)).build();
+ Table dataTable = tEnv.fromDataStream(dataStream, schema);
+ if (getColNames() != null) {
+ Preconditions.checkState(getColNames().length > 0);
+ dataTable = dataTable.as(getColNames()[0]);
+ }
+
+ return new Table[] {dataTable};
+ }
+
+ private static class GenerateRandomContinuousVectorArrayFunction
+ extends RichMapFunction<Long, DenseVector[]> {
+ private final int vectorDim;
+ private final long initSeed;
+ private final int arraySize;
+ private Random random;
+
+ private GenerateRandomContinuousVectorArrayFunction(
+ long initSeed, int vectorDim, int arraySize) {
+ this.vectorDim = vectorDim;
+ this.initSeed = initSeed;
+ this.arraySize = arraySize;
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ int index = getRuntimeContext().getIndexOfThisSubtask();
+ random = new Random(Tuple2.of(initSeed, index).hashCode());
+ }
+
+ @Override
+ public DenseVector[] map(Long value) {
+ DenseVector[] result = new DenseVector[arraySize];
+ for (int i = 0; i < arraySize; i++) {
+ result[i] = new DenseVector(vectorDim);
+ for (int j = 0; j < vectorDim; j++) {
+ result[i].values[j] = random.nextDouble();
+ }
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Map<Param<?>, Object> getParamMap() {
+ return paramMap;
+ }
+}
diff --git
a/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/common/DenseVectorGenerator.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/common/DenseVectorGenerator.java
new file mode 100644
index 0000000..f05cf75
--- /dev/null
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/common/DenseVectorGenerator.java
@@ -0,0 +1,102 @@
+/*
+ * 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.flink.ml.benchmark.datagenerator.common;
+
+import org.apache.flink.api.common.functions.RichMapFunction;
+import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
+import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.ml.benchmark.datagenerator.InputDataGenerator;
+import org.apache.flink.ml.benchmark.datagenerator.param.HasVectorDim;
+import org.apache.flink.ml.common.datastream.TableUtils;
+import org.apache.flink.ml.linalg.DenseVector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.util.ParamUtils;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.util.NumberSequenceIterator;
+import org.apache.flink.util.Preconditions;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/** A DataGenerator which creates a table of DenseVector. */
+public class DenseVectorGenerator
+ implements InputDataGenerator<DenseVectorGenerator>,
HasVectorDim<DenseVectorGenerator> {
+ private final Map<Param<?>, Object> paramMap = new HashMap<>();
+
+ public DenseVectorGenerator() {
+ ParamUtils.initializeMapWithDefaultValues(paramMap, this);
+ }
+
+ @Override
+ public Table[] getData(StreamTableEnvironment tEnv) {
+ StreamExecutionEnvironment env =
TableUtils.getExecutionEnvironment(tEnv);
+
+ DataStream<DenseVector> dataStream =
+ env.fromParallelCollection(
+ new NumberSequenceIterator(1L, getNumValues()),
+ BasicTypeInfo.LONG_TYPE_INFO)
+ .map(new RandomDenseVectorGenerator(getSeed(),
getVectorDim()));
+
+ Table dataTable = tEnv.fromDataStream(dataStream);
+ if (getColNames() != null) {
+ Preconditions.checkState(getColNames().length > 0);
+ dataTable = dataTable.as(getColNames()[0]);
+ }
+
+ return new Table[] {dataTable};
+ }
+
+ private static class RandomDenseVectorGenerator extends
RichMapFunction<Long, DenseVector> {
+ private final int vectorDim;
+ private final long initSeed;
+ private Random random;
+
+ private RandomDenseVectorGenerator(long initSeed, int vectorDim) {
+ this.vectorDim = vectorDim;
+ this.initSeed = initSeed;
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ int index = getRuntimeContext().getIndexOfThisSubtask();
+ random = new Random(Tuple2.of(initSeed, index).hashCode());
+ }
+
+ @Override
+ public DenseVector map(Long value) {
+ double[] values = new double[vectorDim];
+ for (int i = 0; i < vectorDim; i++) {
+ values[i] = random.nextDouble();
+ }
+ return Vectors.dense(values);
+ }
+ }
+
+ @Override
+ public Map<Param<?>, Object> getParamMap() {
+ return paramMap;
+ }
+}
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/param/HasArraySize.java
similarity index 53%
copy from flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
copy to
flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/param/HasArraySize.java
index 0de890c..4c2d48e 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/param/HasArraySize.java
@@ -16,17 +16,27 @@
* limitations under the License.
*/
-package org.apache.flink.ml.param;
+package org.apache.flink.ml.benchmark.datagenerator.param;
-/** Class for the float parameter. */
-public class FloatParam extends Param<Float> {
+import org.apache.flink.ml.param.IntParam;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.param.ParamValidators;
+import org.apache.flink.ml.param.WithParams;
- public FloatParam(
- String name, String description, Float defaultValue,
ParamValidator<Float> validator) {
- super(name, Float.class, description, defaultValue, validator);
+/** Interface for the benchmark array size param. */
+public interface HasArraySize<T> extends WithParams<T> {
+ Param<Integer> ARRAY_SIZE =
+ new IntParam(
+ "arraySize",
+ "Number of elements in the generated array.",
+ 1,
+ ParamValidators.gt(0));
+
+ default int getArraySize() {
+ return get(ARRAY_SIZE);
}
- public FloatParam(String name, String description, Float defaultValue) {
- this(name, description, defaultValue, ParamValidators.alwaysTrue());
+ default T setArraySize(int value) {
+ return set(ARRAY_SIZE, value);
}
}
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/param/HasVectorDim.java
similarity index 53%
copy from flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
copy to
flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/param/HasVectorDim.java
index 0de890c..824f821 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
+++
b/flink-ml-benchmark/src/main/java/org/apache/flink/ml/benchmark/datagenerator/param/HasVectorDim.java
@@ -16,17 +16,27 @@
* limitations under the License.
*/
-package org.apache.flink.ml.param;
+package org.apache.flink.ml.benchmark.datagenerator.param;
-/** Class for the float parameter. */
-public class FloatParam extends Param<Float> {
+import org.apache.flink.ml.param.IntParam;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.param.ParamValidators;
+import org.apache.flink.ml.param.WithParams;
- public FloatParam(
- String name, String description, Float defaultValue,
ParamValidator<Float> validator) {
- super(name, Float.class, description, defaultValue, validator);
+/** Interface for the benchmark vector dimension param. */
+public interface HasVectorDim<T> extends WithParams<T> {
+ Param<Integer> VECTOR_DIM =
+ new IntParam(
+ "vectorDim",
+ "Dimension of vector-typed data to be generated.",
+ 1,
+ ParamValidators.gt(0));
+
+ default int getVectorDim() {
+ return get(VECTOR_DIM);
}
- public FloatParam(String name, String description, Float defaultValue) {
- this(name, description, defaultValue, ParamValidators.alwaysTrue());
+ default T setVectorDim(int value) {
+ return set(VECTOR_DIM, value);
}
}
diff --git a/flink-ml-benchmark/src/main/resources/benchmark-conf.json
b/flink-ml-benchmark/src/main/resources/benchmark-conf.json
new file mode 100644
index 0000000..280453e
--- /dev/null
+++ b/flink-ml-benchmark/src/main/resources/benchmark-conf.json
@@ -0,0 +1,49 @@
+{
+ "version": 1,
+ "KMeans-1": {
+ "stage": {
+ "className": "org.apache.flink.ml.clustering.kmeans.KMeans",
+ "paramMap": {
+ "featuresCol": "features",
+ "predictionCol": "prediction"
+ }
+ },
+ "inputData": {
+ "className":
"org.apache.flink.ml.benchmark.datagenerator.common.DenseVectorGenerator",
+ "paramMap": {
+ "seed": 2,
+ "colNames": ["features"],
+ "numValues": 10000,
+ "vectorDim": 10
+ }
+ }
+ },
+ "KMeansModel-1": {
+ "stage": {
+ "className": "org.apache.flink.ml.clustering.kmeans.KMeansModel",
+ "paramMap": {
+ "featuresCol": "features",
+ "k": 2,
+ "distanceMeasure": "euclidean",
+ "predictionCol": "prediction"
+ }
+ },
+ "modelData": {
+ "className":
"org.apache.flink.ml.benchmark.datagenerator.clustering.KMeansModelDataGenerator",
+ "paramMap": {
+ "seed": 1,
+ "arraySize": 2,
+ "vectorDim": 10
+ }
+ },
+ "inputData": {
+ "className":
"org.apache.flink.ml.benchmark.datagenerator.common.DenseVectorGenerator",
+ "paramMap": {
+ "seed": 2,
+ "colNames": ["features"],
+ "numValues": 10000,
+ "vectorDim": 10
+ }
+ }
+ }
+}
diff --git
a/flink-ml-benchmark/src/test/java/org/apache/flink/ml/benchmark/BenchmarkTest.java
b/flink-ml-benchmark/src/test/java/org/apache/flink/ml/benchmark/BenchmarkTest.java
new file mode 100644
index 0000000..c3ca0cf
--- /dev/null
+++
b/flink-ml-benchmark/src/test/java/org/apache/flink/ml/benchmark/BenchmarkTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.flink.ml.benchmark;
+
+import org.apache.flink.ml.benchmark.datagenerator.common.DenseVectorGenerator;
+import org.apache.flink.ml.clustering.kmeans.KMeans;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.test.util.AbstractTestBase;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/** Tests benchmarks. */
+public class BenchmarkTest extends AbstractTestBase {
+ @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Test
+ public void testParseJsonFile() throws Exception {
+ File configFile = new File(tempFolder.newFolder().getAbsolutePath() +
"/test-conf.json");
+ InputStream inputStream =
+
this.getClass().getClassLoader().getResourceAsStream("benchmark-conf.json");
+ FileUtils.copyInputStreamToFile(inputStream, configFile);
+
+ Map<String, ?> benchmarks =
BenchmarkUtils.parseJsonFile(configFile.getAbsolutePath());
+ assertEquals(benchmarks.size(), 2);
+ assertTrue(benchmarks.containsKey("KMeans-1"));
+ assertTrue(benchmarks.containsKey("KMeansModel-1"));
+ }
+
+ @Test
+ public void testRunBenchmark() throws Exception {
+ StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
+ StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+ Map<String, Object> params = new HashMap<>();
+
+ Map<String, Object> stageParams = new HashMap<>();
+ stageParams.put("className", KMeans.class.getCanonicalName());
+ stageParams.put(
+ "paramMap",
+ new HashMap<String, Object>() {
+ {
+ put("k", 5);
+ put("featuresCol", "test_feature");
+ }
+ });
+ params.put("stage", stageParams);
+
+ Map<String, Object> inputDataParams = new HashMap<>();
+ inputDataParams.put("className",
DenseVectorGenerator.class.getCanonicalName());
+ inputDataParams.put(
+ "paramMap",
+ new HashMap<String, Object>() {
+ {
+ put("colNames", new String[] {"test_feature"});
+ put("numValues", 1000L);
+ put("vectorDim", 10);
+ }
+ });
+ params.put("inputData", inputDataParams);
+
+ long estimatedTime = System.currentTimeMillis();
+ BenchmarkResult result = BenchmarkUtils.runBenchmark(tEnv,
"testBenchmarkName", params);
+ estimatedTime = System.currentTimeMillis() - estimatedTime;
+
+ assertEquals("testBenchmarkName", result.name);
+ assertTrue(result.totalTimeMs > 0);
+ assertTrue(result.totalTimeMs <= estimatedTime);
+ assertEquals(1000L, (long) result.inputRecordNum);
+ assertEquals(1L, (long) result.outputRecordNum);
+ assertEquals(
+ result.inputRecordNum * 1000.0 / result.totalTimeMs,
result.inputThroughput, 1e-5);
+ assertEquals(
+ result.outputRecordNum * 1000.0 / result.totalTimeMs,
+ result.outputThroughput,
+ 1e-5);
+ }
+}
diff --git a/flink-ml-core/src/main/java/org/apache/flink/ml/api/Stage.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/api/Stage.java
index 439b956..20a0bf7 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/api/Stage.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/api/Stage.java
@@ -31,6 +31,8 @@ import java.io.Serializable;
*
* <p>Each stage is with parameters, and requires a public empty constructor
for restoration.
*
+ * <p>NOTE: every Stage subclass should have a no-arg constructor.
+ *
* <p>NOTE: every Stage subclass should implement a static method with
signature {@code static T
* load(StreamTableEnvironment tEnv, String path)}, where {@code T} refers to
the concrete subclass.
* This static method should instantiate a new stage instance based on the
data read from the given
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/ArrayParam.java
similarity index 58%
copy from
flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
copy to flink-ml-core/src/main/java/org/apache/flink/ml/param/ArrayParam.java
index 5e4fc47..adc3b80 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/ArrayParam.java
@@ -18,18 +18,26 @@
package org.apache.flink.ml.param;
-/** Class for the long array parameter. */
-public class LongArrayParam extends Param<Long[]> {
+import org.apache.flink.ml.util.ReadWriteUtils;
- public LongArrayParam(
+import java.io.IOException;
+
+/** Class for array-type parameters. */
+public class ArrayParam<T> extends Param<T[]> {
+
+ public ArrayParam(
String name,
+ Class<T[]> clazz,
String description,
- Long[] defaultValue,
- ParamValidator<Long[]> validator) {
- super(name, Long[].class, description, defaultValue, validator);
+ T[] defaultValue,
+ ParamValidator<T[]> validator) {
+ super(name, clazz, description, defaultValue, validator);
}
- public LongArrayParam(String name, String description, Long[]
defaultValue) {
- this(name, description, defaultValue, ParamValidators.alwaysTrue());
+ @Override
+ public T[] jsonDecode(Object json) throws IOException {
+ // Converts Object Mapper's default ArrayList<T> to T[].
+ String jsonStr = ReadWriteUtils.OBJECT_MAPPER.writeValueAsString(json);
+ return ReadWriteUtils.OBJECT_MAPPER.readValue(jsonStr, clazz);
}
}
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/DoubleArrayParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/DoubleArrayParam.java
index b86dd00..570629f 100644
---
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/DoubleArrayParam.java
+++
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/DoubleArrayParam.java
@@ -19,7 +19,7 @@
package org.apache.flink.ml.param;
/** Class for the double array parameter. */
-public class DoubleArrayParam extends Param<Double[]> {
+public class DoubleArrayParam extends ArrayParam<Double> {
public DoubleArrayParam(
String name,
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatArrayParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatArrayParam.java
index 4224557..af3704f 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatArrayParam.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatArrayParam.java
@@ -19,7 +19,7 @@
package org.apache.flink.ml.param;
/** Class for the float array parameter. */
-public class FloatArrayParam extends Param<Float[]> {
+public class FloatArrayParam extends ArrayParam<Float> {
public FloatArrayParam(
String name,
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
index 0de890c..f86ecfe 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/FloatParam.java
@@ -18,6 +18,8 @@
package org.apache.flink.ml.param;
+import java.io.IOException;
+
/** Class for the float parameter. */
public class FloatParam extends Param<Float> {
@@ -29,4 +31,12 @@ public class FloatParam extends Param<Float> {
public FloatParam(String name, String description, Float defaultValue) {
this(name, description, defaultValue, ParamValidators.alwaysTrue());
}
+
+ @Override
+ public Float jsonDecode(Object json) throws IOException {
+ if (json instanceof Double) {
+ return ((Double) json).floatValue();
+ }
+ return (Float) json;
+ }
}
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/IntArrayParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/IntArrayParam.java
index 4f7c630..6ad7e58 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/IntArrayParam.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/IntArrayParam.java
@@ -19,7 +19,7 @@
package org.apache.flink.ml.param;
/** Class for the integer array parameter. */
-public class IntArrayParam extends Param<Integer[]> {
+public class IntArrayParam extends ArrayParam<Integer> {
public IntArrayParam(
String name,
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
index 5e4fc47..adff46f 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongArrayParam.java
@@ -19,7 +19,7 @@
package org.apache.flink.ml.param;
/** Class for the long array parameter. */
-public class LongArrayParam extends Param<Long[]> {
+public class LongArrayParam extends ArrayParam<Long> {
public LongArrayParam(
String name,
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongParam.java
index 3fd7dd8..410229f 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongParam.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/LongParam.java
@@ -18,6 +18,8 @@
package org.apache.flink.ml.param;
+import java.io.IOException;
+
/** Class for the long parameter. */
public class LongParam extends Param<Long> {
@@ -29,4 +31,12 @@ public class LongParam extends Param<Long> {
public LongParam(String name, String description, Long defaultValue) {
this(name, description, defaultValue, ParamValidators.alwaysTrue());
}
+
+ @Override
+ public Long jsonDecode(Object json) throws IOException {
+ if (json instanceof Integer) {
+ return ((Integer) json).longValue();
+ }
+ return (Long) json;
+ }
}
diff --git a/flink-ml-core/src/main/java/org/apache/flink/ml/param/Param.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/Param.java
index b7a1aef..f5035bf 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/param/Param.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/param/Param.java
@@ -19,7 +19,6 @@
package org.apache.flink.ml.param;
import org.apache.flink.annotation.PublicEvolving;
-import org.apache.flink.ml.util.ReadWriteUtils;
import java.io.IOException;
import java.io.Serializable;
@@ -58,24 +57,24 @@ public class Param<T> implements Serializable {
}
/**
- * Encodes the given object into a json-formatted string.
+ * Encodes the given java object into a json-supported object.
*
- * @param value An object of class type T.
- * @return A json-formatted string.
+ * @param value A java object of class type T.
+ * @return A json-supported object.
*/
- public String jsonEncode(T value) throws IOException {
- return ReadWriteUtils.OBJECT_MAPPER.writeValueAsString(value);
+ public Object jsonEncode(T value) throws IOException {
+ return value;
}
/**
- * Decodes the given string into an object of class type T.
+ * Decodes the given json-supported object into an object of class type T.
*
- * @param json A json-formatted string.
- * @return An object of class type T.
+ * @param json A json-supported object.
+ * @return A java object of class type T.
*/
@SuppressWarnings("unchecked")
- public T jsonDecode(String json) throws IOException {
- return ReadWriteUtils.OBJECT_MAPPER.readValue(json, clazz);
+ public T jsonDecode(Object json) throws IOException {
+ return (T) json;
}
@Override
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/StringArrayParam.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/StringArrayParam.java
index 5062463..7c6b736 100644
---
a/flink-ml-core/src/main/java/org/apache/flink/ml/param/StringArrayParam.java
+++
b/flink-ml-core/src/main/java/org/apache/flink/ml/param/StringArrayParam.java
@@ -19,7 +19,7 @@
package org.apache.flink.ml.param;
/** Class for the string array parameter. */
-public class StringArrayParam extends Param<String[]> {
+public class StringArrayParam extends ArrayParam<String> {
public StringArrayParam(
String name,
diff --git
a/flink-ml-core/src/main/java/org/apache/flink/ml/util/ReadWriteUtils.java
b/flink-ml-core/src/main/java/org/apache/flink/ml/util/ReadWriteUtils.java
index 54aefbe..ed7b227 100644
--- a/flink-ml-core/src/main/java/org/apache/flink/ml/util/ReadWriteUtils.java
+++ b/flink-ml-core/src/main/java/org/apache/flink/ml/util/ReadWriteUtils.java
@@ -33,6 +33,7 @@ import org.apache.flink.ml.builder.GraphModel;
import org.apache.flink.ml.builder.GraphNode;
import org.apache.flink.ml.common.datastream.TableUtils;
import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.param.WithParams;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import
org.apache.flink.streaming.api.functions.sink.filesystem.bucketassigners.BasePathBucketAssigner;
@@ -66,17 +67,17 @@ public class ReadWriteUtils {
// A helper method that calls encodes the given parameter value to a json
string. We can not
// call param.jsonEncode(value) directly because Param::jsonEncode(...)
needs the actual type
// of the value.
- private static <T> String jsonEncodeHelper(Param<T> param, Object value)
throws IOException {
+ private static <T> Object jsonEncodeHelper(Param<T> param, Object value)
throws IOException {
return param.jsonEncode((T) value);
}
// Converts Map<Param<?>, Object> to Map<String, String> which maps the
parameter name to the
// string-encoded parameter value.
- private static Map<String, String> jsonEncode(Map<Param<?>, Object>
paramMap)
+ private static Map<String, Object> jsonEncode(Map<Param<?>, Object>
paramMap)
throws IOException {
- Map<String, String> result = new HashMap<>(paramMap.size());
+ Map<String, Object> result = new HashMap<>(paramMap.size());
for (Map.Entry<Param<?>, Object> entry : paramMap.entrySet()) {
- String json = jsonEncodeHelper(entry.getKey(), entry.getValue());
+ Object json = jsonEncodeHelper(entry.getKey(), entry.getValue());
result.put(entry.getKey().name, json);
}
return result;
@@ -94,9 +95,6 @@ public class ReadWriteUtils {
*/
public static void saveMetadata(Stage<?> stage, String path, Map<String,
?> extraMetadata)
throws IOException {
- // Creates parent directories if not already created.
- FileSystem fs = mkdirs(path);
-
Map<String, Object> metadata = new HashMap<>(extraMetadata);
metadata.put("className", stage.getClass().getName());
metadata.put("timestamp", System.currentTimeMillis());
@@ -104,15 +102,27 @@ public class ReadWriteUtils {
// TODO: add version in the metadata.
String metadataStr = OBJECT_MAPPER.writeValueAsString(metadata);
- Path metadataPath = new Path(path, "metadata");
- if (fs.exists(metadataPath)) {
- throw new IOException("File " + metadataPath + " already exists.");
+ saveToFile(new Path(path, "metadata").toUri().toString(), metadataStr,
false);
+ }
+
+ /** Saves a given string to the specified file. */
+ public static void saveToFile(String pathStr, String content, boolean
isOverwrite)
+ throws IOException {
+ Path path = new Path(pathStr);
+
+ // Creates parent directories if not already created.
+ FileSystem fs = mkdirs(path.getParent());
+
+ FileSystem.WriteMode writeMode = FileSystem.WriteMode.OVERWRITE;
+ if (!isOverwrite) {
+ writeMode = FileSystem.WriteMode.NO_OVERWRITE;
+ if (fs.exists(path)) {
+ throw new IOException("File " + path + " already exists.");
+ }
}
try (BufferedWriter writer =
- new BufferedWriter(
- new OutputStreamWriter(
- fs.create(metadataPath,
FileSystem.WriteMode.NO_OVERWRITE)))) {
- writer.write(metadataStr);
+ new BufferedWriter(new OutputStreamWriter(fs.create(path,
writeMode)))) {
+ writer.write(content);
}
}
@@ -195,7 +205,7 @@ public class ReadWriteUtils {
public static void savePipeline(Stage<?> pipeline, List<Stage<?>> stages,
String path)
throws IOException {
// Creates parent directories if not already created.
- mkdirs(path);
+ mkdirs(new Path(path));
Map<String, Object> extraMetadata = new HashMap<>();
extraMetadata.put("numStages", stages.size());
@@ -232,10 +242,9 @@ public class ReadWriteUtils {
return stages;
}
- private static FileSystem mkdirs(String path) throws IOException {
- Path temp = new Path(path);
- FileSystem fs = temp.getFileSystem();
- fs.mkdirs(temp);
+ private static FileSystem mkdirs(Path path) throws IOException {
+ FileSystem fs = path.getFileSystem();
+ fs.mkdirs(path);
return fs;
}
@@ -249,7 +258,7 @@ public class ReadWriteUtils {
public static void saveGraph(Stage<?> graph, GraphData graphData, String
path)
throws IOException {
// Creates parent directories if not already created.
- mkdirs(path);
+ mkdirs(new Path(path));
Map<String, Object> extraMetadata = new HashMap<>();
extraMetadata.put("graphData", graphData.toMap());
@@ -277,6 +286,7 @@ public class ReadWriteUtils {
* @param expectedClassName The expected class name of the pipeline.
* @return A Graph or GraphModel instance.
*/
+ @SuppressWarnings("unchecked")
public static Stage<?> loadGraph(
StreamTableEnvironment tEnv, String path, String
expectedClassName) throws IOException {
Map<String, ?> metadata = loadMetadata(path, expectedClassName);
@@ -311,20 +321,23 @@ public class ReadWriteUtils {
graphData.outputModelDataIds);
}
- // A helper method that sets stage's parameter value. We can not call
stage.set(param, value)
- // directly because stage::set(...) needs the actual type of the value.
- public static <T> void setParam(Stage<?> stage, Param<T> param, Object
value) {
- stage.set(param, (T) value);
+ // A helper method that sets WithParams object's parameter value. We can
not call
+ // WithParams.set(param, value)
+ // directly because WithParams::set(...) needs the actual type of the
value.
+ @SuppressWarnings("unchecked")
+ public static <T> void setParam(WithParams<?> instance, Param<T> param,
Object value) {
+ instance.set(param, (T) value);
}
- // A helper method that updates stage's param map using values from the
paramOverrides. This
- // method only
- // updates values for parameters already defined in the stage's param map.
- public static void updateExistingParams(Stage<?> stage, Map<Param<?>,
Object> paramOverrides) {
- Set<Param<?>> existingParams = stage.getParamMap().keySet();
+ // A helper method that updates WithParams instance's param map using
values from the
+ // paramOverrides. This method only updates values for parameters already
defined in the
+ // instance's param map.
+ public static void updateExistingParams(
+ WithParams<?> instance, Map<Param<?>, Object> paramOverrides) {
+ Set<Param<?>> existingParams = instance.getParamMap().keySet();
for (Map.Entry<Param<?>, Object> entry : paramOverrides.entrySet()) {
if (existingParams.contains(entry.getKey())) {
- setParam(stage, entry.getKey(), entry.getValue());
+ setParam(instance, entry.getKey(), entry.getValue());
}
}
}
@@ -343,31 +356,41 @@ public class ReadWriteUtils {
* @param <T> The class type of the Stage subclass.
* @return An instance of class type T.
*/
- @SuppressWarnings("unchecked")
public static <T extends Stage<T>> T loadStageParam(String path) throws
IOException {
- Map<String, ?> metadata = loadMetadata(path, "");
- String className = (String) metadata.get("className");
- Map<String, String> paramMap = (Map<String, String>)
metadata.get("paramMap");
-
try {
- Class<T> clazz = (Class<T>) Class.forName(className);
- T instance = InstantiationUtil.instantiate(clazz);
-
- Map<String, Param<?>> nameToParam = new HashMap<>();
- for (Param<?> param :
ParamUtils.getPublicFinalParamFields(instance)) {
- nameToParam.put(param.name, param);
- }
-
- for (Map.Entry<String, String> entry : paramMap.entrySet()) {
- Param<?> param = nameToParam.get(entry.getKey());
- setParam(instance, param, param.jsonDecode(entry.getValue()));
- }
- return instance;
+ return instantiateWithParams(loadMetadata(path, ""));
} catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to load stage.", e);
}
}
+ /**
+ * Instantiates a WithParams subclass from the provided json map.
+ *
+ * @param jsonMap a map containing className and paramMap.
+ * @return the instantiated WithParams subclass instance.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends WithParams<T>> T
instantiateWithParams(Map<String, ?> jsonMap)
+ throws ClassNotFoundException, IOException {
+ String className = (String) jsonMap.get("className");
+ Class<T> clazz = (Class<T>) Class.forName(className);
+ T instance = InstantiationUtil.instantiate(clazz);
+
+ Map<String, Param<?>> nameToParam = new HashMap<>();
+ for (Param<?> param : ParamUtils.getPublicFinalParamFields(instance)) {
+ nameToParam.put(param.name, param);
+ }
+
+ Map<String, Object> paramMap = (Map<String, Object>)
jsonMap.get("paramMap");
+ for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
+ Param<?> param = nameToParam.get(entry.getKey());
+ setParam(instance, param, param.jsonDecode(entry.getValue()));
+ }
+
+ return instance;
+ }
+
/**
* Loads the stage from the given path by invoking the static load()
method of the stage. The
* stage class name is read from the metadata file under the given path.
The load() method is
diff --git a/flink-ml-core/src/test/java/org/apache/flink/ml/api/StageTest.java
b/flink-ml-core/src/test/java/org/apache/flink/ml/api/StageTest.java
index 18b9be8..091502b 100644
--- a/flink-ml-core/src/test/java/org/apache/flink/ml/api/StageTest.java
+++ b/flink-ml-core/src/test/java/org/apache/flink/ml/api/StageTest.java
@@ -57,9 +57,18 @@ public class StageTest {
Param<Integer> INT_PARAM =
new IntParam("intParam", "Description", 1,
ParamValidators.lt(100));
+ // This param might be regarded as integer type given a small value.
Param<Long> LONG_PARAM =
new LongParam("longParam", "Description", 2L,
ParamValidators.lt(100));
+ // This param must be regarded as long type given a large enough value.
+ Param<Long> LONG_PARAM2 =
+ new LongParam(
+ "longParam2",
+ "Description",
+ Integer.MAX_VALUE + 1L,
+ ParamValidators.lt(Integer.MAX_VALUE + 100L));
+
Param<Float> FLOAT_PARAM =
new FloatParam("floatParam", "Description", 3.0f,
ParamValidators.lt(100));
@@ -259,6 +268,7 @@ public class StageTest {
MyStage stage = new MyStage();
assertInvalidValue(stage, MyParams.INT_PARAM, 100);
assertInvalidValue(stage, MyParams.LONG_PARAM, 100L);
+ assertInvalidValue(stage, MyParams.LONG_PARAM2, Integer.MAX_VALUE +
100L);
assertInvalidValue(stage, MyParams.FLOAT_PARAM, 100.0f);
assertInvalidValue(stage, MyParams.DOUBLE_PARAM, 100.0);
assertInvalidClass(stage, MyParams.INT_PARAM, "100");
@@ -281,11 +291,14 @@ public class StageTest {
stage.set(MyParams.LONG_PARAM, 50L);
Assert.assertEquals(50L, (long) stage.get(MyParams.LONG_PARAM));
+ stage.set(MyParams.LONG_PARAM2, Integer.MAX_VALUE + 50L);
+ Assert.assertEquals(Integer.MAX_VALUE + 50L, (long)
stage.get(MyParams.LONG_PARAM2));
+
stage.set(MyParams.FLOAT_PARAM, 50f);
- Assert.assertEquals(50f, (float) stage.get(MyParams.FLOAT_PARAM),
0.0001);
+ Assert.assertEquals(50f, stage.get(MyParams.FLOAT_PARAM), 0.0001);
stage.set(MyParams.DOUBLE_PARAM, 50.0);
- Assert.assertEquals(50, (double) stage.get(MyParams.DOUBLE_PARAM),
0.0001);
+ Assert.assertEquals(50, stage.get(MyParams.DOUBLE_PARAM), 0.0001);
stage.set(MyParams.STRING_PARAM, "50");
Assert.assertEquals("50", stage.get(MyParams.STRING_PARAM));
@@ -311,9 +324,39 @@ public class StageTest {
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
MyStage stage = new MyStage();
+
stage.set(stage.paramWithNullDefault, 1);
+ stage.set(MyParams.BOOLEAN_PARAM, true);
+ stage.set(MyParams.INT_PARAM, 50);
+ stage.set(MyParams.LONG_PARAM, 50L);
+ stage.set(MyParams.LONG_PARAM2, Integer.MAX_VALUE + 50L);
+ stage.set(MyParams.FLOAT_PARAM, 50f);
+ stage.set(MyParams.DOUBLE_PARAM, 50.0);
+ stage.set(MyParams.STRING_PARAM, "50");
+ stage.set(MyParams.INT_ARRAY_PARAM, new Integer[] {50, 51});
+ stage.set(MyParams.LONG_ARRAY_PARAM, new Long[] {50L, 51L});
+ stage.set(MyParams.FLOAT_ARRAY_PARAM, new Float[] {50.0f, 51.0f});
+ stage.set(MyParams.DOUBLE_ARRAY_PARAM, new Double[] {50.0, 51.0});
+ stage.set(MyParams.STRING_ARRAY_PARAM, new String[] {"50", "51"});
+
Stage<?> loadedStage = validateStageSaveLoad(tEnv, stage,
Collections.emptyMap());
- Assert.assertEquals(1, (int) loadedStage.get(MyParams.INT_PARAM));
+
+ Assert.assertEquals(1, (int)
loadedStage.get(stage.paramWithNullDefault));
+ Assert.assertEquals(true, loadedStage.get(MyParams.BOOLEAN_PARAM));
+ Assert.assertEquals(50, (int) loadedStage.get(MyParams.INT_PARAM));
+ Assert.assertEquals(50L, (long) loadedStage.get(MyParams.LONG_PARAM));
+ Assert.assertEquals(Integer.MAX_VALUE + 50L, (long)
loadedStage.get(MyParams.LONG_PARAM2));
+ Assert.assertEquals(50f, loadedStage.get(MyParams.FLOAT_PARAM),
0.0001);
+ Assert.assertEquals(50, loadedStage.get(MyParams.DOUBLE_PARAM),
0.0001);
+ Assert.assertEquals("50", loadedStage.get(MyParams.STRING_PARAM));
+ Assert.assertArrayEquals(new Integer[] {50, 51},
loadedStage.get(MyParams.INT_ARRAY_PARAM));
+ Assert.assertArrayEquals(new Long[] {50L, 51L},
loadedStage.get(MyParams.LONG_ARRAY_PARAM));
+ Assert.assertArrayEquals(
+ new Float[] {50.0f, 51.0f},
loadedStage.get(MyParams.FLOAT_ARRAY_PARAM));
+ Assert.assertArrayEquals(
+ new Double[] {50.0, 51.0},
loadedStage.get(MyParams.DOUBLE_ARRAY_PARAM));
+ Assert.assertArrayEquals(
+ new String[] {"50", "51"},
loadedStage.get(MyParams.STRING_ARRAY_PARAM));
}
@Test
@@ -323,8 +366,9 @@ public class StageTest {
MyStage stage = new MyStage();
stage.set(stage.paramWithNullDefault, 1);
Stage<?> loadedStage =
- validateStageSaveLoad(tEnv, stage,
Collections.singletonMap("intParam", 10));
- Assert.assertEquals(10, (int) loadedStage.get(MyParams.INT_PARAM));
+ validateStageSaveLoad(
+ tEnv, stage,
Collections.singletonMap("paramWithNullDefault", 10));
+ Assert.assertEquals(10, (int)
loadedStage.get(stage.paramWithNullDefault));
}
@Test
diff --git a/flink-ml-dist/pom.xml b/flink-ml-dist/pom.xml
new file mode 100644
index 0000000..8cabfa1
--- /dev/null
+++ b/flink-ml-dist/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-ml-parent</artifactId>
+ <version>2.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>flink-ml-dist_${scala.binary.version}</artifactId>
+ <name>Flink ML : Dist</name>
+ <packaging>jar</packaging>
+
+ <dependencies>
+
+ <!-- Flink ML project binaries -->
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-ml-uber_${scala.binary.version}</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Stateful Functions Dependencies -->
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>statefun-flink-core</artifactId>
+ <version>3.1.0</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-streaming-java_2.12</artifactId>
+ </exclusion>
+ </exclusions>
+ <scope>compile</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <finalName>flink-ml-${project.version}-bin</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>bin</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+
<descriptor>src/main/assemblies/bin.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/flink-ml-dist/src/main/assemblies/bin.xml
b/flink-ml-dist/src/main/assemblies/bin.xml
new file mode 100644
index 0000000..de9bc46
--- /dev/null
+++ b/flink-ml-dist/src/main/assemblies/bin.xml
@@ -0,0 +1,80 @@
+<!--
+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.
+-->
+<assembly
+
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0
http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>dir</format>
+ </formats>
+
+ <includeBaseDirectory>true</includeBaseDirectory>
+ <baseDirectory>flink-ml-${project.version}</baseDirectory>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>lib</outputDirectory>
+ <unpack>false</unpack>
+
+ <includes>
+ <include>org.apache.flink:statefun-flink-core</include>
+
<include>org.apache.flink:flink-ml-uber_${scala.binary.version}</include>
+ </includes>
+ </dependencySet>
+ </dependencySets>
+
+ <files>
+ <file>
+ <source>../LICENSE</source>
+ <outputDirectory/>
+ <fileMode>0644</fileMode>
+ </file>
+ </files>
+
+ <fileSets>
+ <!-- copy scripts -->
+ <fileSet>
+ <directory>src/main/flink-ml-bin/bin</directory>
+ <outputDirectory>bin</outputDirectory>
+ <fileMode>0755</fileMode>
+ </fileSet>
+
+ <!-- copy benchmark json configuration -->
+ <fileSet>
+ <directory>../flink-ml-benchmark/src/main/resources/</directory>
+ <outputDirectory>conf/</outputDirectory>
+ <fileMode>0644</fileMode>
+ <includes>
+ <include>*.json</include>
+ </includes>
+ </fileSet>
+
+ <!-- copy *.txt files -->
+ <fileSet>
+ <directory>src/main/flink-ml-bin/</directory>
+ <outputDirectory/>
+ <fileMode>0644</fileMode>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+
+ </fileSets>
+</assembly>
diff --git a/flink-ml-dist/src/main/flink-ml-bin/README.txt
b/flink-ml-dist/src/main/flink-ml-bin/README.txt
new file mode 100644
index 0000000..ef1de7a
--- /dev/null
+++ b/flink-ml-dist/src/main/flink-ml-bin/README.txt
@@ -0,0 +1,32 @@
+For the latest information about Flink, please visit our website at:
+
+ https://flink.apache.org
+
+and our GitHub Account
+
+ https://github.com/apache/flink
+
+If you have any questions, ask on our Mailing lists:
+
+ [email protected]
+ [email protected]
+
+This distribution includes cryptographic software. The country in
+which you currently reside may have restrictions on the import,
+possession, use, and/or re-export to another country, of
+encryption software. BEFORE using any encryption software, please
+check your country's laws, regulations and policies concerning the
+import, possession, or use, and re-export of encryption software, to
+see if this is permitted. See <http://www.wassenaar.org/> for more
+information.
+
+The U.S. Government Department of Commerce, Bureau of Industry and
+Security (BIS), has classified this software as Export Commodity
+Control Number (ECCN) 5D002.C.1, which includes information security
+software using or performing cryptographic functions with asymmetric
+algorithms. The form and manner of this Apache Software Foundation
+distribution makes it eligible for export under the License Exception
+ENC Technology Software Unrestricted (TSU) exception (see the BIS
+Export Administration Regulations, Section 740.13) for both object
+code and source code.
+
diff --git a/flink-ml-dist/src/main/flink-ml-bin/bin/flink-ml-benchmark.sh
b/flink-ml-dist/src/main/flink-ml-bin/bin/flink-ml-benchmark.sh
new file mode 100644
index 0000000..fb6b357
--- /dev/null
+++ b/flink-ml-dist/src/main/flink-ml-bin/bin/flink-ml-benchmark.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+################################################################################
+# 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.
+################################################################################
+
+current_path=$(pwd)
+flink_ml_bin_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+flink_ml_root_path="$(dirname "$flink_ml_bin_path")"
+
+# Checks FLINK_HOME.
+if [ "$FLINK_HOME" == "" ]; then
+ echo "FLINK_HOME not found. Please make sure you have installed Flink and
configured FLINK_HOME in your environment path."
+ exit 1
+fi
+
+# Checks flink version.
+expected_version="1.14"
+actual_version=`$FLINK_HOME/bin/flink --version | cut -d" " -f2 | tr -d ","`
+unsorted_versions="${expected_version}\n${actual_version}\n"
+sorted_versions=`printf ${unsorted_versions} | sort -V`
+unsorted_versions=`printf ${unsorted_versions}`
+if [ "${unsorted_versions}" != "${sorted_versions}" ]; then
+ echo "$flink_cmd $expected_version or a higher version is required, but
found $actual_version"
+ exit 1
+fi
+
+# Submits benchmark flink job.
+$FLINK_HOME/bin/flink run -c org.apache.flink.ml.benchmark.Benchmark
$FLINK_HOME/lib/flink-ml-uber*.jar ${@:1}
diff --git a/flink-ml-uber/pom.xml b/flink-ml-uber/pom.xml
index 1fb970b..95277b2 100644
--- a/flink-ml-uber/pom.xml
+++ b/flink-ml-uber/pom.xml
@@ -52,6 +52,12 @@ under the License.
<artifactId>flink-ml-lib_${scala.binary.version}</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+ <artifactId>flink-ml-benchmark_${scala.binary.version}</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>
@@ -72,6 +78,7 @@ under the License.
<include>org.apache.flink:flink-ml-core_${scala.binary.version}</include>
<include>org.apache.flink:flink-ml-iteration_${scala.binary.version}</include>
<include>org.apache.flink:flink-ml-lib_${scala.binary.version}</include>
+
<include>org.apache.flink:flink-ml-benchmark_${scala.binary.version}</include>
<include>dev.ludovic.netlib:blas</include>
</includes>
</artifactSet>
diff --git a/pom.xml b/pom.xml
index f29d1e6..4e5fd02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,6 +57,8 @@ under the License.
<module>flink-ml-lib</module>
<module>flink-ml-tests</module>
<module>flink-ml-uber</module>
+ <module>flink-ml-benchmark</module>
+ <module>flink-ml-dist</module>
</modules>
<properties>
@@ -343,6 +345,8 @@ under the License.
<exclude>**/LICENSE*</exclude>
<!-- artifacts created during release process -->
<exclude>release/**</exclude>
+ <!-- Test Data. -->
+ <exclude>flink-ml-benchmark/src/main/resources/*.json</exclude>
</excludes>
</configuration>
</plugin>