diff --git a/java/com/engflow/binaryinput/BUILD b/java/com/engflow/binaryinput/BUILD new file mode 100644 index 00000000..8252be0e --- /dev/null +++ b/java/com/engflow/binaryinput/BUILD @@ -0,0 +1,32 @@ +load("@rules_java//java:defs.bzl", "java_binary", "java_library") + +NUM_FILES = 100 + +# Generates a number of java files based on the value of NUM_FILES +# Each file is named HelloX.java where X is the number of the file +# Each file contains a class with a greetNum method that prints "Hello" + the number of the file +[genrule( + name = "Hello" + str(x), + outs = ["Hello" + str(x) + ".java"], + cmd_bash = "echo 'package com.engflow.binaryinput;" + "\n" + + "public class Hello" + str(x) + + " { public static void greetNum() { System.out.println(\"Hello " + str(x) + "\"); } }' > $@", +) for x in range(1,NUM_FILES+1)] + +# Generates a java library that contains all the generated java files +[java_library( + name = "genbinary" + str(x), + srcs = [":Hello" + str(x) + ".java" for x in range(1,NUM_FILES+1)], + visibility = ["//visibility:public"], +) for x in range(1,NUM_FILES+1)] + +# Main class +java_binary( + name = "main", + srcs = ["Main.java"], + main_class = "com.engflow.binaryinput.Main", + deps = [ + ":genbinary" + str(x) for x in range(1,NUM_FILES+1) + ], + args = [str(NUM_FILES)], +) \ No newline at end of file diff --git a/java/com/engflow/binaryinput/BenchmarkScript.py b/java/com/engflow/binaryinput/BenchmarkScript.py new file mode 100644 index 00000000..7cc90990 --- /dev/null +++ b/java/com/engflow/binaryinput/BenchmarkScript.py @@ -0,0 +1,104 @@ +import subprocess +import uuid +import os +import json + +# This script is used to benchmark the performance of Bazel builds by modifying the BUILD file to define the number of files, +# running Bazel commands to build targets, and analyzing the Bazel profile data to extract critical time and total run time. + +# Usage: +# 1. Run the script and provide the number of files, execution type (local/remote), and number of iterations when prompted. +# 2. The script will modify the BUILD file, run Bazel commands, and analyze the profile data. +# 3. The results will be printed, showing the highest critical time and total run time. + +# Modify the BUILD file to define the number of files +def modify_build_file(num_files): + with open('BUILD', 'r') as file: + lines = file.readlines() + + with open('BUILD', 'w') as file: + for line in lines: + if line.startswith('NUM_FILES'): + file.write(f'NUM_FILES = {num_files}\n') + else: + file.write(line) + +def run_bazel_command(command): + subprocess.run(command, check=True) + +# Read the Bazel profile data +def analyze_bazel_profile(profile_path): + with open(profile_path, 'r') as file: + profile_data = json.load(file) + return profile_data + +# Extract the critical time and total run time from the Bazel profile data +def extract_times(profile_data): + critical_time = 0 + start_time = None + end_time = None + + # Iterate through the events in the profile data to extract the critical time and total run time + for event in profile_data['traceEvents']: + if event.get('cat') == 'critical path component': + critical_time += event['dur'] / 1000000.0 # Convert microseconds to seconds + if event.get('cat') == 'build phase marker' and event['name'] == 'Launch Blaze': + start_time = event['ts'] / 1000000.0 # Convert microseconds to seconds + if event.get('cat') == 'build phase marker' and event['name'] == 'Complete build': + end_time = event['ts'] / 1000000.0 # Convert microseconds to seconds + + # Calculate the total run time + total_run_time = end_time - start_time if start_time and end_time else None + + return critical_time, total_run_time + + +def main(): + num_files = int(input("Enter the number of files: ")) + execution_type = input("Enter the execution type (local/remote): ") + iterations = int(input("Enter the number of iterations: ")) + + modify_build_file(num_files) + + results = [] + + # Path to the Bazel profile data + # Using an absolute path to avoid issues with the Bazel workspace + profile_path = os.path.abspath('profile.json') + + for i in range(iterations): + if execution_type == 'local': + # Clear the Bazel cache + run_bazel_command(['bazel', 'clean', '--expunge']) + # Generate the input files + targets = [f':genbinary{j}' for j in range(1, num_files + 1)] + run_bazel_command(['bazel', 'build'] + targets) + # Build the main target and generate the Bazel profile data + run_bazel_command(['bazel', 'build', f'--profile={profile_path}', ':main']) + elif execution_type == 'remote': + # Generate a unique key for the cache silo + key = str(uuid.uuid4()) + # Generate the input files + targets = [f':genbinary{j}' for j in range(1, num_files + 1)] + run_bazel_command(['bazel', 'build', '--config=engflow', f'--remote_default_exec_properties=cache-silo-key={key}'] + targets) + # Build the main target and generate the Bazel profile data + run_bazel_command(['bazel', 'build', '--config=engflow', f'--profile={profile_path}', f'--remote_default_exec_properties=cache-silo-key={key}', ':main']) + + profile_output = analyze_bazel_profile(profile_path) + critical_time, total_run_time = extract_times(profile_output) + results.append((critical_time, total_run_time)) + + critical_times = [result[0] for result in results] + total_run_times = [result[1] for result in results] + + print(results) + + # Calculate the highest critical time and total run time + critical_time_max = max(critical_times) + total_run_time_max = max(total_run_times) + + print(f'Highest Critical Time: {critical_time_max}') + print(f'Highest Total Run Time: {total_run_time_max}') + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/java/com/engflow/binaryinput/Main.java b/java/com/engflow/binaryinput/Main.java new file mode 100644 index 00000000..d430ce2f --- /dev/null +++ b/java/com/engflow/binaryinput/Main.java @@ -0,0 +1,21 @@ +package com.engflow.binaryinput; + +import java.lang.reflect.InvocationTargetException; + +public class Main { + public static void main(String[] args) { + try { + // args[0] is the number of files to read + int numFiles = Integer.parseInt(args[0]); + + // Load and run the greetNum method from each class + for(int i = 1; i <= numFiles; i++){ + Class clazz = Class.forName("com.engflow.binaryinput.Hello" + i); + clazz.getMethod("greetNum").invoke(null); + } + + } catch (ClassNotFoundException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } +} diff --git a/java/com/engflow/binaryinput/README.md b/java/com/engflow/binaryinput/README.md new file mode 100644 index 00000000..55cba586 --- /dev/null +++ b/java/com/engflow/binaryinput/README.md @@ -0,0 +1,52 @@ +# Multiple Binary Input Example + +## Overview + +The goal of this example project is to test the performance of Engflow's remote execution and caching service based on the number of input binary files in the dependency graph. The project contains a `genrule` that generates a specified number of Java binaries for the `genbinary` Java library, which are then listed as dependencies in the main binary. The `Main.java` file loops through each generated class and calls its `greetNum` method. + +## Project Structure + +- `java/com/engflow/binaryinput/Main.java`: Main class that dynamically loads and invokes methods from generated classes. +- `java/com/engflow/binaryinput/BUILD`: Bazel build file for the `main` java binary and the `genbinary` library. + +## Usage + +To generate the test files, build the `genbinary` library using the `genrule`: +```sh +bazel build //java/com/engflow/binaryinput:genbinary +``` + +Then, the program can be run with the following command: +```sh +bazel run //java/com/engflow/binaryinput:main +``` + +## How It Works + +1. **Generation of Java Binaries:** + - The `genrule` in the `BUILD` file generates a specified number of Java classes (`Hello1.java`, `Hello2.java`, ..., `HelloN.java`). + - Each generated class contains a `greetNum` method that prints a unique message. + +2. **Main Class Execution:** + - The `Main.java` file in `binaryinput` dynamically loads each generated class using reflection. + - It then invokes the `greetNum` method of each class, printing the corresponding message. + +## Configuration + +The number of generated files is controlled by the `NUM_FILES` variable in the `BUILD` file of the `binaryinput` package. Modify this variable to change the number of generated classes and observe the performance impact on Engflow's remote execution and caching service. + +## Example + +To generate and run the program with 10 input binary files: + +1. Set `NUM_FILES` to 10 in `java/com/engflow/binaryinput/BUILD`. +2. Build the `genbinary` library: + ```sh + bazel build //java/com/engflow/binaryinput:genbinary + ``` +3. Run the `main` binary: + ```sh + bazel run //java/com/engflow/binaryinput:main + ``` + +This will generate 10 Java classes, build the `genbinary` library, and run the `main` binary, which will print messages from each generated class. \ No newline at end of file diff --git a/java/com/engflow/cycleexample/README.md b/java/com/engflow/cycleexample/README.md new file mode 100644 index 00000000..ec5a14ba --- /dev/null +++ b/java/com/engflow/cycleexample/README.md @@ -0,0 +1,36 @@ +# Cyclical Dependency Example + +## Overview + +This project is designed to evaluate and benchmark the performance of EngFlow's remote execution and caching services. It specifically focuses on testing scenarios involving cyclical-like structures in the dependency graph, which are handled through interfaces and constructor injection. + +## Purpose + +The primary objectives of this project are to: + +1. **Generate Performance Data**: Create examples with cyclical-like dependencies to test and gather performance data for EngFlow’s remote caching and execution services. +2. **Benchmark Analysis**: Compare the performance of local versus remote execution and caching to evaluate the efficiency and effectiveness of the service. +3. **Support Automation Development**: Contribute to the development of automation algorithms for resource assignment by providing valuable data on how cyclical dependencies impact performance. + +## Project Structure + +The project is organized into several packages, each representing different components of the cyclical dependency example: + +- `class_a`: Contains `ClassA` which depends on `ClassB` through an interface. +- `class_b`: Contains `ClassB` which implements `InterfaceB` and depends on `ClassC`. +- `class_c`: Contains `ClassC` which implements `InterfaceA` and can be initialized with a reference to `ClassA`. +- `interface_a`: Defines the interface `InterfaceA` implemented by `ClassA` and `ClassC`. +- `interface_b`: Defines the interface `InterfaceB` implemented by `ClassB`. +- `main`: Contains the `Main` class which processes the input file. +- `input`: Contains the input text file used by the `Main` class. + +## How the Program Works + +The program takes a text input file and recursively prints each word with each class (`ClassA` prints a word, then `ClassB`, and so on) until the string is empty. The input file should be specified in the `data` attribute of the `java_binary` rule in the `BUILD` file. + +## How to Run Tests + +To run the tests and gather performance data, use the following Bazel command: + +```sh +bazel test //java/com/engflow/cycleexample/class_a:class_a_test diff --git a/java/com/engflow/cycleexample/class_a/BUILD b/java/com/engflow/cycleexample/class_a/BUILD new file mode 100644 index 00000000..c7b362c2 --- /dev/null +++ b/java/com/engflow/cycleexample/class_a/BUILD @@ -0,0 +1,22 @@ +package(default_visibility = ["//visibility:public"]) + +java_library( + name = "class_a", + srcs = ["ClassA.java"], + deps = [ + "//java/com/engflow/cycleexample/class_b:class_b", + "//java/com/engflow/cycleexample/interface_a:interface_a", + ], +) + +java_test( + name = "class_a_test", + srcs = ["ClassATest.java"], + test_class = "com.engflow.cycleexample.class_a.ClassATest", + deps = [ + ":class_a", + "//java/com/engflow/cycleexample/class_b:class_b", + "//java/com/engflow/cycleexample/class_c:class_c", + "//java/com/engflow/cycleexample/interface_a:interface_a", + ], +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_a/ClassA.java b/java/com/engflow/cycleexample/class_a/ClassA.java new file mode 100644 index 00000000..12472673 --- /dev/null +++ b/java/com/engflow/cycleexample/class_a/ClassA.java @@ -0,0 +1,31 @@ +package com.engflow.cycleexample.class_a; + +import com.engflow.cycleexample.class_b.ClassB; +import com.engflow.cycleexample.interface_a.InterfaceA; + +public class ClassA implements InterfaceA { + private ClassB classB; + + public ClassA(ClassB classB) { + this.classB = classB; + } + + @Override + public void methodA(String input) { + // If the input is null or empty, return immediately + if (input == null || input.isEmpty()) { + return; + } + + //Find the index of the first space character in the input string. + int spaceIndex = input.indexOf(' '); + //Extract the word from the beginning of the input string up to the space character. + String word = (spaceIndex == -1) ? input : input.substring(0, spaceIndex); + //Extract the remaining part of the input string after the space character. + String remaining = (spaceIndex == -1) ? "" : input.substring(spaceIndex + 1); + + //Print the word extracted from the input string. + System.out.println("ClassA: " + word); + classB.methodB(remaining); + } +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_a/ClassATest.java b/java/com/engflow/cycleexample/class_a/ClassATest.java new file mode 100644 index 00000000..884f5fb4 --- /dev/null +++ b/java/com/engflow/cycleexample/class_a/ClassATest.java @@ -0,0 +1,42 @@ +package com.engflow.cycleexample.class_a; + +import com.engflow.cycleexample.class_b.ClassB; +import com.engflow.cycleexample.class_c.ClassC; +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class ClassATest { + private static class TestClassB extends ClassB { + boolean methodBCalled = false; + + public TestClassB(ClassC classC) { + super(classC); + } + + @Override + public void methodB(String input) { + methodBCalled = true; + } + } + + @Test + public void testMethodA() { + // Create a ClassC instance with a null ClassA object + ClassC classC = new ClassC(null); + + // Create a TestClassB instance with the ClassC object + TestClassB testClassB = new TestClassB(classC); + + // Create a new ClassA instance with the TestClassB object + ClassA classA = new ClassA(testClassB); + + // Properly initialize classC with classA + classC.setClassA(classA); + + // Call methodA on classA with a sample input + classA.methodA("sample input"); + + // Verify that methodB on the TestClassB was called + assertTrue(testClassB.methodBCalled); + } +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_b/BUILD b/java/com/engflow/cycleexample/class_b/BUILD new file mode 100644 index 00000000..8cff66a1 --- /dev/null +++ b/java/com/engflow/cycleexample/class_b/BUILD @@ -0,0 +1,21 @@ +package(default_visibility = ["//visibility:public"]) + +java_library( + name = "class_b", + srcs = ["ClassB.java"], + deps = [ + "//java/com/engflow/cycleexample/class_c:class_c", + "//java/com/engflow/cycleexample/interface_b:interface_b", + ], +) + +java_test( + name = "class_b_test", + srcs = ["ClassBTest.java"], + test_class = "com.engflow.cycleexample.class_b.ClassBTest", + deps = [ + ":class_b", + "//java/com/engflow/cycleexample/class_c:class_c", + "//java/com/engflow/cycleexample/interface_b:interface_b", + ], +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_b/ClassB.java b/java/com/engflow/cycleexample/class_b/ClassB.java new file mode 100644 index 00000000..75a0f3ff --- /dev/null +++ b/java/com/engflow/cycleexample/class_b/ClassB.java @@ -0,0 +1,31 @@ +package com.engflow.cycleexample.class_b; + +import com.engflow.cycleexample.class_c.ClassC; +import com.engflow.cycleexample.interface_b.InterfaceB; + +public class ClassB implements InterfaceB { + private ClassC classC; + + public ClassB(ClassC classC) { + this.classC = classC; + } + + @Override + public void methodB(String input) { + // If the input is null or empty, return immediately + if (input == null || input.isEmpty()) { + return; + } + + //Find the index of the first space character in the input string. + int spaceIndex = input.indexOf(' '); + //Extract the word from the beginning of the input string up to the space character. + String word = (spaceIndex == -1) ? input : input.substring(0, spaceIndex); + //Extract the remaining part of the input string after the space character. + String remaining = (spaceIndex == -1) ? "" : input.substring(spaceIndex + 1); + + //Print the word extracted from the input string. + System.out.println("ClassB: " + word); + classC.methodA(remaining); + } +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_b/ClassBTest.java b/java/com/engflow/cycleexample/class_b/ClassBTest.java new file mode 100644 index 00000000..bf70888b --- /dev/null +++ b/java/com/engflow/cycleexample/class_b/ClassBTest.java @@ -0,0 +1,35 @@ +package com.engflow.cycleexample.class_b; + +import com.engflow.cycleexample.class_c.ClassC; +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class ClassBTest { + private static class TestClassC extends ClassC { + boolean methodACalled = false; + + public TestClassC() { + super(null); + } + + @Override + public void methodA(String input) { + methodACalled = true; + } + } + + @Test + public void testMethodB() { + // Create a TestClassC instance + TestClassC testClassC = new TestClassC(); + + // Create an instance of ClassB with the TestClassC object + ClassB classB = new ClassB(testClassC); + + // Call methodB on classB + classB.methodB("Sample input"); + + // Verify that methodA on the TestClassC was called + assertTrue(testClassC.methodACalled); + } +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_c/BUILD b/java/com/engflow/cycleexample/class_c/BUILD new file mode 100644 index 00000000..baaf955d --- /dev/null +++ b/java/com/engflow/cycleexample/class_c/BUILD @@ -0,0 +1,20 @@ +package(default_visibility = ["//visibility:public"]) + +java_library( + name = "class_c", + srcs = ["ClassC.java"], + visibility = ["//visibility:public"], + deps = [ + "//java/com/engflow/cycleexample/interface_a:interface_a", + ], +) + +java_test( + name = "class_c_test", + srcs = ["ClassCTest.java"], + test_class = "com.engflow.cycleexample.class_c.ClassCTest", + deps = [ + ":class_c", + "//java/com/engflow/cycleexample/class_a:class_a", + ], +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_c/ClassC.java b/java/com/engflow/cycleexample/class_c/ClassC.java new file mode 100644 index 00000000..3a70622b --- /dev/null +++ b/java/com/engflow/cycleexample/class_c/ClassC.java @@ -0,0 +1,38 @@ +package com.engflow.cycleexample.class_c; + +import com.engflow.cycleexample.interface_a.InterfaceA; + +public class ClassC implements InterfaceA { + private InterfaceA classA; + + public ClassC(InterfaceA classA) { + this.classA = classA; + } + + @Override + public void methodA(String input) { + // If the input is null or empty, return immediately + if (input == null || input.isEmpty()) { + return; + } + + // Find the index of the first space character in the input string. + int spaceIndex = input.indexOf(' '); + // Extract the word from the beginning of the input string up to the space character. + String word = (spaceIndex == -1) ? input : input.substring(0, spaceIndex); + // Extract the remaining part of the input string after the space character. + String remaining = (spaceIndex == -1) ? "" : input.substring(spaceIndex + 1); + + // Print the word extracted from the input string. + System.out.println("ClassC: " + word); + classA.methodA(remaining); + } + + /** + * Set the classA field of this class. + * @param classA + */ + public void setClassA(InterfaceA classA) { + this.classA = classA; + } +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/class_c/ClassCTest.java b/java/com/engflow/cycleexample/class_c/ClassCTest.java new file mode 100644 index 00000000..05eb3f71 --- /dev/null +++ b/java/com/engflow/cycleexample/class_c/ClassCTest.java @@ -0,0 +1,35 @@ +package com.engflow.cycleexample.class_c; + +import com.engflow.cycleexample.class_a.ClassA; +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class ClassCTest { + private static class TestClassA extends ClassA { + boolean methodACalled = false; + + public TestClassA() { + super(null); + } + + @Override + public void methodA(String input) { + methodACalled = true; + } + } + + @Test + public void testMethodA() { + // Create a TestClassA instance + TestClassA testClassA = new TestClassA(); + + // Create an instance of ClassC with the TestClassA object + ClassC classC = new ClassC(testClassA); + + // Call methodA on classC + classC.methodA("sample input"); + + // Verify that methodA on the TestClassA was called + assertTrue(testClassA.methodACalled); + } +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/input/BUILD b/java/com/engflow/cycleexample/input/BUILD new file mode 100644 index 00000000..4b9e382f --- /dev/null +++ b/java/com/engflow/cycleexample/input/BUILD @@ -0,0 +1,5 @@ +filegroup( + name = "input", + srcs = glob(["*.txt"]), + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/input/input.txt b/java/com/engflow/cycleexample/input/input.txt new file mode 100644 index 00000000..7e0f7d52 --- /dev/null +++ b/java/com/engflow/cycleexample/input/input.txt @@ -0,0 +1 @@ +Sie stellte das Tschechische Streichholzschächtelchen auf den Tisch, auf den Tisch stellte sie das Tschechische Streichholzschächtelchen \ No newline at end of file diff --git a/java/com/engflow/cycleexample/interface_a/BUILD b/java/com/engflow/cycleexample/interface_a/BUILD new file mode 100644 index 00000000..5f6a706b --- /dev/null +++ b/java/com/engflow/cycleexample/interface_a/BUILD @@ -0,0 +1,5 @@ +java_library( + name = "interface_a", + srcs = ["InterfaceA.java"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/interface_a/InterfaceA.java b/java/com/engflow/cycleexample/interface_a/InterfaceA.java new file mode 100644 index 00000000..77f83f6a --- /dev/null +++ b/java/com/engflow/cycleexample/interface_a/InterfaceA.java @@ -0,0 +1,5 @@ +package com.engflow.cycleexample.interface_a; + +public interface InterfaceA { + void methodA(String input); +} diff --git a/java/com/engflow/cycleexample/interface_b/BUILD b/java/com/engflow/cycleexample/interface_b/BUILD new file mode 100644 index 00000000..fc1c1352 --- /dev/null +++ b/java/com/engflow/cycleexample/interface_b/BUILD @@ -0,0 +1,5 @@ +java_library( + name = "interface_b", + srcs = ["InterfaceB.java"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/interface_b/InterfaceB.java b/java/com/engflow/cycleexample/interface_b/InterfaceB.java new file mode 100644 index 00000000..7cfd152b --- /dev/null +++ b/java/com/engflow/cycleexample/interface_b/InterfaceB.java @@ -0,0 +1,5 @@ +package com.engflow.cycleexample.interface_b; + +public interface InterfaceB { + void methodB(String input); +} \ No newline at end of file diff --git a/java/com/engflow/cycleexample/main/BUILD b/java/com/engflow/cycleexample/main/BUILD new file mode 100644 index 00000000..b9fa6a1b --- /dev/null +++ b/java/com/engflow/cycleexample/main/BUILD @@ -0,0 +1,15 @@ +load("@rules_java//java:defs.bzl", "java_binary") + +java_binary( + name = "main", + srcs = ["Main.java"], + deps = [ + "//java/com/engflow/cycleexample/class_a:class_a", + "//java/com/engflow/cycleexample/class_b:class_b", + "//java/com/engflow/cycleexample/class_c:class_c", + ], + data = [ + "//java/com/engflow/cycleexample/input:input", + ], + main_class = "com.engflow.cycleexample.main.Main", +) \ No newline at end of file diff --git a/java/com/engflow/cycleexample/main/Main.java b/java/com/engflow/cycleexample/main/Main.java new file mode 100644 index 00000000..35d3b18d --- /dev/null +++ b/java/com/engflow/cycleexample/main/Main.java @@ -0,0 +1,39 @@ +package com.engflow.cycleexample.main; + +import com.engflow.cycleexample.class_a.ClassA; +import com.engflow.cycleexample.class_b.ClassB; +import com.engflow.cycleexample.class_c.ClassC; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +public class Main { + public static void main(String[] args) { + //Input file path + String fileName = "com/engflow/cycleexample/input/input.txt"; + StringBuilder content = new StringBuilder(); + + //Read the input file and store the content in a StringBuilder + try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { + String line; + while ((line = br.readLine()) != null) { + content.append(line).append(" "); + } + } catch (IOException e) { + e.printStackTrace(); + } + + String input = content.toString().trim(); + + // Create instances of ClassA, ClassB, and ClassC + ClassC classC = new ClassC(null); + ClassB classB = new ClassB(classC); + ClassA classA = new ClassA(classB); + + // Properly initialize classC with classA + classC.setClassA(classA); + + classA.methodA(input); + } +} \ No newline at end of file diff --git a/java/com/engflow/setinput/README.md b/java/com/engflow/setinput/README.md new file mode 100644 index 00000000..4e6ae765 --- /dev/null +++ b/java/com/engflow/setinput/README.md @@ -0,0 +1,26 @@ +# Set Inputs Example + +## Overview + +The goal of this example project is to test the performance of Engflow's remote execution and caching service for different input sizes. The project contains a genrule that writes a set number of `.txt` files in the `genfile` directory. The content of this files is then read and printed out with the `Reader` class. + +## Project Structure + +- `java/com/engflow/setinput/genfile`: Directory where the `.txt` files generated by the genrule are stored. +- `java/com/engflow/varyinginputs/main`: Contains the `Main` class which orchestrates the reading process. +- `java/com/engflow/varyinginputs/reader`: Contains the `Reader` class responsible for reading the files. + +## Usage + +To generate the test files, build the filegroup target: +```sh +bazel build //java/com/engflow/setinput/genfile +``` + +Then, the program can be run with the following command: + +```sh +bazel run //java/com/engflow/varyinginputs/main +``` + +To modify the amount of files generated, change the value of the `NUM_FILES` in the `BUILD` file inside the `genfile` directory. \ No newline at end of file diff --git a/java/com/engflow/setinput/genfile/BUILD b/java/com/engflow/setinput/genfile/BUILD new file mode 100644 index 00000000..dbfb5d29 --- /dev/null +++ b/java/com/engflow/setinput/genfile/BUILD @@ -0,0 +1,12 @@ +NUM_FILES = 16 + +[genrule( + name = "benchmark_test_file" + str(x), + outs = ["benchmark_test_file" + str(x) + ".txt"], + cmd_bash = "echo 'benchmark test file " + str(x) + "' > $@", +) for x in range(1,NUM_FILES+1)] + +filegroup( + name = "genfile", + srcs = [":benchmark_test_file" + str(x) for x in range(1,NUM_FILES+1)], +) \ No newline at end of file diff --git a/java/com/engflow/setinput/genreader/BUILD b/java/com/engflow/setinput/genreader/BUILD new file mode 100644 index 00000000..42c01404 --- /dev/null +++ b/java/com/engflow/setinput/genreader/BUILD @@ -0,0 +1,5 @@ +java_library( + name = "genreader", + srcs = ["GenReader.java"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/java/com/engflow/setinput/genreader/GenReader.java b/java/com/engflow/setinput/genreader/GenReader.java new file mode 100644 index 00000000..72c4b18c --- /dev/null +++ b/java/com/engflow/setinput/genreader/GenReader.java @@ -0,0 +1,35 @@ +package com.engflow.setinput.genreader; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.BufferedReader; + +public class GenReader { + + /** + * Reads the content of the specified file and prints it to the console. + * @param inputPath + */ + public void readFiles(String inputPath) { + File file = new File(inputPath); + + // Check if the file exists and is a file + if (!file.exists() || !file.isFile()) { + System.out.println("Invalid file path: " + inputPath + "\n Absolute path: " + file.getAbsolutePath()+ + "\n File: " + file); + return; + } + + // Read the content of the file and print it to the console + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + System.out.println("Reading file: " + file.getName()); + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java/com/engflow/setinput/main/BUILD b/java/com/engflow/setinput/main/BUILD new file mode 100644 index 00000000..6346ce2c --- /dev/null +++ b/java/com/engflow/setinput/main/BUILD @@ -0,0 +1,11 @@ +load("@rules_java//java:defs.bzl", "java_binary") + +java_binary( + name = "main", + srcs = ["Main.java"], + deps = [ + "//java/com/engflow/setinput/genreader:genreader", + ], + data = glob(["java/com/engflow/setinput/genfile/*.txt"]), + main_class = "com.engflow.setinput.main.Main", +) \ No newline at end of file diff --git a/java/com/engflow/setinput/main/Main.java b/java/com/engflow/setinput/main/Main.java new file mode 100644 index 00000000..a5ce9890 --- /dev/null +++ b/java/com/engflow/setinput/main/Main.java @@ -0,0 +1,34 @@ +package com.engflow.setinput.main; + +import com.engflow.setinput.genreader.GenReader; + +import java.nio.file.Paths; +import java.nio.file.Files; +import java.io.IOException; + +public class Main { + + public static void main(String[] args) { + String genfilePath = "com/engflow/setinput/genfile"; // Path relative to bazel-bin + String workspacePath = System.getenv("BUILD_WORKING_DIRECTORY"); + //System.out.println("Workspace path: " + workspacePath); + + if (workspacePath == null) { + System.out.println("Workspace path could not be determined."); + return; + } + + String bazelBinPath = Paths.get(workspacePath, "bazel-bin").toString(); + //System.out.println("Bazel-bin path: " + bazelBinPath); + String fullPath = Paths.get(bazelBinPath, genfilePath).toString(); + + GenReader reader = new GenReader(); + try { + Files.list(Paths.get(fullPath)) + .filter(Files::isRegularFile) + .forEach(file -> reader.readFiles(file.toString())); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/README.md b/java/com/engflow/varyinginputs/README.md new file mode 100644 index 00000000..deea7ac1 --- /dev/null +++ b/java/com/engflow/varyinginputs/README.md @@ -0,0 +1,34 @@ +# Varying Inputs Example + +## Overview + +The goal of this example project is to test the performance of Engflow's remote execution and caching service for different input sizes. The project involves creating a specified number of txt files, each containing 100 random characters, using the `Writer` class, and then reading and printing out their contents with the `Reader` class. + +## Project Structure + +- `java/com/engflow/varyinginputs/input`: Directory where the generated `.txt` files are saved. +- `java/com/engflow/varyinginputs/main`: Contains the `Main` class which orchestrates the file creation and reading process. +- `java/com/engflow/varyinginputs/reader`: Contains the `Reader` class responsible for reading the files. +- `java/com/engflow/varyinginputs/writer`: Contains the `Writer` class responsible for writing the files. + +## Usage + +To run the project and specify the number of files to be created, use the following command: + +```sh +bazel run //java/com/engflow/varyinginputs/main -- +``` + +Replace `` with the desired number of files to be created. + +## Example + +To create and read 10 files, you would run: + +```sh +bazel run //java/com/engflow/varyinginputs/main -- 10 +``` + +This command will: +1. Use the `Writer` class to create 10 files, each containing 100 random characters, in the `input` directory. +2. Use the `Reader` class to read and print the contents of these files. \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/input/BUILD b/java/com/engflow/varyinginputs/input/BUILD new file mode 100644 index 00000000..4b9e382f --- /dev/null +++ b/java/com/engflow/varyinginputs/input/BUILD @@ -0,0 +1,5 @@ +filegroup( + name = "input", + srcs = glob(["*.txt"]), + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/main/BUILD b/java/com/engflow/varyinginputs/main/BUILD new file mode 100644 index 00000000..f5302b9e --- /dev/null +++ b/java/com/engflow/varyinginputs/main/BUILD @@ -0,0 +1,14 @@ +load("@rules_java//java:defs.bzl", "java_binary") + +java_binary( + name = "main", + srcs = ["Main.java"], + deps = [ + "//java/com/engflow/varyinginputs/reader:reader", + "//java/com/engflow/varyinginputs/writer:writer", + ], + data = [ + "//java/com/engflow/varyinginputs/input:input", + ], + main_class = "com.engflow.varyinginputs.main.Main", +) \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/main/Main.java b/java/com/engflow/varyinginputs/main/Main.java new file mode 100644 index 00000000..b8d44e86 --- /dev/null +++ b/java/com/engflow/varyinginputs/main/Main.java @@ -0,0 +1,30 @@ +package com.engflow.varyinginputs.main; + +import com.engflow.varyinginputs.reader.Reader; +import com.engflow.varyinginputs.writer.Writer; + +public class Main { + + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("Please provide the number of files as an argument."); + return; + } + + int numberOfFiles; + try { + numberOfFiles = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + System.out.println("Invalid number format: " + args[0]); + return; + } + + Writer writer = new Writer(); + Reader reader = new Reader(); + + String filePath = "com/engflow/varyinginputs/input"; + + writer.writeFiles(numberOfFiles, filePath); + reader.readFiles(filePath); + } +} \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/reader/BUILD b/java/com/engflow/varyinginputs/reader/BUILD new file mode 100644 index 00000000..4033aea4 --- /dev/null +++ b/java/com/engflow/varyinginputs/reader/BUILD @@ -0,0 +1,14 @@ +java_library( + name = "reader", + srcs = ["Reader.java"], + visibility = ["//visibility:public"], +) + +java_test( + name = "reader_test", + srcs = ["ReaderTest.java"], + test_class = "com.engflow.varyinginputs.reader.ReaderTest", + deps = [ + ":reader", + ], +) \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/reader/Reader.java b/java/com/engflow/varyinginputs/reader/Reader.java new file mode 100644 index 00000000..7d106f2c --- /dev/null +++ b/java/com/engflow/varyinginputs/reader/Reader.java @@ -0,0 +1,45 @@ +package com.engflow.varyinginputs.reader; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.BufferedReader; + +public class Reader { + + /** + * Reads the content of all files in the specified directory and prints it to the console. + * @param inputPath + */ + public void readFiles(String inputPath) { + File directory = new File(inputPath); + + // Check if the directory exists and is a directory + if (!directory.exists() || !directory.isDirectory()) { + System.out.println("Invalid directory path: " + inputPath); + return; + } + + // List all files in the directory and check if there are any + File[] files = directory.listFiles(); + if (files == null || files.length == 0) { + System.out.println("No files found in the directory: " + inputPath); + return; + } + + // Read the content of each file and print it to the console + for (File file : files) { + if (file.isFile()) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + System.out.println("Reading file: " + file.getName()); + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/reader/ReaderTest.java b/java/com/engflow/varyinginputs/reader/ReaderTest.java new file mode 100644 index 00000000..a303c364 --- /dev/null +++ b/java/com/engflow/varyinginputs/reader/ReaderTest.java @@ -0,0 +1,65 @@ +package com.engflow.varyinginputs.reader; + +import org.junit.Test; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import static org.junit.Assert.assertTrue; + +public class ReaderTest { + + @Test + public void testReadFilesPrintsFileContents() throws IOException { + Reader reader = new Reader(); + String inputPath = "test_input"; + String fileName = "testFile.txt"; + String fileContent = "Hello, World!"; + + // Set up test files + File directory = new File(inputPath); + directory.mkdirs(); + File testFile = new File(inputPath + "/" + fileName); + try (FileWriter writer = new FileWriter(testFile)) { + writer.write(fileContent); + } + + // Capture the output + java.io.ByteArrayOutputStream outContent = new java.io.ByteArrayOutputStream(); + System.setOut(new java.io.PrintStream(outContent)); + + // Run the method + reader.readFiles(inputPath); + + // Verify the output + String expectedOutput = "Reading file: " + fileName + "\n" + fileContent + "\n"; + assertTrue(outContent.toString().contains(expectedOutput)); + + // Clean up after test + testFile.delete(); + directory.delete(); + } + + @Test + public void testReadFilesHandlesEmptyDirectory() { + Reader reader = new Reader(); + String inputPath = "empty_test_input"; + + // Set up empty directory + File directory = new File(inputPath); + directory.mkdirs(); + + // Capture the output + java.io.ByteArrayOutputStream outContent = new java.io.ByteArrayOutputStream(); + System.setOut(new java.io.PrintStream(outContent)); + + // Run the method + reader.readFiles(inputPath); + + // Verify the output + String expectedOutput = "No files found in the directory: " + inputPath + "\n"; + assertTrue(outContent.toString().contains(expectedOutput)); + + // Clean up after test + directory.delete(); + } +} \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/writer/BUILD b/java/com/engflow/varyinginputs/writer/BUILD new file mode 100644 index 00000000..1fc02ea5 --- /dev/null +++ b/java/com/engflow/varyinginputs/writer/BUILD @@ -0,0 +1,14 @@ +java_library( + name = "writer", + srcs = ["Writer.java"], + visibility = ["//visibility:public"], +) + +java_test( + name = "writer_test", + srcs = ["WriterTest.java"], + test_class = "com.engflow.varyinginputs.writer.WriterTest", + deps = [ + ":writer", + ], +) \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/writer/Writer.java b/java/com/engflow/varyinginputs/writer/Writer.java new file mode 100644 index 00000000..5d911ecc --- /dev/null +++ b/java/com/engflow/varyinginputs/writer/Writer.java @@ -0,0 +1,61 @@ +package com.engflow.varyinginputs.writer; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Random; +import java.util.UUID; + +public class Writer { + + /** + * Write the specified number of files to the output path. + * @param numberOfFiles + * @param outputPath + */ + public void writeFiles(int numberOfFiles, String outputPath) { + File directory = new File(outputPath); + + if (!directory.exists()) { + directory.mkdirs(); + } + + // list all files in the directory and count them + File[] existingFiles = directory.listFiles(); + int existingFilesCount = existingFiles != null ? existingFiles.length : 0; + + Random random = new Random(); + + // create new files if the number of existing files is less than the required number + if (existingFilesCount < numberOfFiles) { + for (int i = existingFilesCount; i < numberOfFiles; i++) { + // create a new file with a unique name to avoid conflicts + String fileName = outputPath + "/file" + UUID.randomUUID() + ".txt"; + File file = new File(fileName); + + try { + // write 100 random characters to the file + file.createNewFile(); + FileWriter writer = new FileWriter(file); + for (int j = 0; j < 100; j++) { + char randomChar = (char) (random.nextInt(26) + 'a'); + writer.write(randomChar); + } + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + // delete files if the number of existing files is greater than the required number + else if (existingFilesCount > numberOfFiles) { + while (existingFilesCount > numberOfFiles) { + int fileIndex = random.nextInt(existingFilesCount); + File fileToDelete = existingFiles[fileIndex]; + if (fileToDelete.delete()) { + existingFilesCount--; + } + } + } + } +} \ No newline at end of file diff --git a/java/com/engflow/varyinginputs/writer/WriterTest.java b/java/com/engflow/varyinginputs/writer/WriterTest.java new file mode 100644 index 00000000..3d692139 --- /dev/null +++ b/java/com/engflow/varyinginputs/writer/WriterTest.java @@ -0,0 +1,66 @@ +package com.engflow.varyinginputs.writer; + +import org.junit.Test; +import java.io.File; +import java.io.IOException; +import static org.junit.Assert.assertEquals; + +public class WriterTest { + + @Test + public void testWriteFilesCreatesCorrectNumberOfFiles() throws IOException { + Writer writer = new Writer(); + String outputPath = "test_output"; + int numberOfFiles = 5; + + // Clean up before test + File directory = new File(outputPath); + if (directory.exists()) { + for (File file : directory.listFiles()) { + file.delete(); + } + directory.delete(); + } + + // Run the method + writer.writeFiles(numberOfFiles, outputPath); + + // Verify the number of files created + File[] files = new File(outputPath).listFiles(); + assertEquals(numberOfFiles, files.length); + + // Clean up after test + for (File file : files) { + file.delete(); + } + new File(outputPath).delete(); + } + + @Test + public void testWriteFilesDeletesExcessFiles() throws IOException { + Writer writer = new Writer(); + String outputPath = "test_output"; + int initialNumberOfFiles = 10; + int finalNumberOfFiles = 5; + + // Create initial files + File directory = new File(outputPath); + directory.mkdirs(); + for (int i = 0; i < initialNumberOfFiles; i++) { + new File(outputPath + "/file" + i + ".txt").createNewFile(); + } + + // Run the method + writer.writeFiles(finalNumberOfFiles, outputPath); + + // Verify the number of files after deletion + File[] files = new File(outputPath).listFiles(); + assertEquals(finalNumberOfFiles, files.length); + + // Clean up after test + for (File file : files) { + file.delete(); + } + new File(outputPath).delete(); + } +} \ No newline at end of file