This is a simple lightweight profiler for java, using a basic timer approach. It allows you to start timers in your functions, give them multiple segments, and stop them. You an then export the performance profile and visualize it in the displayer part of this project, which will display it as a tree.
The interface for this project is very simple. You only have to use the PerformanceTimer class. All its functions are thread-safe. However, enabling or disabling the profiler while timers are already running results in undefined behaviour. The profiler has three parts to it:
You can use PerformanceTimer.enable() and PerformanceTimer.disable() to turn on or off profiling. These are static functions, and it enables/disables globally. When the profiling is disabled, every function is exited immediately. By default, the profiling is enabled.
Timing is done accumulatively. Each function and segment time will get added to the previous times of that function and segment. They are accumulated with respect to the parent function timer calling this function, so if both function A and B call function C, and all three track their times, then there will be two performance measures for C, one called by A and one called by B. If a timer does not have a parent, it is seen as the root of a new process, and can be visualized independently by the displayer.
You can create a timer by calling the PerformanceTimer constructor. You give it your current class, the name of the function, and optionally the name of the first segment of the function, if you decide to split up that function into multiple segments. The constructor will automatically start the timer.
You can call the nextSegment() function to mark a new segment in the function. This will stop the timer for the previous segment and start the timer for the next segment.
To finish the last segment, simply call the stop() function.
To export the performance profile, use the PerformanceTimer.flushData() function, which is a static function, and takes in an output stream to which the data is written in json format. This json can then be loaded in by the displayer.
When starting the displayer, it will prompt you with a file selector where you can select the performance profile json. After selecting your json, it will be visualized.
The visualization shows you the performance in a tree structure. Each node will be a rectangle with its width representing the time spent in that function. The nodes are visualized with a top half and bottom half:
- The top half represents the total function. Hovering over the top half will tell you the class, the name of the function, the total amount of time spent in that function, the amount of times the function has been entered and the average amount time spend in the function per call.
- The bottom half represents each of the segments of that function. Hovering over a segment in the bottom of a node will tell you the name of the segment, as well as the hit count and the total and average times.
The top node is the root function of the process. All function calls in it are visualized under their parents to form a tree. The nodes are always sorted from most to least time spent in them.
You can scroll to zoom in or out horizontally. Use 'a' and 'd' to pan to the left and right. Use 'w' and 's' to visualize the next or the previous process. You can exit the program using ESC.
To use the profiler in your project, import the performance-timer module into your project and use the PerformanceTimer class. To import the module, first add JitPack to your repositories:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
And then add the dependency
<dependency>
<groupId>com.github.basmens.profiler</groupId>
<artifactId>performance-timer</artifactId>
<version>v1.0.0</version>
</dependency>
To your dependencies.
To run the visualizer, clone the github repo and run the Main class in the performance-displayer module in your IDE of choice.
This project already uses clever data structures to remain lightweight and to minimize cost of thread synchronization. However, that doesn't mean that there isn't a lot to be gained. I think with better fundamental architecture choices, the performance timing can be even more lightweight. Furthermore, use of a json serializer/deserializer would have also been smart, allowing less duplicate code between the two modules. Then using a gui library to make the displayer better, more interactive and more intuitive would be a nice improvement.