diff --git a/ServerBenchmark/build.gradle b/ServerBenchmark/build.gradle new file mode 100644 index 0000000..000cbf3 --- /dev/null +++ b/ServerBenchmark/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'java-library' +} + +dependencies { + compile group: 'com.google.guava', name: 'guava', version: '25.0-jre' + compile group: 'org.jetbrains', name: 'annotations', version: '15.0' + compile group: 'org.projectlombok', name: 'lombok', version: '1.16.20' + compile group: 'commons-io', name: 'commons-io', version: '2.6' + compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.5.1' + compile group: 'jfree', name: 'jfreechart', version: '1.0.12' + compile group: 'com.jcraft', name: 'jsch', version: '0.1.52' + + testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3' + testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.1.0' + testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.0.0-M4' + testCompile group: 'org.mockito', name: 'mockito-all', version: '1.9.5' + + testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.1.0' + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.1.0' + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.1.0' +} + + +repositories { + jcenter() +} + +task wrapper(type: Wrapper) { + description = 'Generates gradlew[.bat] scripts' + gradleVersion = '4.6' +} diff --git a/ServerBenchmark/gradle/wrapper/gradle-wrapper.jar b/ServerBenchmark/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/ServerBenchmark/gradle/wrapper/gradle-wrapper.jar differ diff --git a/ServerBenchmark/gradle/wrapper/gradle-wrapper.properties b/ServerBenchmark/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bf3de21 --- /dev/null +++ b/ServerBenchmark/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/ServerBenchmark/gradlew b/ServerBenchmark/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/ServerBenchmark/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/ServerBenchmark/gradlew.bat b/ServerBenchmark/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/ServerBenchmark/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/ServerBenchmark/results/logs/Blocking_ArraySize.txt b/ServerBenchmark/results/logs/Blocking_ArraySize.txt new file mode 100644 index 0000000..f1ff8f7 --- /dev/null +++ b/ServerBenchmark/results/logs/Blocking_ArraySize.txt @@ -0,0 +1,42 @@ +Server type: BlockingArray size: 5000 +Clients number: 25 +Delay: 200 +Queries number: 25 +Altering option: ArraySize +1000 10000 1000 +TaskTime +10 +1000 5.9872 +2000 15.216 +3000 18.0544 +4000 34.2496 +5000 46.7072 +6000 60.9648 +7000 80.0992 +8000 103.2432 +9000 139.2432 +10000 181.6352 +ClientTime +10 +1000 349.44 +2000 483.28 +3000 701.44 +4000 727.16 +5000 875.68 +6000 794.8 +7000 983.44 +8000 1034.72 +9000 1391.64 +10000 1522.8 +ServerTime +10 +1000 18.1664 +2000 36.9168 +3000 49.0976 +4000 141.0704 +5000 271.4112 +6000 199.9296 +7000 229.4272 +8000 451.0144 +9000 607.3536 +10000 1110.0096 diff --git a/ServerBenchmark/results/logs/Blocking_ClientsNumber.txt b/ServerBenchmark/results/logs/Blocking_ClientsNumber.txt new file mode 100644 index 0000000..2db0e89 --- /dev/null +++ b/ServerBenchmark/results/logs/Blocking_ClientsNumber.txt @@ -0,0 +1,42 @@ +Server type: BlockingArray size: 10000 +Clients number: 25 +Delay: 200 +Queries number: 20 +Altering option: ClientsNumber +10 100 10 +TaskTime +10 +10 184.64646464646464 +20 183.985 +30 182.46333333333334 +40 180.06125 +50 180.081 +60 179.75416666666666 +70 178.325 +80 186.181875 +90 192.13055555555556 +100 190.952 +ClientTime +10 +10 781.2 +20 1322.25 +30 2061.8 +40 2612.1 +50 3160.82 +60 3489.0333333333333 +70 4196.0142857142855 +80 4858.5125 +90 5631.3 +100 6458.75 +ServerTime +10 +10 415.89 +20 718.695 +30 971.3816666666667 +40 1503.8775 +50 1324.76 +60 1937.2608333333333 +70 2147.4785714285713 +80 3223.5475 +90 5015.8415786548085 +100 5291.949 diff --git a/ServerBenchmark/results/logs/Blocking_Delay.txt b/ServerBenchmark/results/logs/Blocking_Delay.txt new file mode 100644 index 0000000..457a562 --- /dev/null +++ b/ServerBenchmark/results/logs/Blocking_Delay.txt @@ -0,0 +1,42 @@ +Server type: BlockingArray size: 5000 +Clients number: 25 +Delay: 200 +Queries number: 25 +Altering option: Delay +0 900 100 +TaskTime +10 +0 48.6016 +100 48.4048 +200 51.4096 +300 50.6192 +400 50.1168 +500 54.112 +600 49.6976 +700 51.904 +800 51.6992 +900 50.6736 +ClientTime +10 +0 752.4 +100 656.12 +200 708.32 +300 835.64 +400 936.04 +500 1168.2 +600 1059.4 +700 1266.64 +800 1307.84 +900 1526.64 +ServerTime +10 +0 382.68 +100 126.872 +200 221.1824 +300 129.624 +400 135.1968 +500 285.848 +600 137.4 +700 145.7808 +800 179.184 +900 151.3024 diff --git a/ServerBenchmark/results/logs/NonBlocking_ArraySize.txt b/ServerBenchmark/results/logs/NonBlocking_ArraySize.txt new file mode 100644 index 0000000..4253963 --- /dev/null +++ b/ServerBenchmark/results/logs/NonBlocking_ArraySize.txt @@ -0,0 +1,42 @@ +Server type: NonBlockingArray size: 5000 +Clients number: 25 +Delay: 200 +Queries number: 25 +Altering option: ArraySize +1000 10000 1000 +TaskTime +10 +1000 1.3152 +2000 4.6496 +3000 11.0544 +4000 22.2288 +5000 37.1488 +6000 58.3408 +7000 83.5168 +8000 116.3872 +9000 152.832 +10000 184.3952 +ClientTime +10 +1000 238.4 +2000 304.68 +3000 411.4 +4000 517.68 +5000 600.12 +6000 698.04 +7000 875.4 +8000 1294.68 +9000 1326.96 +10000 1733.64 +ServerTime +10 +1000 10.976 +2000 19.2896 +3000 55.5168 +4000 116.2928 +5000 169.6288 +6000 220.6256 +7000 342.656 +8000 741.624 +9000 776.712 +10000 1264.2256 diff --git a/ServerBenchmark/results/logs/NonBlocking_ClientsNumber.txt b/ServerBenchmark/results/logs/NonBlocking_ClientsNumber.txt new file mode 100644 index 0000000..d7e4a48 --- /dev/null +++ b/ServerBenchmark/results/logs/NonBlocking_ClientsNumber.txt @@ -0,0 +1,42 @@ +Server type: NonBlockingArray size: 10000 +Clients number: 25 +Delay: 200 +Queries number: 20 +Altering option: ClientsNumber +10 100 10 +TaskTime +10 +10 207.0964467005076 +20 203.203007518797 +30 207.125 +40 204.82559598494353 +50 212.47294589178355 +60 202.46166666666667 +70 206.64285714285714 +80 202.205625 +90 196.43444444444444 +100 190.4385 +ClientTime +10 +10 823.9 +20 1393.85 +30 2281.0666666666666 +40 3053.85 +50 4020.5 +60 4029.9 +70 4764.971428571429 +80 5477.65 +90 6299.711111111111 +100 6592.06 +ServerTime +10 +10 492.685 +20 989.8175 +30 1729.435 +40 2430.08125 +50 3349.428 +60 3546.2716666666665 +70 3983.267142857143 +80 4993.553125 +90 5843.877777777778 +100 5947.957 diff --git a/ServerBenchmark/results/logs/NonBlocking_Delay.txt b/ServerBenchmark/results/logs/NonBlocking_Delay.txt new file mode 100644 index 0000000..3f4e55c --- /dev/null +++ b/ServerBenchmark/results/logs/NonBlocking_Delay.txt @@ -0,0 +1,42 @@ +Server type: NonBlockingArray size: 5000 +Clients number: 25 +Delay: 200 +Queries number: 25 +Altering option: Delay +0 900 100 +TaskTime +10 +0 40.912 +100 40.9072 +200 41.6096 +300 41.1232 +400 40.3872 +500 40.0736 +600 39.9856 +700 40.3424 +800 41.3232 +900 42.064 +ClientTime +10 +0 590.6 +100 699.4 +200 824.48 +300 658.6 +400 750.88 +500 882.64 +600 958.32 +700 1186.56 +800 1291.96 +900 1305.36 +ServerTime +10 +0 247.7376 +100 292.9776 +200 333.5824 +300 151.5584 +400 138.5808 +500 82.8752 +600 95.1392 +700 176.808 +800 161.9392 +900 138.3488 diff --git a/ServerBenchmark/results/logs/Simple_ArraySize.txt b/ServerBenchmark/results/logs/Simple_ArraySize.txt new file mode 100644 index 0000000..1426d96 --- /dev/null +++ b/ServerBenchmark/results/logs/Simple_ArraySize.txt @@ -0,0 +1,42 @@ +Server type: SimpleArray size: 5000 +Clients number: 25 +Delay: 200 +Queries number: 25 +Altering option: ArraySize +1000 10000 1000 +TaskTime +10 +1000 17.804173354735152 +2000 39.3856 +3000 51.584 +4000 51.3072 +5000 78.1184 +6000 69.6352 +7000 117.54019292604502 +8000 135.24679487179486 +9000 208.9984 +10000 366.2371794871795 +ClientTime +10 +1000 327.92 +2000 428.8 +3000 512.24 +4000 626.64 +5000 788.88 +6000 1365.16 +7000 1024.24 +8000 1214.16 +9000 1484.48 +10000 1865.52 +ServerTime +10 +1000 36.02247191011236 +2000 52.4208 +3000 82.3701923076923 +4000 108.944 +5000 173.0448 +6000 267.8512 +7000 392.2215088282504 +8000 284.5872 +9000 543.5776 +10000 964.776886035313 diff --git a/ServerBenchmark/results/logs/Simple_ClientsNumber.txt b/ServerBenchmark/results/logs/Simple_ClientsNumber.txt new file mode 100644 index 0000000..7446203 --- /dev/null +++ b/ServerBenchmark/results/logs/Simple_ClientsNumber.txt @@ -0,0 +1,42 @@ +Server type: SimpleArray size: 10000 +Clients number: 25 +Delay: 200 +Queries number: 20 +Altering option: ClientsNumber +10 100 10 +TaskTime +10 +10 259.075 +20 367.8725 +30 360.36622073578593 +40 420.2120451693852 +50 515.871 +60 541.045 +70 491.1660701503221 +80 686.3289555972483 +90 623.1728738187882 +100 616.5450450450451 +ClientTime +10 +10 891.8 +20 2287.75 +30 2999.366666666667 +40 2572.45 +50 2969.4 +60 3627.2833333333333 +70 5813.914285714286 +80 4832.6375 +90 4982.211111111111 +100 5561.11 +ServerTime +10 +10 488.0 +20 1325.915 +30 1444.6761268781302 +40 1153.360552763819 +50 1293.895 +60 1751.1191666666666 +70 2411.175375805297 +80 2319.101875 +90 1639.6703724291274 +100 2274.55 diff --git a/ServerBenchmark/results/logs/Simple_Delay.txt b/ServerBenchmark/results/logs/Simple_Delay.txt new file mode 100644 index 0000000..d0b0b30 --- /dev/null +++ b/ServerBenchmark/results/logs/Simple_Delay.txt @@ -0,0 +1,42 @@ +Server type: SimpleArray size: 5000 +Clients number: 25 +Delay: 200 +Queries number: 25 +Altering option: Delay +0 900 100 +TaskTime +10 +0 77.7888 +100 63.8512 +200 60.37019230769231 +300 79.9824 +400 71.472 +500 83.32 +600 65.1392 +700 75.62339743589743 +800 101.7184 +900 60.8464 +ClientTime +10 +0 681.08 +100 686.04 +200 814.28 +300 828.92 +400 933.32 +500 1082.84 +600 1159.6 +700 1263.36 +800 1199.0 +900 1564.36 +ServerTime +10 +0 237.5632 +100 123.0304 +200 112.31410256410257 +300 116.8512 +400 116.1136 +500 188.0624 +600 107.5792 +700 119.08173076923077 +800 181.4096 +900 132.336 diff --git a/ServerBenchmark/results/plots/ClientTime(ArraySize).png b/ServerBenchmark/results/plots/ClientTime(ArraySize).png new file mode 100644 index 0000000..b6123ec Binary files /dev/null and b/ServerBenchmark/results/plots/ClientTime(ArraySize).png differ diff --git a/ServerBenchmark/results/plots/ClientTime(ClientsNumber).png b/ServerBenchmark/results/plots/ClientTime(ClientsNumber).png new file mode 100644 index 0000000..f535747 Binary files /dev/null and b/ServerBenchmark/results/plots/ClientTime(ClientsNumber).png differ diff --git a/ServerBenchmark/results/plots/ClientTime(Delay).png b/ServerBenchmark/results/plots/ClientTime(Delay).png new file mode 100644 index 0000000..6a28e01 Binary files /dev/null and b/ServerBenchmark/results/plots/ClientTime(Delay).png differ diff --git a/ServerBenchmark/results/plots/ServerTime(ArraySize).png b/ServerBenchmark/results/plots/ServerTime(ArraySize).png new file mode 100644 index 0000000..ce1a6b7 Binary files /dev/null and b/ServerBenchmark/results/plots/ServerTime(ArraySize).png differ diff --git a/ServerBenchmark/results/plots/ServerTime(ClientsNumber).png b/ServerBenchmark/results/plots/ServerTime(ClientsNumber).png new file mode 100644 index 0000000..472902a Binary files /dev/null and b/ServerBenchmark/results/plots/ServerTime(ClientsNumber).png differ diff --git a/ServerBenchmark/results/plots/ServerTime(Delay).png b/ServerBenchmark/results/plots/ServerTime(Delay).png new file mode 100644 index 0000000..9b26bc7 Binary files /dev/null and b/ServerBenchmark/results/plots/ServerTime(Delay).png differ diff --git a/ServerBenchmark/results/plots/TaskTime(ArraySize).png b/ServerBenchmark/results/plots/TaskTime(ArraySize).png new file mode 100644 index 0000000..50ed61b Binary files /dev/null and b/ServerBenchmark/results/plots/TaskTime(ArraySize).png differ diff --git a/ServerBenchmark/results/plots/TaskTime(ClientsNumber).png b/ServerBenchmark/results/plots/TaskTime(ClientsNumber).png new file mode 100644 index 0000000..d8d1ee8 Binary files /dev/null and b/ServerBenchmark/results/plots/TaskTime(ClientsNumber).png differ diff --git a/ServerBenchmark/results/plots/TaskTime(Delay).png b/ServerBenchmark/results/plots/TaskTime(Delay).png new file mode 100644 index 0000000..153f5ed Binary files /dev/null and b/ServerBenchmark/results/plots/TaskTime(Delay).png differ diff --git a/ServerBenchmark/settings b/ServerBenchmark/settings new file mode 100644 index 0000000..d791ba4 --- /dev/null +++ b/ServerBenchmark/settings @@ -0,0 +1,4 @@ +1000 +1 +10 +1 \ No newline at end of file diff --git a/ServerBenchmark/settings.gradle b/ServerBenchmark/settings.gradle new file mode 100644 index 0000000..d4fae04 --- /dev/null +++ b/ServerBenchmark/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'ServerBenchmark' + diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ArrayMessage.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ArrayMessage.java new file mode 100644 index 0000000..39a2355 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ArrayMessage.java @@ -0,0 +1,607 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: src/main/resources/array.proto + +package com.mit.spbau.kirakosian; + +public final class ArrayMessage { + private ArrayMessage() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface ArrayOrBuilder extends + // @@protoc_insertion_point(interface_extends:tutorial.Array) + com.google.protobuf.MessageOrBuilder { + + /** + * repeated int32 data = 1; + */ + java.util.List getDataList(); + /** + * repeated int32 data = 1; + */ + int getDataCount(); + /** + * repeated int32 data = 1; + */ + int getData(int index); + } + /** + * Protobuf type {@code tutorial.Array} + */ + public static final class Array extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:tutorial.Array) + ArrayOrBuilder { + private static final long serialVersionUID = 0L; + // Use Array.newBuilder() to construct. + private Array(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private Array() { + data_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Array( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + data_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + data_.add(input.readInt32()); + break; + } + case 10: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001) && input.getBytesUntilLimit() > 0) { + data_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + while (input.getBytesUntilLimit() > 0) { + data_.add(input.readInt32()); + } + input.popLimit(limit); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + data_ = java.util.Collections.unmodifiableList(data_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_descriptor; + } + + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.mit.spbau.kirakosian.ArrayMessage.Array.class, com.mit.spbau.kirakosian.ArrayMessage.Array.Builder.class); + } + + public static final int DATA_FIELD_NUMBER = 1; + private java.util.List data_; + /** + * repeated int32 data = 1; + */ + public java.util.List + getDataList() { + return data_; + } + /** + * repeated int32 data = 1; + */ + public int getDataCount() { + return data_.size(); + } + /** + * repeated int32 data = 1; + */ + public int getData(int index) { + return data_.get(index); + } + + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < data_.size(); i++) { + output.writeInt32(1, data_.get(i)); + } + unknownFields.writeTo(output); + } + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < data_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(data_.get(i)); + } + size += dataSize; + size += 1 * getDataList().size(); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.mit.spbau.kirakosian.ArrayMessage.Array)) { + return super.equals(obj); + } + com.mit.spbau.kirakosian.ArrayMessage.Array other = (com.mit.spbau.kirakosian.ArrayMessage.Array) obj; + + boolean result = true; + result = result && getDataList() + .equals(other.getDataList()); + result = result && unknownFields.equals(other.unknownFields); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getDataCount() > 0) { + hash = (37 * hash) + DATA_FIELD_NUMBER; + hash = (53 * hash) + getDataList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static com.mit.spbau.kirakosian.ArrayMessage.Array parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(com.mit.spbau.kirakosian.ArrayMessage.Array prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code tutorial.Array} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:tutorial.Array) + com.mit.spbau.kirakosian.ArrayMessage.ArrayOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_descriptor; + } + + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.mit.spbau.kirakosian.ArrayMessage.Array.class, com.mit.spbau.kirakosian.ArrayMessage.Array.Builder.class); + } + + // Construct using com.mit.spbau.kirakosian.ArrayMessage.Array.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + public Builder clear() { + super.clear(); + data_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.mit.spbau.kirakosian.ArrayMessage.internal_static_tutorial_Array_descriptor; + } + + public com.mit.spbau.kirakosian.ArrayMessage.Array getDefaultInstanceForType() { + return com.mit.spbau.kirakosian.ArrayMessage.Array.getDefaultInstance(); + } + + public com.mit.spbau.kirakosian.ArrayMessage.Array build() { + com.mit.spbau.kirakosian.ArrayMessage.Array result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public com.mit.spbau.kirakosian.ArrayMessage.Array buildPartial() { + com.mit.spbau.kirakosian.ArrayMessage.Array result = new com.mit.spbau.kirakosian.ArrayMessage.Array(this); + int from_bitField0_ = bitField0_; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + data_ = java.util.Collections.unmodifiableList(data_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.data_ = data_; + onBuilt(); + return result; + } + + public Builder clone() { + return (Builder) super.clone(); + } + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.setField(field, value); + } + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.addRepeatedField(field, value); + } + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.mit.spbau.kirakosian.ArrayMessage.Array) { + return mergeFrom((com.mit.spbau.kirakosian.ArrayMessage.Array)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.mit.spbau.kirakosian.ArrayMessage.Array other) { + if (other == com.mit.spbau.kirakosian.ArrayMessage.Array.getDefaultInstance()) return this; + if (!other.data_.isEmpty()) { + if (data_.isEmpty()) { + data_ = other.data_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureDataIsMutable(); + data_.addAll(other.data_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.mit.spbau.kirakosian.ArrayMessage.Array parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (com.mit.spbau.kirakosian.ArrayMessage.Array) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List data_ = java.util.Collections.emptyList(); + private void ensureDataIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + data_ = new java.util.ArrayList(data_); + bitField0_ |= 0x00000001; + } + } + /** + * repeated int32 data = 1; + */ + public java.util.List + getDataList() { + return java.util.Collections.unmodifiableList(data_); + } + /** + * repeated int32 data = 1; + */ + public int getDataCount() { + return data_.size(); + } + /** + * repeated int32 data = 1; + */ + public int getData(int index) { + return data_.get(index); + } + /** + * repeated int32 data = 1; + */ + public Builder setData( + int index, int value) { + ensureDataIsMutable(); + data_.set(index, value); + onChanged(); + return this; + } + /** + * repeated int32 data = 1; + */ + public Builder addData(int value) { + ensureDataIsMutable(); + data_.add(value); + onChanged(); + return this; + } + /** + * repeated int32 data = 1; + */ + public Builder addAllData( + java.lang.Iterable values) { + ensureDataIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, data_); + onChanged(); + return this; + } + /** + * repeated int32 data = 1; + */ + public Builder clearData() { + data_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:tutorial.Array) + } + + // @@protoc_insertion_point(class_scope:tutorial.Array) + private static final com.mit.spbau.kirakosian.ArrayMessage.Array DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new com.mit.spbau.kirakosian.ArrayMessage.Array(); + } + + public static com.mit.spbau.kirakosian.ArrayMessage.Array getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + public Array parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Array(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public com.mit.spbau.kirakosian.ArrayMessage.Array getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_tutorial_Array_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_tutorial_Array_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\036src/main/resources/array.proto\022\010tutori" + + "al\"\025\n\005Array\022\014\n\004data\030\001 \003(\005B(\n\030com.mit.spb" + + "au.kirakosianB\014ArrayMessage" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + internal_static_tutorial_Array_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_tutorial_Array_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_tutorial_Array_descriptor, + new java.lang.String[] { "Data", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Main.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Main.java new file mode 100644 index 0000000..12db9e4 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Main.java @@ -0,0 +1,18 @@ +package com.mit.spbau.kirakosian; + +import com.mit.spbau.kirakosian.ui.SceneManager; +import com.mit.spbau.kirakosian.ui.Window; + +import javax.swing.*; + +public class Main { + + public static void main(final String[] args) { + SwingUtilities.invokeLater(() -> { + final Window window = new Window(); + SceneManager.init(window); + window.setVisible(true); + }); + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Protocol.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Protocol.java new file mode 100644 index 0000000..bec995b --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Protocol.java @@ -0,0 +1,8 @@ +package com.mit.spbau.kirakosian; + +public class Protocol { + + public static final int NEW_QUERY = 0; + public static final int STOP = 1; + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Utils.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Utils.java new file mode 100644 index 0000000..ced851a --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/Utils.java @@ -0,0 +1,72 @@ +package com.mit.spbau.kirakosian; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.apache.commons.io.input.BoundedInputStream; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@SuppressWarnings("WeakerAccess") +public class Utils { + + public static void writeArray(final DataOutputStream os, final int[] array) throws IOException { + final byte[] bytes = makeArrayMessage(array); + os.writeInt(bytes.length); + os.write(bytes); + } + + public static int[] readArray(final DataInputStream is) throws IOException { + final int size = is.readInt(); + final BoundedInputStream bis = new BoundedInputStream(is, size); + return listToArray(ArrayMessage.Array.parseFrom(bis).getDataList()); + } + + public static int[] getArray(final byte[] array) throws InvalidProtocolBufferException { + return listToArray(ArrayMessage.Array.parseFrom(array).getDataList()); + } + + public static void sort(final int[] array) { + for (int i = 0; i < array.length; i++) { + for (int j = i; j < array.length; j++) { + if (array[i] > array[j]) { + final int tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + } + } + } + } + + public static int[] generate(final int size) { + final Random random = new Random(); + final int array[] = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = random.nextInt(); + } + return array; + } + + public static byte[] makeArrayMessage(final int[] array) { + return ArrayMessage.Array.newBuilder().addAllData(arrayToList(array)).build().toByteArray(); + } + + public static int[] listToArray(final List list) { + final int[] array = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + array[i] = list.get(i); + } + return array; + } + + public static List arrayToList(final int[] array) { + final List list = new ArrayList<>(); + for (final int i : array) { + list.add(i); + } + return list; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/ArrayClient.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/ArrayClient.java new file mode 100644 index 0000000..1474910 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/ArrayClient.java @@ -0,0 +1,59 @@ +package com.mit.spbau.kirakosian.client; + +import com.mit.spbau.kirakosian.Utils; +import com.mit.spbau.kirakosian.servers.impl.AbstractServer; + +import java.io.*; +import java.net.InetAddress; +import java.net.Socket; + +import static com.mit.spbau.kirakosian.Protocol.*; + +class ArrayClient { + + private long time; + private final Socket socket; + + ArrayClient(final String ip) throws IOException { + socket = new Socket(InetAddress.getByName(ip), AbstractServer.PORT); + } + + @SuppressWarnings("WeakerAccess") + public long getTime() { + return time; + } + + @SuppressWarnings("WeakerAccess") + public void work(final int queries, final int size, final int delay) throws IOException { + try (final DataOutputStream os = new DataOutputStream(socket.getOutputStream()); + final DataInputStream is = new DataInputStream(socket.getInputStream())) { + final long begin = System.currentTimeMillis(); + for (int i = 0; i < queries; i++) { + final int[] array = Utils.generate(size); + + os.writeInt(NEW_QUERY); + Utils.writeArray(os, array); + os.flush(); + + @SuppressWarnings("unused") final int[] result = Utils.readArray(is); + + try { + Thread.sleep(delay); + } catch (final InterruptedException e) { + // unlucky + } + } + os.writeInt(STOP); + os.flush(); + final long end = System.currentTimeMillis(); + time = end - begin; + } catch (final IOException e) { + try { + socket.close(); + } catch (final IOException e1) { + // do nothing + } + throw e; + } + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/AutoOptions.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/AutoOptions.java new file mode 100644 index 0000000..fd43a7d --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/AutoOptions.java @@ -0,0 +1,53 @@ +package com.mit.spbau.kirakosian.client; + +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; +import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize; +import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.Delay; + +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings("WeakerAccess") +public class AutoOptions { + + private final Map, Integer> map = new HashMap<>(); + private final Class altering; + private final int delta; + private final int max; + + public AutoOptions(final TestOptions options) { + delta = options.delta(); + max = options.upperBound(); + map.putAll(options.options()); + altering = options.alteringOption(); + map.put(altering, options.lowerBound()); + } + + public void next() { + final int value = map.get(altering); + map.put(altering, value + delta); + } + + public boolean notFinished() { + return map.get(altering) <= max; + } + + public int clients() { + return map.get(ClientsNumber.class); + } + + public int queries() { + return map.get(QueriesNumber.class); + } + + public int arraySize() { + return map.get(ArraySize.class); + } + + public int delay() { + return map.get(Delay.class); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/TestingClient.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/TestingClient.java new file mode 100644 index 0000000..ef86d3b --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/client/TestingClient.java @@ -0,0 +1,83 @@ +package com.mit.spbau.kirakosian.client; + +import com.mit.spbau.kirakosian.connector.ApplicationConnector; +import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException; +import com.mit.spbau.kirakosian.options.TestOptions; + +import java.io.*; +import java.net.InetAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +import static com.mit.spbau.kirakosian.connector.Protocol.*; + +public class TestingClient { + + private final AutoOptions options; + final private ObjectOutputStream os; + final private ObjectInputStream is; + + private final String ip; + + private TestingClient(final String ip) throws IOException, ClassNotFoundException { + final Socket socket = new Socket(InetAddress.getByName(ip), ApplicationConnector.APPLICATION_PORT); + this.ip = ip; + os = new ObjectOutputStream(socket.getOutputStream()); + is = new ObjectInputStream(socket.getInputStream()); + if (is.readInt() != OPTIONS) { + throw new UnexpectedProtocolMessageException(); + } + options = new AutoOptions((TestOptions) is.readObject()); + } + + private void startTest() throws IOException { + while (options.notFinished()) { + System.out.println(options.arraySize() + " " + options.clients() + " " + options.queries() + " " + options.delay()); + if (is.readInt() != START_TEST_CASE) { + throw new UnexpectedProtocolMessageException(); + } + + final Map threads = new HashMap<>(); + for (int i = 0; i < options.clients(); i++) { + final ArrayClient client = new ArrayClient(ip); + threads.put(new Thread(() -> { + try { + client.work(options.queries(), options.arraySize(), options.delay()); + } catch (final IOException e) { + // do nothing + } + }), client); + } + for (final Thread thread : threads.keySet()) { + thread.start(); + } + for (final Map.Entry entry : threads.entrySet()) { + try { + entry.getKey().join(); + os.writeInt(CLIENT_TIME); + os.writeLong(entry.getValue().getTime() / options.queries()); + os.flush(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + os.writeInt(END_TEST_CASE); + os.flush(); + options.next(); + } + } + + public static void main(final String[] args) throws IOException, ClassNotFoundException { + final String ip; + if (args.length == 0) { + ip = "127.0.0.1"; + } else { + ip = args[0]; + } + final TestingClient client = new TestingClient(ip); + client.startTest(); + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/ApplicationConnector.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/ApplicationConnector.java new file mode 100644 index 0000000..253ecfa --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/ApplicationConnector.java @@ -0,0 +1,103 @@ +package com.mit.spbau.kirakosian.connector; + +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.testing.StatsListener; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.ServerSocket; +import java.net.Socket; + +import static com.mit.spbau.kirakosian.connector.Protocol.*; + +public class ApplicationConnector { + + public final static int APPLICATION_PORT = 8093; + + private final ServerSocket server; + private Socket socket; + private ObjectInputStream is; + private ObjectOutputStream os; + + private final TestOptions options; + private final StatsListener stats; + + public ApplicationConnector(final TestOptions options, final StatsListener stats) throws IOException { + this.options = options; + this.stats = stats; + server = new ServerSocket(APPLICATION_PORT); + } + + public void waitForNewClient() throws IOException { + try { + socket = server.accept(); + } catch (IOException e) { + try { + server.close(); + } catch (IOException e1) { + // ignore error during close + } + throw e; + } + processSocket(socket); + } + + private void processSocket(final Socket socket) throws IOException { + try { + is = new ObjectInputStream(socket.getInputStream()); + os = new ObjectOutputStream(socket.getOutputStream()); + + os.writeInt(OPTIONS); + os.writeObject(options); + os.flush(); + } catch (final IOException e) { + try { + closeAll(); + } catch (final IOException e1) { + // ignore close exception + } + throw e; + } + } + + private void closeAll() throws IOException { + is.close(); // try to close resources + os.close(); + socket.close(); + server.close(); + } + + public void startTestCase() throws IOException { + try { + os.writeInt(START_TEST_CASE); + os.flush(); + + int signal; + signal = is.readInt(); + while (true) { + if (signal == END_TEST_CASE) { + stats.done(); + break; + } else if (signal == CLIENT_TIME) { + final long time = is.readLong(); + stats.timeForClientOnClient(time); + } else { + throw new UnexpectedProtocolMessageException(); + } + signal = is.readInt(); + } + } catch (final IOException e) { + try { + closeAll(); + } catch (final IOException e1) { + // ignore close exception + } + throw e; + } + } + + public void close() throws IOException { + closeAll(); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/Protocol.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/Protocol.java new file mode 100644 index 0000000..d21553f --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/Protocol.java @@ -0,0 +1,10 @@ +package com.mit.spbau.kirakosian.connector; + +public class Protocol { + + public final static int OPTIONS = 0; + public final static int START_TEST_CASE = 1; + public static final int END_TEST_CASE = 2; + public static final int CLIENT_TIME = 3; + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/UnexpectedProtocolMessageException.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/UnexpectedProtocolMessageException.java new file mode 100644 index 0000000..98b7f64 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/connector/UnexpectedProtocolMessageException.java @@ -0,0 +1,5 @@ +package com.mit.spbau.kirakosian.connector; + +public class UnexpectedProtocolMessageException extends RuntimeException { + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/Controller.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/Controller.java new file mode 100644 index 0000000..4beba81 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/Controller.java @@ -0,0 +1,26 @@ +package com.mit.spbau.kirakosian.controller; + +import com.mit.spbau.kirakosian.testing.TestResults; +import com.mit.spbau.kirakosian.ui.SceneManager; + +import javax.swing.*; + +public class Controller { + + public static void calculationsCompleted(final TestResults results) { + ResultsWriter.saveResults(results); + SceneManager.RESULTS_SCENE.acceptResults(results); + SwingUtilities.invokeLater(() -> SceneManager.setScene(SceneManager.RESULTS_SCENE)); + } + + public static void calculationsCompletedDebug(final TestResults results) { + ResultsWriter.saveResults(results); +// SceneManager.RESULTS_SCENE.acceptResults(results); +// SwingUtilities.invokeLater(() -> SceneManager.setScene(SceneManager.RESULTS_SCENE)); + } + + public static void cancel() { + SwingUtilities.invokeLater(() -> SceneManager.setScene(SceneManager.SETTINGS_SCENE)); + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/ResultsWriter.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/ResultsWriter.java new file mode 100644 index 0000000..779331d --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/controller/ResultsWriter.java @@ -0,0 +1,73 @@ +package com.mit.spbau.kirakosian.controller; + +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.options.Utils; +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.metrics.MetricResult; +import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime; +import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime; +import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime; +import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize; +import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.Delay; +import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber; +import com.mit.spbau.kirakosian.testing.TestResults; + +import java.io.*; +import java.util.Map; + +class ResultsWriter { + + static void saveResults(final TestResults results) { + final String name = getName(results); + final File file = new File("results/logs/" + name); + if (!file.exists()) { + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + } catch (final IOException e) { + e.printStackTrace(); + } + } + final TestOptions options = results.options(); + //noinspection TryWithIdenticalCatches + try (final Writer writer = new PrintWriter(file)) { + writer.write("Server type: " + options.serverType().name()); + writer.write("Array size: " + options.getOption(ArraySize.class) + "\n"); + writer.write("Clients number: " + options.getOption(ClientsNumber.class) + "\n"); + writer.write("Delay: " + options.getOption(Delay.class) + "\n"); + writer.write("Queries number: " + options.getOption(QueriesNumber.class) + "\n"); + + writeAlteringMetric(writer, options); + + writeResultsForMetric(writer, results, TaskTime.class); + writeResultsForMetric(writer, results, ClientTime.class); + writeResultsForMetric(writer, results, ServerTime.class); + } catch (final FileNotFoundException e) { + // should never happen + } catch (final IOException e) { + // just ignore + } + } + + private static void writeAlteringMetric(final Writer writer, final TestOptions options) throws IOException { + writer.write("Altering option: " + options.alteringOption().getSimpleName() + "\n"); + writer.write(options.lowerBound() + " " + options.upperBound() + " " + options.delta() + "\n"); + } + + private static void writeResultsForMetric(final Writer writer, + final TestResults results, + final Class meta) throws IOException { + writer.write(meta.getSimpleName() + "\n"); + final MetricResult metricResult = results.getResultsForMetric(meta); + writer.write(metricResult.results().size() + "\n"); + for (final Map.Entry entry : metricResult.results().entrySet()) { + writer.write(entry.getKey() + " " + entry.getValue() + "\n"); + } + } + + private static String getName(final TestResults results) { + return results.options().serverType() + "_" + results.options().alteringOption().getSimpleName() + ".txt"; + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/TestOptions.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/TestOptions.java new file mode 100644 index 0000000..cd9dbe5 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/TestOptions.java @@ -0,0 +1,147 @@ +package com.mit.spbau.kirakosian.options; + +import com.mit.spbau.kirakosian.testing.ServerTestInitializer; +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; +import com.mit.spbau.kirakosian.servers.Servers; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Stores all chosen in GUI options. This class is used to pass these options fro, GUI to logic. + * Every required by logic option should must be set. + * + * Also any valid option class must be taken correctly by logic. See {@link TestOptions#validate()} + * method for more information. + */ +public class TestOptions implements Serializable { + + private final Map, Integer> options = new HashMap<>(); + private Servers.ServerType serverType; + + private final Set> metrics = new HashSet<>(); + + private Class alteringOptionMeta; + private int lowerBound; + private int upperBound; + private int delta; + + public Map, Integer> options() { + return options; + } + + public Servers.ServerType serverType() { + return serverType; + } + + public void setServerType(final Servers.ServerType serverType) { + this.serverType = serverType; + } + + public Class alteringOption() { + return alteringOptionMeta; + } + + public void setAlteringOptionMeta(final Class alteringOptionMeta) { + this.alteringOptionMeta = alteringOptionMeta; + } + + public int lowerBound() { + return lowerBound; + } + + public void setLowerBound(final int lowerBound) { + this.lowerBound = lowerBound; + } + + public int upperBound() { + return upperBound; + } + + public void setUpperBound(final int upperBound) { + this.upperBound = upperBound; + } + + public int delta() { + return delta; + } + + public void setDelta(final int delta) { + this.delta = delta; + } + + public void setOption(final Class option, final Integer value) { + options.put(option, value); + } + + public Integer getOption(final Class meta) { + return options.get(meta); + } + + public void addMetric(final Class metric) { + metrics.add(metric); + } + + public boolean requiresMetric(final Class metric) { + return metrics.contains(metric); + } + + public Set> metrics() { + return metrics; + } + + /** + * The method checks if provided options are legal (for example, altering + * property was provided). + * + * @return string with error message or null if data was legal. + */ + public String validate() { + if (serverType == null) { + return "Server type was not provided."; + } + if (alteringOptionMeta == null) { + return "Altering property was not provided."; + } + final ParameterOptionMeta alteringOption = Utils.getOptionInstance(alteringOptionMeta); + if (!alteringOption.mayAlter()) { + return "Option " + alteringOptionMeta.getSimpleName() + " cannot alter."; + } + for (final Class option : ServerTestInitializer.getRequiredOptions()) { + if (options.get(option) == null && alteringOptionMeta != option) { + return "Option " + option.getSimpleName() + " was not provided"; + } + } + for (final Class metric : metrics) { + if (!ServerTestInitializer.getSupportedMetrics().contains(metric)) { + return "Metric " + metric.getSimpleName() + " is unsupported"; + } + } + if (lowerBound > upperBound) { + return "Min property value is greater then max value"; + } + if (lowerBound < alteringOption.minValue()) { + return "Illegal max value"; + } + if (upperBound > alteringOption.maxValue()) { + return "Illegal in value"; + } + if (delta <= 0) { + return "Delta must be positive"; + } + return null; + } + + public void print() { + for (final Map.Entry, Integer> meta : options.entrySet()) { + System.out.println(meta.getKey().getSimpleName() + " has value: " + meta.getValue()); + } + System.out.println(alteringOption().getSimpleName() + " is altering from " + + lowerBound + " to " + upperBound + " with delta " + delta); + System.out.println("Server type is: " + serverType.name()); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/UIOptions.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/UIOptions.java new file mode 100644 index 0000000..d28eddd --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/UIOptions.java @@ -0,0 +1,67 @@ +package com.mit.spbau.kirakosian.options; + + +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime; +import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime; +import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime; +import com.mit.spbau.kirakosian.options.parameters.*; +import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize; +import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.Delay; +import com.mit.spbau.kirakosian.servers.Servers; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * This class represents options supported by GUI. Not all described options/metrics might be + * supported by logic. + */ +public class UIOptions { + + private static final List options = new ArrayList<>(); + private static final Set serverTypes = new HashSet<>(); + + private static final List> metrics = new ArrayList<>(); + + static { + serverTypes.add(Servers.ServerType.Simple); + serverTypes.add(Servers.ServerType.Blocking); + serverTypes.add(Servers.ServerType.NonBlocking); + + metrics.add(ServerTime.class); + metrics.add(ClientTime.class); + metrics.add(TaskTime.class); + try { + registerOption(ArraySize.class); + registerOption(ClientsNumber.class); + registerOption(Delay.class); + registerOption(QueriesNumber.class); + } catch (final NoSuchMethodException | InvocationTargetException | + IllegalAccessException | InstantiationException e) { + throw new RuntimeException("Error occurred during initialization", e); + } + } + + private static void registerOption(final Class clazz) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { + options.add(clazz.getDeclaredConstructor().newInstance()); + } + + public static List options() { + return options; + } + + public static Set serverOptions() { + return serverTypes; + } + + public static List> metrics() { + return metrics; + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/Utils.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/Utils.java new file mode 100644 index 0000000..7fe620e --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/Utils.java @@ -0,0 +1,77 @@ +package com.mit.spbau.kirakosian.options; + +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime; +import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime; +import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime; +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; +import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize; +import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.Delay; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +public class Utils { + + private static final Map, MetricMeta> metricMap = new HashMap<>(); + private static final Map, ParameterOptionMeta> optionsMap = new HashMap<>(); + + static { + try { + registerOption(ArraySize.class); + registerOption(ClientsNumber.class); + registerOption(Delay.class); + registerOption(QueriesNumber.class); + + registerMetric(ClientTime.class); + registerMetric(ServerTime.class); + registerMetric(TaskTime.class); + } catch (final NoSuchMethodException | InvocationTargetException | + IllegalAccessException | InstantiationException e) { + throw new RuntimeException("Error occurred during initialization", e); + } + } + + private static void registerOption(final Class meta) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException, InstantiationException { + optionsMap.put(meta, meta.getDeclaredConstructor().newInstance()); + } + + private static void registerMetric(final Class meta) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { + metricMap.put(meta, meta.getDeclaredConstructor().newInstance()); + } + + public static MetricMeta getMetricInstance(final Class meta) { + if (metricMap.containsKey(meta)) { + return metricMap.get(meta); + } else { + final MetricMeta instance; + try { + instance = meta.getConstructor().newInstance(); + } catch (final InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(); // should never happen + } + metricMap.put(meta, instance); + return instance; + } + } + + public static ParameterOptionMeta getOptionInstance(final Class meta) { + if (optionsMap.containsKey(meta)) { + return optionsMap.get(meta); + } else { + final ParameterOptionMeta instance; + try { + instance = meta.getConstructor().newInstance(); + } catch (final InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(); // should never happen + } + optionsMap.put(meta, instance); + return instance; + } + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricMeta.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricMeta.java new file mode 100644 index 0000000..d86d50b --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricMeta.java @@ -0,0 +1,10 @@ +package com.mit.spbau.kirakosian.options.metrics; + +public interface MetricMeta { + + String name(); + + String description(); + + String shortName(); +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricResult.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricResult.java new file mode 100644 index 0000000..c6bc557 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/MetricResult.java @@ -0,0 +1,27 @@ +package com.mit.spbau.kirakosian.options.metrics; + +import java.util.Map; +import java.util.TreeMap; + +public class MetricResult { + + private final Class meta; + + private final Map results = new TreeMap<>(); + + public MetricResult(final Class meta) { + this.meta = meta; + } + + public Class meta() { + return meta; + } + + public void addResult(final Integer parameter, final Number value) { + results.put(parameter, value); + } + + public Map results() { + return results; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ClientTime.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ClientTime.java new file mode 100644 index 0000000..329af58 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ClientTime.java @@ -0,0 +1,21 @@ +package com.mit.spbau.kirakosian.options.metrics.impl; + +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; + +public class ClientTime implements MetricMeta { + + @Override + public String name() { + return "Client time"; + } + + @Override + public String description() { + return "Time elapsed between sending task and getting response"; + } + + @Override + public String shortName() { + return "C"; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ServerTime.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ServerTime.java new file mode 100644 index 0000000..0d65fb0 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/ServerTime.java @@ -0,0 +1,21 @@ +package com.mit.spbau.kirakosian.options.metrics.impl; + +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; + +public class ServerTime implements MetricMeta { + + @Override + public String name() { + return "Server time"; + } + + @Override + public String description() { + return "Time elapsed between getting task and sending answer"; + } + + @Override + public String shortName() { + return "S"; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/TaskTime.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/TaskTime.java new file mode 100644 index 0000000..505e98b --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/metrics/impl/TaskTime.java @@ -0,0 +1,21 @@ +package com.mit.spbau.kirakosian.options.metrics.impl; + +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; + +public class TaskTime implements MetricMeta { + + @Override + public String name() { + return "Task time"; + } + + @Override + public String description() { + return "Time for completing one task"; + } + + @Override + public String shortName() { + return "T"; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/ParameterOptionMeta.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/ParameterOptionMeta.java new file mode 100644 index 0000000..2898370 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/ParameterOptionMeta.java @@ -0,0 +1,16 @@ +package com.mit.spbau.kirakosian.options.parameters; + +public interface ParameterOptionMeta { + + String name(); + + int minValue(); + + int maxValue(); + + String description(); + + boolean mayAlter(); + + String shortName(); +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ArraySize.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ArraySize.java new file mode 100644 index 0000000..5844f2e --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ArraySize.java @@ -0,0 +1,36 @@ +package com.mit.spbau.kirakosian.options.parameters.impl; + +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; + +public class ArraySize implements ParameterOptionMeta { + + @Override + public String name() { + return "Array size"; + } + + @Override + public String description() { + return "Size of array for each query"; + } + + @Override + public int minValue() { + return 0; + } + + @Override + public int maxValue() { + return 10000; + } + + @Override + public boolean mayAlter() { + return true; + } + + @Override + public String shortName() { + return "A"; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ClientsNumber.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ClientsNumber.java new file mode 100644 index 0000000..a2ef238 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/ClientsNumber.java @@ -0,0 +1,36 @@ +package com.mit.spbau.kirakosian.options.parameters.impl; + +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; + +public class ClientsNumber implements ParameterOptionMeta { + + @Override + public String name() { + return "Number of clients"; + } + + @Override + public String description() { + return "Number of clients working at the same time"; + } + + @Override + public int minValue() { + return 1; + } + + @Override + public int maxValue() { + return 101; + } + + @Override + public boolean mayAlter() { + return true; + } + + @Override + public String shortName() { + return "C "; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/Delay.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/Delay.java new file mode 100644 index 0000000..1372ccf --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/Delay.java @@ -0,0 +1,36 @@ +package com.mit.spbau.kirakosian.options.parameters.impl; + +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; + +public class Delay implements ParameterOptionMeta { + + @Override + public String name() { + return "Time space between queries"; + } + + @Override + public String description() { + return "Time before responce from server and new query"; + } + + @Override + public int minValue() { + return 0; + } + + @Override + public int maxValue() { + return 10_000; + } + + @Override + public boolean mayAlter() { + return true; + } + + @Override + public String shortName() { + return "D"; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/QueriesNumber.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/QueriesNumber.java new file mode 100644 index 0000000..1f71a5e --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/options/parameters/impl/QueriesNumber.java @@ -0,0 +1,36 @@ +package com.mit.spbau.kirakosian.options.parameters.impl; + +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; + +public class QueriesNumber implements ParameterOptionMeta { + + @Override + public String name() { + return "Number of queries"; + } + + @Override + public String description() { + return "Number of queries from each client"; + } + + @Override + public int minValue() { + return 1; + } + + @Override + public int maxValue() { + return 53; + } + + @Override + public boolean mayAlter() { + return false; + } + + @Override + public String shortName() { + return "Q"; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Server.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Server.java new file mode 100644 index 0000000..ec0d845 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Server.java @@ -0,0 +1,21 @@ +package com.mit.spbau.kirakosian.servers; + +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; + +/** + * Server interface. Might have different architectures. + */ +public interface Server { + + void start(); + + /** + * Turns off the server. It is required, that any implementation releases + + * every port, used by server. + */ + void shutDown(); + + void setServerActionListener(ServerStatsListener actionListener); + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/ServerStatsListener.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/ServerStatsListener.java new file mode 100644 index 0000000..ba274ff --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/ServerStatsListener.java @@ -0,0 +1,12 @@ +package com.mit.spbau.kirakosian.servers; + +public interface ServerStatsListener { + + default void done() {} + + default void timeForTask(final long time) {} + + default void timeForClientOnServer(final long time) {} + + void fail(final Exception e); +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Servers.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Servers.java new file mode 100644 index 0000000..6d41968 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/Servers.java @@ -0,0 +1,28 @@ +package com.mit.spbau.kirakosian.servers; + +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; +import com.mit.spbau.kirakosian.servers.impl.blockingServer.BlockingServer; +import com.mit.spbau.kirakosian.servers.impl.nonBlockingServer.NonBlockingServer; +import com.mit.spbau.kirakosian.servers.impl.simpleServer.SimpleServer; + +public class Servers { + + public static Server createServer(final ServerType type) throws AbortException { + switch (type) { + case Simple: + return new SimpleServer(); + case Blocking: + return new BlockingServer(); + case NonBlocking: + return new NonBlockingServer(); + } + throw new UnknownServerTypeException(); + } + + private static class UnknownServerTypeException extends RuntimeException { + } + + public enum ServerType { + Simple, Blocking, NonBlocking + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/exceptions/AbortException.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/exceptions/AbortException.java new file mode 100644 index 0000000..a1bef6b --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/exceptions/AbortException.java @@ -0,0 +1,5 @@ +package com.mit.spbau.kirakosian.servers.exceptions; + +public class AbortException extends Throwable { + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractBlockingServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractBlockingServer.java new file mode 100644 index 0000000..3e8116d --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractBlockingServer.java @@ -0,0 +1,71 @@ +package com.mit.spbau.kirakosian.servers.impl; + +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +public abstract class AbstractBlockingServer extends AbstractServer { + + private final @NotNull ServerSocket server; + private volatile boolean working; + private Thread mainThread; + + public AbstractBlockingServer() throws AbortException { + try { + server = new ServerSocket(PORT); + } catch (final IOException e) { + throw new AbortException(); + } + } + + protected abstract void processConnection(final Socket socket); + + @Override + public void start() { + working = true; + mainThread = new Thread(this::work); + mainThread.start(); + } + + private void work() { + while (working) { + final Socket socket; + try { + socket = server.accept(); + new Thread(() -> processConnection(socket)).start(); + } catch (final IOException e) { + if (e instanceof SocketException && !working) { + break; + } + listener.fail(e); + break; + } + } + + if (!server.isClosed()) { + try { + server.close(); + } catch (final IOException e) { + listener.fail(e); + } + } + } + + @Override + public synchronized void shutDown() { + if (!working) { + return; + } + working = false; + try { + server.close(); + mainThread.join(); + } catch (final IOException | InterruptedException e) { + listener.fail(e); + } + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractServer.java new file mode 100644 index 0000000..817cc47 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/AbstractServer.java @@ -0,0 +1,15 @@ +package com.mit.spbau.kirakosian.servers.impl; + +import com.mit.spbau.kirakosian.servers.Server; +import com.mit.spbau.kirakosian.servers.ServerStatsListener; + +public abstract class AbstractServer implements Server { + + public static final int PORT = 8099; + protected ServerStatsListener listener; + + @Override + public void setServerActionListener(final ServerStatsListener actionListener) { + listener = actionListener; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/BlockingServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/BlockingServer.java new file mode 100644 index 0000000..9ae6e22 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/BlockingServer.java @@ -0,0 +1,32 @@ +package com.mit.spbau.kirakosian.servers.impl.blockingServer; + +import com.mit.spbau.kirakosian.servers.ServerStatsListener; +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; +import com.mit.spbau.kirakosian.servers.impl.AbstractBlockingServer; + +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@SuppressWarnings("WeakerAccess") +public class BlockingServer extends AbstractBlockingServer { + + private final ExecutorService pool = Executors.newFixedThreadPool(3); + + public BlockingServer() throws AbortException { + super(); + } + + @Override + protected void processConnection(final Socket socket) { + new Session(socket, this); + } + + public ExecutorService getPool() { + return pool; + } + + public ServerStatsListener getListener() { + return listener; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/Session.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/Session.java new file mode 100644 index 0000000..a5ef379 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/blockingServer/Session.java @@ -0,0 +1,112 @@ +package com.mit.spbau.kirakosian.servers.impl.blockingServer; + +import com.mit.spbau.kirakosian.Utils; +import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException; +import com.mit.spbau.kirakosian.servers.ServerStatsListener; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY; +import static com.mit.spbau.kirakosian.Protocol.STOP; + +class Session { + + private final Executor writer = Executors.newSingleThreadExecutor(); + private final ExecutorService pool; + private final ServerStatsListener listener; + + private final Socket socket; + private DataOutputStream os; + + Session(final Socket socket, final BlockingServer server) { + pool = server.getPool(); + this.socket = socket; + listener = server.getListener(); + try { + os = new DataOutputStream(socket.getOutputStream()); + } catch (final IOException e) { + try { + socket.close(); + } catch (final IOException e1) { + listener.fail(e1); + } + listener.fail(e); + } + new Thread(this::processReading).start(); + } + + private void processReading() { + try (final DataInputStream is = new DataInputStream(socket.getInputStream())) { + int signal; + signal = is.readInt(); + while (signal != STOP) { + if (signal != NEW_QUERY) { + throw new UnexpectedProtocolMessageException(); + } + + final long queryBegin = System.currentTimeMillis(); + final int[] array = Utils.readArray(is); + pool.submit(new SortTask(array, queryBegin)); + + signal = is.readInt(); + } + + } catch (final IOException e) { + try { + socket.close(); + } catch (final IOException e1) { + listener.fail(e1); + } + listener.fail(e); + } + } + + private class SortTask implements Runnable { + + private final long queryBegin; + private final int[] array; + + SortTask(final int[] array, final long queryBegin) { + this.queryBegin = queryBegin; + this.array = array; + } + + @Override + public void run() { + final long begin = System.currentTimeMillis(); + Utils.sort(array); + final long end = System.currentTimeMillis(); + listener.timeForTask(end - begin); + writer.execute(new SendBackTask(array, queryBegin)); + } + } + + private class SendBackTask implements Runnable { + + private final long queryBegin; + private final int[] array; + + SendBackTask(final int[] array, final long queryBegin) { + this.queryBegin = queryBegin; + this.array = array; + } + + + @Override + public void run() { + try { + Utils.writeArray(os, array); + final long queryEnd = System.currentTimeMillis(); + listener.timeForClientOnServer(queryEnd - queryBegin); + } catch (final IOException e) { + listener.fail(e); + } + } + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/Client.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/Client.java new file mode 100644 index 0000000..797482f --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/Client.java @@ -0,0 +1,125 @@ +package com.mit.spbau.kirakosian.servers.impl.nonBlockingServer; + +import com.mit.spbau.kirakosian.Utils; +import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY; +import static com.mit.spbau.kirakosian.Protocol.STOP; + +@SuppressWarnings("WeakerAccess") +public class Client { + + private static final int BUFFER_SIZE = 1000_000; + + private final @NotNull SocketChannel channel; + private final NonBlockingServer server; + + private final ByteBuffer reading = ByteBuffer.allocate(BUFFER_SIZE); + private final ByteBuffer writing = ByteBuffer.allocate(BUFFER_SIZE); + MessageReader reader = new MessageReader(reading); + + private long queryBegin; + private State state; + + public Client(@NotNull final SocketChannel channel, final NonBlockingServer server) { + this.channel = channel; + this.server = server; + state = State.READING; + writing.flip(); + } + + public void onWrite() throws IOException { + synchronized (writing) { + if (state != State.WRITING) { + return; + } + channel.write(writing); + processWroteInfo(); + } + } + + public void onRead() throws IOException { + if (state != State.READING) { + return; + } + //noinspection StatementWithEmptyBody + while (channel.read(reading) > 0) {} + + processReadInfo(); + } + + private void processReadInfo() { + reading.flip(); + + while (!reader.completed() && reader.canRead()) { + reader.readNext(); + } + + if (reader.completed()) { + if (reader.getState() == STOP) { + server.unregisterRead(this); + server.unregisterWrite(this); + } else if (reader.getState() == NEW_QUERY) { + queryBegin = reader.queryBegin(); + server.pool().submit(new SortTask(reader.getArray())); + } else { + throw new UnexpectedProtocolMessageException(); + } + reader.refresh(); + reading.clear(); + return; + } + + reading.compact(); + } + + private void processWroteInfo() { + if (writing.remaining() == 0) { + server.listener().timeForClientOnServer(System.currentTimeMillis() - queryBegin); + state = State.READING; + } + } + + @NotNull + public SocketChannel getChannel() { + return channel; + } + + public void sendNewMessage(final int[] array) { + synchronized (writing) { + final byte[] message = Utils.makeArrayMessage(array); + writing.compact(); + writing.putInt(message.length); + writing.put(message); + writing.flip(); + } + } + + private class SortTask implements Runnable { + + private final int[] innerArray; + + public SortTask(final int[] innerArray) { + this.innerArray = innerArray; + } + + @Override + public void run() { + final long begin = System.currentTimeMillis(); + Utils.sort(innerArray); + final long end = System.currentTimeMillis(); + server.listener().timeForTask(end - begin); + sendNewMessage(innerArray); + state = State.WRITING; + } + } + + enum State { + READING, WRITING + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/MessageReader.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/MessageReader.java new file mode 100644 index 0000000..9caf011 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/MessageReader.java @@ -0,0 +1,91 @@ +package com.mit.spbau.kirakosian.servers.impl.nonBlockingServer; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.mit.spbau.kirakosian.Utils; +import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException; + +import java.nio.ByteBuffer; + +import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY; +import static com.mit.spbau.kirakosian.Protocol.STOP; + +@SuppressWarnings("WeakerAccess") +public class MessageReader { + + private final ByteBuffer buffer; + + private long queryBegin; + + private int signal = -1; + private int size = - 1; + private int readSize = 0; + private byte[] array; + + public MessageReader(final ByteBuffer buffer) { + this.buffer = buffer; + } + + public boolean canRead() { + if (size == -1) { + return buffer.remaining() >= 4; + } + return buffer.remaining() >= 1; + } + + public boolean completed() { + if (signal == -1) { + return false; + } + if (signal == STOP) { + return true; + } + if (signal == NEW_QUERY) { + return size != -1 && size == readSize; + } + throw new UnexpectedProtocolMessageException(); + } + + public void readNext() { + if (completed()) { + throw new RuntimeException(); // should never happen + } + if (signal == -1) { + queryBegin = System.currentTimeMillis(); + signal = buffer.getInt(); + return; + } + if (size == -1) { + size = buffer.getInt(); + array = new byte[size]; + return; + } + if (size != 0 && size == readSize) { + throw new RuntimeException(); + } + array[readSize++] = buffer.get(); + } + + public int getState() { + return signal; + } + + public int[] getArray() { + try { + return Utils.getArray(array); + } catch (final InvalidProtocolBufferException e) { + throw new RuntimeException(); // should never happen + } + } + + public long queryBegin() { + return queryBegin; + } + + public void refresh() { + array = null; + size = -1; + readSize = 0; + signal = -1; + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/NonBlockingServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/NonBlockingServer.java new file mode 100644 index 0000000..5c98a28 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/nonBlockingServer/NonBlockingServer.java @@ -0,0 +1,218 @@ +package com.mit.spbau.kirakosian.servers.impl.nonBlockingServer; + +import com.mit.spbau.kirakosian.servers.ServerStatsListener; +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; +import com.mit.spbau.kirakosian.servers.impl.AbstractServer; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.*; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@SuppressWarnings("WeakerAccess") +public class NonBlockingServer extends AbstractServer { + + private volatile boolean working = true; + private final ServerSocketChannel server; + + private Thread mainThread; + private Thread readingThread; + private Thread writingThread; + private final ExecutorService pool = Executors.newFixedThreadPool(3); + + private final Selector reader; + private final Selector writer; + + private final ConcurrentLinkedQueue registerRead = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue registerWrite = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue unregisterRead = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue unregisterWrite = new ConcurrentLinkedQueue<>(); + + public NonBlockingServer() throws AbortException { + try { + server = ServerSocketChannel.open(); + server.bind(new InetSocketAddress(PORT)); + + reader = Selector.open(); + writer = Selector.open(); + } catch (final IOException e) { + throw new AbortException(); + } + } + + @Override + public void start() { + working = true; + mainThread = new Thread(this::work); + readingThread = new Thread(this::processRead); + writingThread = new Thread(this::processWrite); + mainThread.start(); + readingThread.start(); + writingThread.start(); + } + + private void work() { + while (working) { + try { + final SocketChannel channel = server.accept(); + processConnection(channel); + } catch (final IOException e) { + if (working) { + listener.fail(e); + break; + } + break; + } + } + + closeAll(); + } + + private void processConnection(final SocketChannel channel) { + final Client client = new Client(channel, this); + try { + channel.configureBlocking(false); + } catch (final IOException e) { + listener.fail(e); + } + registerRead(client); + registerWrite(client); + } + + public void registerRead(final Client client) { + registerRead.add(client); + reader.wakeup(); + } + + public void registerWrite(final Client client) { + registerWrite.add(client); + writer.wakeup(); + } + + public void unregisterRead(final Client client) { + unregisterRead.add(client); + reader.wakeup(); + } + + public void unregisterWrite(final Client client) { + unregisterWrite.add(client); + writer.wakeup(); + } + + private void processRead() { + while (working) { + try { + while (!registerRead.isEmpty()) { + final Client client = registerRead.poll(); + if (client == null) { + continue; + } + client.getChannel().register(reader, SelectionKey.OP_READ, client); + } + while (!unregisterRead.isEmpty()) { + final Client client = unregisterRead.poll(); + if (client == null) { + continue; + } + client.getChannel().keyFor(reader).cancel(); + } + + final int ready = reader.select(); + if (ready == 0) { + continue; + } + final Iterator iterator = reader.selectedKeys().iterator(); + while (iterator.hasNext()) { + final SelectionKey key = iterator.next(); + final Client client = (Client) key.attachment(); + client.onRead(); + iterator.remove(); + } + } catch (final IOException e) { + if (working) { + listener.fail(e); + break; + } + break; + } + } + + closeAll(); + } + + private void processWrite() { + while (working) { + try { + while (!registerWrite.isEmpty()) { + final Client client = registerWrite.poll(); + if (client == null) { + continue; + } + client.getChannel().register(writer, SelectionKey.OP_WRITE, client); + } + while (!unregisterWrite.isEmpty()) { + final Client client = unregisterWrite.poll(); + if (client == null) { + continue; + } + client.getChannel().keyFor(writer).cancel(); + } + + final int ready = writer.select(); + if (ready == 0) { + continue; + } + final Iterator iterator = writer.selectedKeys().iterator(); + while (iterator.hasNext()) { + final SelectionKey key = iterator.next(); + final Client client = (Client) key.attachment(); + client.onWrite(); + iterator.remove(); + } + } catch (final IOException e) { + if (working) { + listener.fail(e); + break; + } + break; + } + } + + closeAll(); + } + + private void closeAll() { + if (working) { + working = false; + try { + reader.close(); + writer.close(); + server.close(); + mainThread.join(); + readingThread.join(); + writingThread.join(); + } catch (final IOException | InterruptedException e) { + listener.fail(e); + } + } + } + + @Override + public synchronized void shutDown() { + if (!working) { + return; + } + closeAll(); + } + + public ServerStatsListener listener() { + return listener; + } + + public ExecutorService pool() { + return pool; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/simpleServer/SimpleServer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/simpleServer/SimpleServer.java new file mode 100644 index 0000000..c7d7484 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/servers/impl/simpleServer/SimpleServer.java @@ -0,0 +1,58 @@ +package com.mit.spbau.kirakosian.servers.impl.simpleServer; + +import com.mit.spbau.kirakosian.connector.UnexpectedProtocolMessageException; +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; +import com.mit.spbau.kirakosian.servers.impl.AbstractBlockingServer; +import com.mit.spbau.kirakosian.Utils; + +import java.io.*; +import java.net.Socket; + +import static com.mit.spbau.kirakosian.Protocol.NEW_QUERY; +import static com.mit.spbau.kirakosian.Protocol.STOP; + +public class SimpleServer extends AbstractBlockingServer { + + public SimpleServer() throws AbortException { + super(); + } + + @Override + protected void processConnection(final Socket socket) { + try (final DataOutputStream os = new DataOutputStream(socket.getOutputStream()); + final DataInputStream is = new DataInputStream(socket.getInputStream())) { + + int signal; + signal = is.readInt(); + while (signal != STOP) { + if (signal != NEW_QUERY) { + throw new UnexpectedProtocolMessageException(); + } + final long queryBegin = System.currentTimeMillis(); + final int[] array = Utils.readArray(is); + + final long sortBegin = System.currentTimeMillis(); + Utils.sort(array); + final long sortEnd = System.currentTimeMillis(); + + Utils.writeArray(os, array); + os.flush(); + final long queryEnd = System.currentTimeMillis(); + + listener.timeForTask(sortEnd - sortBegin); + listener.timeForClientOnServer(queryEnd - queryBegin); + + signal = is.readInt(); + } + + } catch (final IOException e) { + try { + socket.close(); + } catch (final IOException e1) { + listener.fail(e1); + } + listener.fail(e); + } + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTest.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTest.java new file mode 100644 index 0000000..61b369a --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTest.java @@ -0,0 +1,79 @@ +package com.mit.spbau.kirakosian.testing; + +import com.mit.spbau.kirakosian.connector.ApplicationConnector; +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime; +import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime; +import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime; +import com.mit.spbau.kirakosian.servers.Server; +import com.mit.spbau.kirakosian.servers.Servers; +import com.mit.spbau.kirakosian.servers.exceptions.AbortException; + +import java.io.IOException; + +class ServerTest { + + private final TestOptions options; + private final StatsListener stats = new StatsListener(); + + ServerTest(final TestOptions options) { + this.options = options; + } + + /** + * This function runs test and stores information in {@link TestResults} class. + */ + void startTest(final TestResults results) throws IOException { + stats.clear(); + + // wait for second application + final ApplicationConnector connector = new ApplicationConnector(options, stats); // lethal error + connector.waitForNewClient(); // lethal error + System.out.println("Starting"); + + for (int currentValue = options.lowerBound(); + currentValue <= options.upperBound(); + currentValue += options.delta()) { + + final Server server; + try { + server = Servers.createServer(options.serverType()); + } catch (final AbortException e) { + // failed to create server, just skip test case + continue; + } + server.setServerActionListener(stats); + server.start(); + + System.out.println("Test case " + currentValue); + connector.startTestCase(); // lethal error + + server.shutDown(); + updateResult(results, currentValue); + stats.clear(); + } + + System.out.println("End of testing"); + connector.close(); + } + + private void updateResult(final TestResults results, final int value) { + if (stats.anyErrorsOccurred()) { + System.out.println("Test case was skipped cause errors"); + for (final Exception e : stats.getErrors()) { + e.printStackTrace(); + } + return; + } + if (options.requiresMetric(TaskTime.class)) { + results.addPoint(TaskTime.class, value, stats.getTaskTime()); + } + if (options.requiresMetric(ClientTime.class)) { + results.addPoint(ClientTime.class, value, stats.getClientTime()); + } + if (options.requiresMetric(ServerTime.class)) { + results.addPoint(ServerTime.class, value, stats.getServerTime()); + } + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTestInitializer.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTestInitializer.java new file mode 100644 index 0000000..8ae8fd8 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/ServerTestInitializer.java @@ -0,0 +1,154 @@ +package com.mit.spbau.kirakosian.testing; + +import com.mit.spbau.kirakosian.controller.Controller; +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.metrics.impl.ClientTime; +import com.mit.spbau.kirakosian.options.metrics.impl.ServerTime; +import com.mit.spbau.kirakosian.options.metrics.impl.TaskTime; +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.options.parameters.impl.ArraySize; +import com.mit.spbau.kirakosian.options.parameters.impl.ClientsNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.QueriesNumber; +import com.mit.spbau.kirakosian.options.parameters.impl.Delay; +import com.mit.spbau.kirakosian.servers.Servers; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +public class ServerTestInitializer { + + // this two fields must correspond with ServerTest class (actually, this class might have + // different implementations) + private final static Set> requiredOptions = new HashSet<>(); + private final static Set> supportedMetrics = new HashSet<>(); + + static { + requiredOptions.add(ArraySize.class); + requiredOptions.add(ClientsNumber.class); + requiredOptions.add(Delay.class); + requiredOptions.add(QueriesNumber.class); + + supportedMetrics.add(TaskTime.class); + supportedMetrics.add(ServerTime.class); + supportedMetrics.add(ClientTime.class); + } + + public static Set> getRequiredOptions() { + return requiredOptions; + } + + public static Set> getSupportedMetrics() { + return supportedMetrics; + } + + public static void startTest(final TestOptions options) { + options.setServerType(Servers.ServerType.Simple); + options.print(); + final ServerTest serverTest = new ServerTest(options); + final TestResults results = new TestResults(options); + try { + serverTest.startTest(results); + } catch (final IOException e) { + e.printStackTrace(); + Controller.cancel(); + } + + Controller.calculationsCompleted(results); + } + + public static void startTestDebug(final TestOptions options) { + options.setServerType(Servers.ServerType.Simple); + options.print(); + ServerTest serverTest = new ServerTest(options); + TestResults results = new TestResults(options); + try { + serverTest.startTest(results); + } catch (final IOException e) { + e.printStackTrace(); + Controller.cancel(); + } + + Controller.calculationsCompletedDebug(results); + + options.setServerType(Servers.ServerType.Blocking); + options.print(); + serverTest = new ServerTest(options); + results = new TestResults(options); + try { + serverTest.startTest(results); + } catch (final IOException e) { + e.printStackTrace(); + Controller.cancel(); + } + + Controller.calculationsCompletedDebug(results); + + options.setServerType(Servers.ServerType.NonBlocking); + options.print(); + serverTest = new ServerTest(options); + results = new TestResults(options); + try { + serverTest.startTest(results); + } catch (final IOException e) { + e.printStackTrace(); + Controller.cancel(); + } + + Controller.calculationsCompletedDebug(results); + + } + + public static void main(final String[] args) { + final TestOptions options = new TestOptions(); + options.setAlteringOptionMeta(ArraySize.class); + options.setOption(QueriesNumber.class, 25); + options.setOption(ArraySize.class, 5000); + options.setOption(ClientsNumber.class, 25); + options.setOption(Delay.class, 200); + options.setDelta(1000); + options.setLowerBound(1000); + options.setUpperBound(10000); + options.setServerType(Servers.ServerType.Simple); + + options.addMetric(TaskTime.class); + options.addMetric(ClientTime.class); + options.addMetric(ServerTime.class); + + String error = options.validate(); + if (error != null) { + System.out.println(error); + return; + } + + startTestDebug(options); + + options.setAlteringOptionMeta(ClientsNumber.class); + options.setDelta(10); + options.setLowerBound(10); + options.setUpperBound(100); + + error = options.validate(); + if (error != null) { + System.out.println(error); + return; + } + + startTestDebug(options); + + options.setAlteringOptionMeta(Delay.class); + options.setDelta(100); + options.setLowerBound(0); + options.setUpperBound(900); + + error = options.validate(); + if (error != null) { + System.out.println(error); + return; + } + + startTestDebug(options); + + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/StatsListener.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/StatsListener.java new file mode 100644 index 0000000..626a060 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/StatsListener.java @@ -0,0 +1,63 @@ +package com.mit.spbau.kirakosian.testing; + +import com.mit.spbau.kirakosian.servers.ServerStatsListener; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("WeakerAccess") +public class StatsListener implements ServerStatsListener { + + private final List errors = new ArrayList<>(); + + private final List taskTimes = new ArrayList<>(); + private final List serverTimes = new ArrayList<>(); + private final List clientTimes = new ArrayList<>(); + + public void clear() { + taskTimes.clear(); + serverTimes.clear(); + clientTimes.clear(); + errors.clear(); + } + + public boolean anyErrorsOccurred() { + return errors.size() != 0; + } + + public List getErrors() { + return errors; + } + + public double getTaskTime() { + return taskTimes.stream().mapToLong(e -> e).average().orElse(0); + } + + public double getServerTime() { + return serverTimes.stream().mapToLong(e -> e).average().orElse(0); + } + + public double getClientTime() { + return clientTimes.stream().mapToLong(e -> e).average().orElse(0); + } + + public void timeForClientOnClient(final long time) { + clientTimes.add(time); + } + + @Override + public void timeForClientOnServer(final long time) { + serverTimes.add(time); + } + + @Override + public void timeForTask(final long time) { + taskTimes.add(time); + } + + @Override + public void fail(final Exception e) { + errors.add(e); + } + +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/TestResults.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/TestResults.java new file mode 100644 index 0000000..e2a27a4 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/testing/TestResults.java @@ -0,0 +1,37 @@ +package com.mit.spbau.kirakosian.testing; + +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.metrics.MetricResult; + +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings("WeakerAccess") +public class TestResults { + + private final Map, MetricResult> results = new HashMap<>(); + + private final TestOptions options; + + public TestResults(final TestOptions options) { + this.options = options; + for (final Class meta : options.metrics()) { + results.put(meta, new MetricResult(meta)); + } + } + + public TestOptions options() { + return options; + } + + public void addPoint(final Class meta, final Integer parameter, final Number value) { + if (results.containsKey(meta)) { + results.get(meta).addResult(parameter, value); + } + } + + public MetricResult getResultsForMetric(final Class meta) { + return results.get(meta); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Scene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Scene.java new file mode 100644 index 0000000..3eae37d --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Scene.java @@ -0,0 +1,15 @@ +package com.mit.spbau.kirakosian.ui; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; + +public abstract class Scene extends JPanel { + + protected Scene() { + setBorder(new EmptyBorder(20, 20, 20, 20)); + setLayout(new BorderLayout()); + + setVisible(false); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/SceneManager.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/SceneManager.java new file mode 100644 index 0000000..bba3809 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/SceneManager.java @@ -0,0 +1,23 @@ +package com.mit.spbau.kirakosian.ui; + +import com.mit.spbau.kirakosian.ui.scenes.InitialScene; +import com.mit.spbau.kirakosian.ui.scenes.ResultsScene; +import com.mit.spbau.kirakosian.ui.scenes.SettingsScene; +import com.mit.spbau.kirakosian.ui.scenes.TestingScene; + +public class SceneManager { + + private static Window window; + public final static InitialScene INITIAL_SCENE = new InitialScene(); + public final static SettingsScene SETTINGS_SCENE = new SettingsScene(); + public final static TestingScene TESTING_SCENE = new TestingScene(); + public static final ResultsScene RESULTS_SCENE = new ResultsScene(); + + public static void init(final Window w) { + window = w; + } + + public static void setScene(final Scene scene) { + window.setScene(scene); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Window.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Window.java new file mode 100644 index 0000000..3f25e3c --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/Window.java @@ -0,0 +1,32 @@ +package com.mit.spbau.kirakosian.ui; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +public class Window extends JFrame { + + @SuppressWarnings("WeakerAccess") + @NotNull private Scene currentScene; + + public Window() { + super("Server Benchmark"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setResizable(false); + setSize(new Dimension(500, 600)); + + currentScene = SceneManager.SETTINGS_SCENE; + currentScene.setVisible(true); + setContentPane(currentScene); + } + + + void setScene(final Scene scene) { + currentScene.setVisible(false); + scene.setVisible(true); + setContentPane(scene); + revalidate(); + currentScene = scene; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/InitialScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/InitialScene.java new file mode 100644 index 0000000..28d5ef7 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/InitialScene.java @@ -0,0 +1,16 @@ +package com.mit.spbau.kirakosian.ui.scenes; + +import com.mit.spbau.kirakosian.ui.Scene; +import com.mit.spbau.kirakosian.ui.SceneManager; + +import javax.swing.*; + +public class InitialScene extends Scene { + + public InitialScene() { + super(); + final JButton button = new JButton(); + button.addActionListener(e -> SceneManager.setScene(SceneManager.SETTINGS_SCENE)); + add(button); + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/ResultsScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/ResultsScene.java new file mode 100644 index 0000000..6bdaf83 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/ResultsScene.java @@ -0,0 +1,46 @@ +package com.mit.spbau.kirakosian.ui.scenes; + +import com.mit.spbau.kirakosian.options.Utils; +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.metrics.MetricResult; +import com.mit.spbau.kirakosian.testing.TestResults; +import com.mit.spbau.kirakosian.ui.Scene; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.data.xy.XYDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import java.util.Map; + +public class ResultsScene extends Scene { + + public void acceptResults(final TestResults results) { + final Class meta = results.options().metrics().iterator().next(); + final JFreeChart xyLineChart = ChartFactory.createXYLineChart("", + Utils.getOptionInstance(results.options().alteringOption()).name(), "Time", + makeDataset(results), + PlotOrientation.VERTICAL, + true,true,false); + add(new ChartPanel(xyLineChart)); + + } + + private XYDataset makeDataset(final TestResults results) { + final XYSeriesCollection dataset = new XYSeriesCollection(); + for (final Class meta : results.options().metrics()) { + final String name = Utils.getMetricInstance(meta).name(); + final XYSeries series = new XYSeries(name); + + final MetricResult result = results.getResultsForMetric(meta); + for (final Map.Entry entry : result.results().entrySet()) { + series.add(entry.getKey(), entry.getValue()); + } + + dataset.addSeries(series); + } + return dataset; + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/SettingsScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/SettingsScene.java new file mode 100644 index 0000000..1cccd5c --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/SettingsScene.java @@ -0,0 +1,227 @@ +package com.mit.spbau.kirakosian.ui.scenes; + +import com.mit.spbau.kirakosian.testing.ServerTestInitializer; +import com.mit.spbau.kirakosian.options.metrics.MetricMeta; +import com.mit.spbau.kirakosian.options.parameters.ParameterOptionMeta; +import com.mit.spbau.kirakosian.options.UIOptions; +import com.mit.spbau.kirakosian.options.TestOptions; +import com.mit.spbau.kirakosian.servers.Servers; +import com.mit.spbau.kirakosian.ui.Scene; +import com.mit.spbau.kirakosian.ui.SceneManager; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.util.ArrayList; + +public class SettingsScene extends Scene { + + private final ButtonGroup buttonGroup = new ButtonGroup(); + private JComboBox comboBox; + private final java.util.List optionPanels = new ArrayList<>(); + + public SettingsScene() { + super(); + final JButton startButton = new JButton("Run server test"); + startButton.addActionListener(e -> + { + final TestOptions testOptions; + try { + testOptions = collectOptions(); + } catch (final IllegalOptionsException e1) { + JOptionPane.showMessageDialog(null, + "Error: Invalid options were set. Only integer values are allowed", "Error Message", + JOptionPane.ERROR_MESSAGE); + return; + } + final String error = testOptions.validate(); + if (error != null) { + JOptionPane.showMessageDialog(null, + "Error: " + error, "Error Message", + JOptionPane.ERROR_MESSAGE); + return; + } + SceneManager.setScene(SceneManager.TESTING_SCENE); + new Thread(() -> ServerTestInitializer.startTest(testOptions)).start(); + }); + final JPanel settingsPanel = createSettingsPanel(); + + add(settingsPanel); + add(startButton, BorderLayout.SOUTH); + } + + private TestOptions collectOptions() throws IllegalOptionsException { + final TestOptions options = new TestOptions(); + for (final Class meta : UIOptions.metrics()) { + options.addMetric(meta); + } + options.setServerType((Servers.ServerType) comboBox.getSelectedItem()); + for (final OptionPanel panel : optionPanels) { + panel.collectOptions(options); + } + return options; + } + + private JPanel createSettingsPanel() { + final JPanel settingsPanel = new JPanel(); + settingsPanel.setLayout(new BorderLayout()); + final JPanel optionsPanel = new JPanel(); + optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS)); + + optionsPanel.add(Box.createVerticalGlue()); + for (final ParameterOptionMeta option : UIOptions.options()) { + final OptionPanel panel = new OptionPanel(option); + optionPanels.add(panel); + optionsPanel.add(panel); + optionsPanel.add(Box.createVerticalGlue()); + } + + settingsPanel.add(createServerTypeOption(), BorderLayout.NORTH); + settingsPanel.add(optionsPanel); + return settingsPanel; + } + + private Component createServerTypeOption() { + final JComboBox comboBox = new JComboBox<>(); + for (final Servers.ServerType option : UIOptions.serverOptions()) { + comboBox.addItem(option); + } + ((JLabel)comboBox.getRenderer()).setHorizontalAlignment(JLabel.CENTER); + comboBox.setToolTipText("Server type"); + this.comboBox = comboBox; + return comboBox; + } + + private class OptionPanel extends JPanel { + + private final ParameterOptionMeta option; // option to set up + private final JSlider slider; // slider to choose option value + private final JPanel alterPanel; // panel with settings for altering option + + private boolean altering; // shows whether slider or panel is showed + private final JTextField lowerBound = new JTextField("0"); + private final JTextField upperBound = new JTextField("0"); + private final JTextField delta = new JTextField("0"); + + private OptionPanel(final ParameterOptionMeta option) { + this.option = option; + setLayout(new GridLayout(2, 1)); + + slider = new JSlider(option.minValue(), option.maxValue()); + setUpSlider(); + + final JLabel label = new JLabel(option.name()); + label.setOpaque(true); + label.setHorizontalTextPosition(JLabel.CENTER); + add(label); + + alterPanel = createAlterPanel(); + + add(createInnerPanel()); + } + + private JPanel createAlterPanel() { + final JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.add(Box.createHorizontalGlue()); + panel.add(createAlterOption("Min", lowerBound)); + panel.add(Box.createHorizontalGlue()); + panel.add(createAlterOption("Max", upperBound)); + panel.add(Box.createHorizontalGlue()); + panel.add(createAlterOption("Delta", delta)); + panel.add(Box.createHorizontalGlue()); + return panel; + } + + private Component createAlterOption(final String name, final JTextField textField) { + final JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + final JLabel label = new JLabel(name); + panel.add(label); + panel.add(Box.createHorizontalStrut(10)); + panel.add(textField); + return panel; + } + + /** + * Creates new inner panel + * + * @return created inner panel + */ + private Component createInnerPanel() { + final JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.add(slider); + + if (option.mayAlter()) { + final JRadioButton button = new JRadioButton(); + panel.add(button, BorderLayout.EAST); + buttonGroup.add(button); + button.addItemListener(e -> { + switch (e.getStateChange()) { + case ItemEvent.SELECTED: { + panel.remove(slider); + panel.add(alterPanel); + panel.revalidate(); + altering = true; + break; + } + case ItemEvent.DESELECTED: { + panel.remove(alterPanel); + panel.add(slider); + panel.revalidate(); + altering = false; + break; + } + } + }); + } + return panel; + } + + private void setUpSlider() { + final int length = option.maxValue() - option.minValue(); + slider.setMajorTickSpacing(getTickSpace(length, 4)); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.setMinorTickSpacing(getTickSpace(length, 50)); + slider.setSnapToTicks(true); + slider.setToolTipText(option.description()); + } + + /** + * The method evaluates optimal tick space for target number of slices. + * + * @param length length of the gap + * @param number number of slices + * @return optimal tick space + */ + private int getTickSpace(final int length, final int number) { + return length < number ? 1 : length / number; + } + + @SuppressWarnings("WeakerAccess") + public void collectOptions(final TestOptions options) throws IllegalOptionsException { + if (altering) { + options.setAlteringOptionMeta(option.getClass()); + try { + options.setLowerBound(Integer.parseInt(lowerBound.getText())); + options.setUpperBound(Integer.parseInt(upperBound.getText())); + options.setDelta(Integer.parseInt(delta.getText())); + } catch (final NumberFormatException e) { + throw new IllegalOptionsException(e); + } + options.setOption(option.getClass(), null); + } else { + options.setOption(option.getClass(), slider.getValue()); + } + } + } + + + private class IllegalOptionsException extends Throwable { + IllegalOptionsException(final NumberFormatException e) { + super(e); + } + } +} diff --git a/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/TestingScene.java b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/TestingScene.java new file mode 100644 index 0000000..b5431d9 --- /dev/null +++ b/ServerBenchmark/src/main/java/com/mit/spbau/kirakosian/ui/scenes/TestingScene.java @@ -0,0 +1,13 @@ +package com.mit.spbau.kirakosian.ui.scenes; + +import com.mit.spbau.kirakosian.ui.Scene; + +import javax.swing.*; + +public class TestingScene extends Scene { + + public TestingScene() { + super(); + add(new JLabel("Waiting for testing...")); + } +} diff --git a/ServerBenchmark/src/main/resources/array.proto b/ServerBenchmark/src/main/resources/array.proto new file mode 100644 index 0000000..469c67e --- /dev/null +++ b/ServerBenchmark/src/main/resources/array.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +package tutorial; + +option java_package = "com.mit.spbau.kirakosian"; +option java_outer_classname = "ArrayMessage"; + +message Array { + repeated int32 data = 1; +} \ No newline at end of file diff --git a/ServerBenchmark/src/script.py b/ServerBenchmark/src/script.py new file mode 100644 index 0000000..1528568 --- /dev/null +++ b/ServerBenchmark/src/script.py @@ -0,0 +1,55 @@ +import matplotlib.pyplot as plt +import numpy as np +from scipy import interpolate + +servers = ['Simple', 'Blocking', 'NonBlocking'] +metrics = ['ClientTime', 'ServerTime', 'TaskTime'] +params = ['ClientsNumber', 'ArraySize', 'Delay'] + +colors = ["red", "green", "blue"] + +def get_data(i, metric, param): + name = "results/logs/" + servers[i] + '_' + param + ".txt" + input = open(name, "r") + xs = [] + ys = [] + i = 0 + lines = input.readlines() + while i != len(lines) and not(lines[i].startswith(metric)): + i = i + 1 + if (i == len(lines)): + return [1, 2, 3], [1, 2, 3] + i = i + 1 + n = int(lines[i]) + for line in lines[i + 1 : i + n + 1]: + xs.append(int(float(line.split(' ')[0]))) + ys.append(int(float(line.split(' ')[1][0 : -1]))) + return xs, ys + +def build_plot(metric, param): + print(metric, param) + name = metric + '(' + param + ')' + plt.title(name) + plt.xlabel(param) + plt.ylabel(metric) + lines = [] + + for i in range(len(servers)): + xs, ys = get_data(i, metric, param) + x_coord = np.linspace(min(xs), max(xs)) + y_coord = interpolate.spline(xs, ys, x_coord) + + line, = plt.plot(x_coord, y_coord, color=colors[i]) + lines.append(line) + + plt.grid(True, linestyle='-', color='0.75') + plt.legend(lines, servers, loc='best') + + plt.savefig('results/plots/' + name + '.png') + plt.clf() + +if __name__ == '__main__': + for metric in metrics: + for param in params: + + build_plot(metric, param) \ No newline at end of file