Showing posts with label performance. Show all posts
Showing posts with label performance. Show all posts

Wednesday, June 29, 2016

The art of creating efficient algorithms

How often do we think outside the box to create a creative and efficient algorithm when we develop software? I bet answer is "probably not often enough". I have not been coding in Python for close to two years. In fact I have not been coding much for the past two years in any language apart from a few Java utilities and the odd shell scripts to help me get things done in a predicable and automated manner. Hence this is not a post about how to create good algorithms, but rather some reflections on code performance during my re-entry to Python programming. So please forgive me for any false assumptions and poor code quality.

While reading a book on Python 3 recently, I came across an interesting anecdote about the German mathematician Carl Friedrich Gauss, who at the age of 8 was given a homework assignment to sum up all the numbers between 1 and 100. To his teacher’s astonishment Carl Friedrich answered 5050 a few seconds later, and it was the teacher had to go home and check if the answer was correct.

Instead of adding every number between 1 and 100, the young Carl Friedrich quickly realized that there were 50 pairs of unequal numbers that would sum up to 100:
99 + 1
98 + 2
And so on, all the way down to 51 + 49
And since he had not included the number 50 yet, the answer was (50 pairs * 100) + 50 = 5050

So the youngster had by himself discovered a pretty efficient algorithm for summing up integers. Not bad for an 8 year old! According to Wikipedia this story is contested, but nevertheless it is a very good example of a creative solution for optimizing an algorithm.

Naturally I needed to test the same logic on a computer, so I wrote a small Python script to check the human way of thinking (the count()method) and the young Carl Friedrich' way of thinking (the f_count() method):

import time

class FunnyMath(object):

    def __init__(self,num=0):
        self.num = num
   
    def count(self,num=0):
        start = time.time()
        self.num = num;
        for i in range(1,num):
            self.num += i
        end = time.time()
        print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),int(self.num)))
    
    def f_count(self,num=0):
        start = time.time()
        self.num = num**2/2.0 + num/2.0
        end = time.time()
        print("Creative approach took {} seconds to get the result: {}".format(round((end-start),2),int(self.num)))
    
def main():
    fm = FunnyMath()
    num = 100000000
    fm.f_count(num)
    fm.count(num)
   
if __name__ == "__main__":
    main()

How long it takes to run will of course depend on your hardware resources. I tested this script on two different laptops.

On the "fast" laptop I got the following output:
Creative approach took 0.0 seconds to get the result: 5000000050000000
Traditional approach took 14.88 seconds to get the result: 5000000050000000

On the "slow" laptop I got the following output:
Creative approach took 0.0 seconds to get the result: 5000000050000000
Traditional approach took 27.76 seconds to get the result: 5000000050000000

So it is an interesting example that shows that some algorithms can and should be optimized. Not only did f_count() perform better. It also had fewer lines of code.

 But with a rewrite I could also optimize the slow count() method:

import time

class FunnyMath(object):

    def __init__(self,num=0):
        assert type(num) is int, "num was not an integer {}".format(num)
        self.num = num
   
    def count(self):
        num = self.num
        for i in range(1,num):
            num += i
        return int(num)
        
    def f_count(self):
        return int(self.num**2/2.0 + self.num/2.0)
    
def main():
    fm = FunnyMath(num = 100000000)
    start = time.time() 
    num = fm.f_count()
    end = time.time()
    print("Creative approach took {} seconds to get the result: {}".format(round((end-start),2),num))
    
    start = time.time()
    num = fm.count()
    end = time.time()
    print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),num))
    
if __name__ == "__main__":
    main()

Then I ran it on the "fast" laptop and got the following output:
Creative approach took 0.0 seconds to get the result: 5000000050000000
Traditional approach took 5.98 seconds to get the result: 5000000050000000

A full rewrite was not really necessary, but I wanted a cleaner and more compact code for my class. The observant reader might have spotted the real problem with the first definition of the count() method:

def count(self,num=0):
        start = time.time()
        self.num = num;
        for i in range(1,num):
            self.num += i
        end = time.time()
        print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),int(self.num)))
 
For each iteration in the loop I was updating the class variable num instead of just using a local variable called num, so the same performance gain for the count() method could have been achieved with a small rewrite:

def count(self,num=0):
        start = time.time()
        num = num;
        for i in range(1,num):
            num += i
        end = time.time()
        print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),int(num)))

When I ran that on the "fast" laptop I got the the following output:
Traditional approach took 5.92 seconds to get the result: 5000000050000000

The reason for this performance gain is explained in the Python wiki.

