#Code Optimization
295 messages · Page 1 of 1 (latest)
⌛ This post has been reserved for your question.
Hey @true sorrel! Please use
/closeor theClose Postbutton above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically closed after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
check a profiler to find out what's slow
Also, what's the point of that code?
AtomicBoolean running = new AtomicBoolean(true);
Thread thread = new Thread(() -> {
try {
Thread.sleep((long) (TURN_TIME_LIMIT * 0.8));
running.set(false);
} catch (Exception e) {
e.printStackTrace();
}
});
thread.start();
Why don't you just check the current system time instead?
also synchronization etc could make your code slower
I did that before and with this I got a 2% improvement, even though its ugly.
The profiler is telling me the randomPlay and generateAllActions are the main bottlenecks.
I am both looking to improve the algorithm as the code itself.
What exactly did you use before?
so the whole MCTS is synchronized? Which means that you cannot run it in multiple threads..?
It is going to be ran single threaded so I can remove the synchronized
Also, are you mutating your data classes anywhere?
Sorry I dont understand that terminology
well you could maybe attempt to use multiple threads for a speedup
you have data classes like Action5
Are you changing the variables in these data classes?
I am not talking about the names
it might be faster if you use records (so you can't change any of the variables) for some of the data classes and recreate them when they are changed
e.g. MCTSState5
I have never heard of records
that could be faster but it could also be slower
records are special classes where all fields are final
Which Java version are you using?
16
you could try updating your Java version - there are lots of performance updates with newer Java versions
¯_(ツ)_/¯
It's a task where u have to develop a bot for a game and you need to maximize your winning percentages. The file I sent above is the only file you can control.
I am getting decent results against minimax with alpha-beta pruning etc but I am looking to even get more wins.
Records are available in Java 16
Ah then I will definitly have a look at them. What makes them faster than classes?
The JIT knows that you cannot change the variables in there and can do some optimizations with that
especially in newer Java versions
not sure about 16
Also why don't you try using multiple threads
e.g. you could play 2 games at the same time
(or more)
and then combine the results
Ill try to explain it more clear.
Bot 1 and Bot 2 will play 1000 games against eachother.
8 threads will play 125 games simultaneously and the results will be combined.
e.g. you start 4 threads that all play 20 games and then you combine the results
Yes that's what happens, but thas has no influence on the performance of my bot right.
Are you doing that?
That doesnt speed up your actual bot, it just speeds up the time it takes to play 1000 games.
Are you pretraining your MCTS?
No
Where would the 'brains' be stored?
just create a file with an initial tree
and include that in the application
and when the application starts, it uses that
Alright well I like the idea, but everything is supposed to be in that 1 java-file I sent.
Could I store it in a list or something?
Also, if possible, keep objects confined to a method
that could also improve performance
What does that mean?
e.g. if a method creates an object, try to not pass that object to many other methods
and especially don't save that object anywhere else
if you do that, these objects might be stored on the stack hence less allocation/GC work etc
I actually had a version first where I did that, but now I made these global objects that every method uses.
make a copy of your version and change it to use fewer global objects
one by one
maybe making some more local improves performance
these:
final MCTSNode5 rootNode = new MCTSNode5();
final ArrayList<Action5> actionList = new ArrayList<>();
Adding these instead of always re-creating them in methods got me more than 10% improvement execution time wise
while it might not be the case with others
I fee llike making rootNode local could be an improvement
The global objects got me a massive improvement so I think the opposite is true in here unless I did something wrong in older versions with locals
but creating big lists can be slow
genereateAllActions is a huge switch case with tons of loops, do you see bad stuff in there?
well, the loops
yep but is there a way to get rid of em?
(did that so it is easier readable for me)
You could try making Action5 a record
that could speed up something, also in that method
Can you show PylosSphere and PylosLocation?
yes I can but I am not allowed to change anything in these classes
I will definitly try it out, but I dont see directly what difference that would make in the method
Also, do you really need the public StudentPlayer2 player; in Action5?
if some fields are unnecessary, remove them
It is to access the pylosGamesimulator. First I had it static but it messed up when the tournement split up the games over multiple threads, since statics arent thread-safe.
So thats why i added that field to the actions class
yeah but what do you need it for?
in the MOVE case, you are iterating through all locations
maybe you can find a way to only iterate through usable locations or similar
I use the pylosgamesimulator in the switch cases here
the difference is that it doesn't need to store the object on the heap
parameters could be more efficient than fields
and if you have many Action5s, it could also result in you needing less memory
but for all of the optimizations, you'd need to check how well they work
I will definitly try them out. Are there also like libraries to speed up stuff?
Any other suggestions?
This is my project structure and I am trying to benchmark 1 file that is in pylos-student, but it uses the other packages aswell.
decide on a module where you want to do benchmarks in
then add both jmh-core and jmh-generator-annprocess to the annotation processor
what is the annotation processor?
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${uberjar.name}</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<!--
Shading signed JARs will fail without this.
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
-->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
and you can use this configuration with the shade plugin
this is my current pom.xml of pylos-student:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pylos</artifactId>
<groupId>be.kuleuven</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pylos-student</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>be.kuleuven</groupId>
<artifactId>pylos-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.3</version>
</dependency>
</dependencies>
</project>
no JMH there
maybe you want to create another module for your benchmarks
if you want to
Ill do that but I am very new to this so I got no clue what to do to set it up
I installed maven
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
you probably need these dependencies
and you might also want to use that plugin to get a JAR with benchmarks
I added a benchmark module:
ok then add the things I mentioned to the benchmark module
and add a dependency on the other module you want to measure
So i need to add the 2 dependencies on the 2 modules?
if module A is dependent on module B, it should be sufficient to add a dependency on module A
ok
this is my pom.xml in benchmark module now:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>be.kuleuven</groupId>
<artifactId>pylos</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>org.example</groupId>
<artifactId>benchmark</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
I also added the 2 dependecies to the other module
add that as well
to <plugins>
that allows you to create a JAR for doing your benchmarks
now write your benchmarks in that project
For now, I would start with an empty benchmark
With that, you should be able to create a JAR using mvn package
and then you can just run the JAR with java -jar which will run your benchmarks
Do i write @Benchmark above the method I want to improve?
Or what do I do?
you create another benchmark that executes what you want to benchmark
in the benchmark project:
@Benchmark
void someBenchmark(){
whateverYouWantToTest();
}
if you need to do initialization work, you can do that as well
ill start with an empty one to test if its setup correctly then
yes
but initialization work shouldn't be done in the benchmark
and JMH is especially for microbenchmarks - it will run your benchmark over and over
you don't need the main
and I wouldn't print to the console in benchmarks unless you want to measure that
because printing something to the console will result in console spam
and for more accurate results, you might want to close IntelliJ etc when you are actually measuring something
I would like to benchmark methods from the file you helped me with earlier. How can I do that then?
It's in a different module so I am not sure how to test that
Im confused on how this works for multiple modules
you can use classes of other modules in your benchmarks
if the module you are using is a dependency of the benchmark module
Can you give me an example on how I can test a certain method from the code we discussed this morning?
you just call that method in your benchmark
I am trying to optimize the class I indicated with red
.
How can I refer to that method then? its in a different module
Does the benchmark module have a dependency on the student module?
I dont know what that means
in your benchmark module
you add a <dependency>
with the group id/artifact id of the student module
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>```
once you have that, you can use the classes from the student module in the benchmark module
These are my dependencies
yes, there
you add another dependency
What do i type?
with group id, artifact id and version of your student module
you add another block like the other two but different values
you can find the values in the pom.xml of the student module
<dependency>
<groupId>be.kuleuven</groupId>
<artifactId>pylos-student</artifactId>
</dependency>
``` i added this
yes
How can i access the methods in the Benchmark class then?
I am trying to import it but i must be using a wrong path
if you just type the class name, IntelliJ can typically add the import automatically
it asks me if it should move the class from the pylos-student to the benchmark module
Thats not what we want right
reload the maven project in IntelliJ
just close and reopen?
on the right side, there's a Maven tab
that should have a reload symbol somewhere
at least that's where it was a few years ago
What is the parent?
Is the benchmark module included in the maven tabL?
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>be.kuleuven</groupId>
<artifactId>pylos</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>pylos-core</module>
<module>pylos-gui</module>
<module>pylos-student</module>
<module>benchmark</module>
</modules>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
ok that looks good
is there a version in the student pom.xml?
not talking about dependencies or the <parent>
try executing mvn clean install
${project.version}
this means same version as current project
which is 1.0-SNAPSHOT in your case
if you scroll up, it should show you why
My main class of benchmark is corrupt
the class symbol should b e blue
not grey/orange
well you have a syntax error there
package org.example;
import org.openjdk.jmh.annotations.Benchmark;
import StudentPlayer2;
public class Main {
@Benchmark
public void someBenchmark(){
}
}
What do i do?
oh i fixed it
Lets say I want to benchmark a certain method, how do I do that in the best way?
just run the method once in the benchmark method
And do initialization outside that method since you don't want to benchmark the initialization
Im a bit confused. The StudentPlayer2 class is made to battle another bot. But state is needed to use methods right?
You can use an @Setup method for initialization
public class Main{
private StateClass whateverStateYouNeed;
private StudentPlayer2 player;
@Setup
public void setup(){
whateverStateYouNeed = initializeState();
player = new StudentPlayer2();
}
@Benchmark
public void someBenchmark(){
player.whateverYouWantToMeasure();
}
}
if the method to measure has some results, you should return it or use Blackhole
Well I am not sure how I should implement the setup. I usually just use the startBattle method to let my bot 1v1 another bot.
If you want to measure how performance battles are in general, set up everything necessary for the battle (players, maybe a battlefield) int he setup method
and run it in the benchmark method
This method starts a battle, but is there a point benchmarking this method?
Does the method wait for the battle to finish?
yes it starts 100 games and then prints the winrate
I would only do a single battle in your benchmark
since JMH runs your method over and over anyways
package org.example;
import be.kuleuven.pylos.game.PylosBoard;
import be.kuleuven.pylos.game.PylosGame;
import be.kuleuven.pylos.player.student.StudentPlayer2;
import be.kuleuven.pylos.player.student.StudentPlayerJonas;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Setup;
import java.util.Random;
import static be.kuleuven.pylos.main.PylosMain.startSingleGame;
public class Main {
@Setup
public void init(){
}
@Benchmark
public void someBenchmark(){
startSingleGame();
}
}
This is the current benchmark class
since like it is running the benchmark
seems*
I want to benchmark the MCTS method for example. I dont know how to do the properly setup a game.
I get tons of errors because it didnt initialize properly
well, you might find the code for initializing the game in the start methods
public static void startSingleGame() {
Random random = new Random(0);
PylosPlayer playerLight = new StudentPlayerJonas();
PylosPlayer playerDark = new StudentPlayer2();
PylosBoard pylosBoard = new PylosBoard();
PylosGame pylosGame = new PylosGame(pylosBoard, playerLight, playerDark, random,
PylosGameObserver.CONSOLE_GAME_OBSERVER, PylosPlayerObserver.NONE);
pylosGame.play();
}
This is the code to start a single game
that's how a board is initialized
but maybe something happens inside the pylosGame.play(); method
seems like doing the stuff from startSingleGame might be enough
but maybe you want to do a few random moves with both players before starting your benchmarks
im getting nullpointer exceptions in my p1 class
well, it tells you how long a game takes on average
this is why whole games aren't good benchmarks
and why you should probably calculate a single move in each benchmark
Ok ill try to go deeper
@Benchmark
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@Fork(2)
@Threads(4)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
Which one of these should I use?
A benchmark takes a pretty long time with the standard options
by default, it's 10 minutes per benchmark I think
you can reduce the number of iterations or similar
depends on what works for you
Is warmup / fork important?
warmup is important, yes
What exactly are warmup / fork?
I have to go soon but I want to tell you I really appreciate this.
warmup "warms up" your code so it gets fast
This will help me in my optimization and algorithm courses aswell
and regarding forks: JMH uses multiple Java processes (forks) to run multiple benchmarks independently
How should I use this benchmark tool to improve my code then?
Do I make some changes and then re-run the benchmark to check the amount of operations has gone up?
you can try reducing the fork count
is a high amount of operations good or bad?
Do i want to maximize them or minimize them?
many operations per second means fast
if you are measuring throughput
So I need to take a benchmark, write the amount of operations per second down, make a change to my code and re-do it?
And then compare if the amount of operations has gone up?
yeah
And isnt the amount of operations a bit random aswell?
hm?
Is it not operations per second?
well if you can run it many times per second, it's fast
Can i make multiple benchmark methods? or do i need to comment the other ones out when measuring
yes
if you have multiple benchmark methods, JMH will run all and give you a comparison of the results
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived.
If your question was not answered yet, feel free to re-open this post or create a new one.
In case your post is not getting any attention, you can try to use/help ping.
Warning: abusing this will result in moderative actions taken against you.
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived.
If your question was not answered yet, feel free to re-open this post or create a new one.
In case your post is not getting any attention, you can try to use/help ping.
Warning: abusing this will result in moderative actions taken against you.