Still, the count() method would always be slower than the f_count() method.

Monday, January 11, 2016

How to find which Java thread on WebLogic that causes high CPU utilization on Linux

Scope

Sometimes your Linux server is having close to 100% CPU utilization. It is easy and quick to find which process is spending more CPU by running the top command. It takes a few more steps to establish which Java(TM) thread is using CPU, and what that thread is doing. This blog will explain the procedure for gather evidence to your developers that their code is too CPU intensive.

Scenarios

I have tried this procedure using the following versions of Oracle® WebLogic and Java(TM):
  • Oracle® WebLogic 12.1.3 with Oracle® Java(TM) SE 7 - jdk1.7.0_79
  • Oracle® WebLogic 10.3.6 with Oracle®Java(TM) SE 7 - jdk1.7.0_79
  • Oracle® WebLogic 10.3.6 with Oracle® Java(TM) SE 6 - jdk1.6.0_45
  • Oracle® WebLogic 10.3.6 with Oracle® JRockit(R) 6 - R28.2.7
These guidelines would also work on other servers using Oracle® Java(TM) SE.

Overview

The procedure for finding the most CPU intensive thread is basically the same for each combination of Oracle® WebLogic and Java(TM):
  • Find the ID of the process that has highest CPU utilization
  • Find the ID of the thread that has highest CPU utilization based on the process ID found above.
  • Take a thread dump of the process based on the process ID found above.
  • Search for the thread ID in the thread dump
  • Identify the code that causes issues


Find the ID of the process that has highest CPU utilization

Use the top command and look at the CPU% column to identify which PID has highest CPU utilization. In the case below, the PID with 7253 uses most CPU%.



Find the ID of the thread that has highest CPU utilization based on the process ID found above

Again we can use the top command to find out which threads are spending most CPU% with the PID as input. The command will be:
top -H -p <PID>



Take a thread dump of the process based on the process ID found above

Use the jstack <pid> command with Java HotSpot(TM) or the jrcmd <pid> print_threads command with Oracle® JRockit(R) to get a thread dump. For instance:
$JAVA_HOME/bin/jstack 7253 > /tmp/stack.txt



Search for the thread ID in the thread dump file

Some tools will write the thread IDs as decimal numbers and others will write it as hexadecimal numbers. In this blog post I used Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode) which wrote the thread IDs as hexadecimal numbers. When testing Oracle® JRockit(R) however, my thread IDs were written with decimal numbers. 

From the list of thread IDs above, I converted the three thread IDs with highest CPU% to hex:

[oracle@localhost dom_12c_hotspot7]$ printf "%x\n" 7255
1c57
[oracle@localhost dom_12c_hotspot7]$ printf "%x\n" 7286
1c76
[oracle@localhost dom_12c_hotspot7]$ printf "%x\n" 7291
1c7b

I can now search for 1c57, 1c76 and 1c7b in the /tmp/stack.txt file I created.

Nothing exciting about 1c57 as it was only the "VM Thread":


The other thread ID 1c76 is coming from my custom code:


The third thread ID 1c7b is also coming from my custom code, and that thread is even STUCK.:



This was just an example on how to find threads with high CPU utilization. The code deployed was written poorly on purpose to cater for this article. Finding the performance issue on a real life application will probably prove to be more complex. In case there are multiple threads that fights over the CPU, then it is probably better to do live analyses using a tool like JVisualVM.

Thursday, September 24, 2015

Configure sysstat on Antergos Linux

Installation

Install sysstat

pacman -S sysstat

Install a cron implementation

pacman -S cronie


Configuration

Configure crontab

Run crontab -e to open the crontab editor, and enter the following to setup automatic sysstat collection:

# Run system activity accounting tool every 10 minutes
*/10 * * * * /usr/lib/sa/sa1 1 1
# Generate a daily summary of process accounting at 23:53
53 23 * * * /usr/lib/sa/sa2 -A

Save and exit the crontab editor.

Configure cronie.service

touch /etc/crontab
systemctl enable cronie.service
systemctl start cronie.service

Verification

To check that historic syststat collection is working run:
ls -l /var/log/sa
and verify that a new file is created with the name san, where n is the numeric representation of the current day of the month. For instance, if you did this on September 24th, the file would be called /var/log/sa/sa24. Please note that it could take up to 10 minutes for this file to be created.

Troubleshooting

If the /var/log/sa directory is still empty, it is probably something wrong with the crontab or the cronie.service.

Check cronie.service

systemctl status cronie.service -l

Check system journal

journalctl -xn