No dia 08/07/2008 o blog var/log/mind publicou uma comparação de desempenho entre algumas linguagens de programação. As linguagens testadas pelo blog foram C++, JAVA, Ruby, JRuby, Python, Jython, Groovy e PHP. Esse tema gerou até um bafafá na blogosfera nacional e internacional.

A tipo de benchmark feito pelo é blog foi bem simples e baseado em um pedaço de lógica onde é medido o tempo de cada iteração num total de 100000 iterações.

O mais espantoso foi que JAVA obteve um desempenho melhor do que C++. Esse resultado me deixou com uma pulga atrás da orelha porque sempre acreditei (mas nunca havia testado) de que C++ por ser somente compilado seria bem mais rápido do que JAVA que é compilado e interpretado pela JVM.

Então resolvi fazer o mesmo teste que o blog var/log/mind fez. Utilizei os mesmos códigos disponibilizados por ele e rodei eles em minha máquina. Somente não testei Python, Jython e o Groovy.

Ambiente de Testes

Meu ambiente de testes difere do ambiente de testes do var/log/mind (veja a tabela mais abaixo), assim como versões de compiladores e interpretadores. Abaixo segue o meu ambiente de testes e mais um pouco abaixo as versões dos compiladores e interpretadores usados no teste.

Meu ambiente:

  • Processador: AMD 4200+ X2 64 Bits
  • Memória: 3GB RAM
  • SO: Windows XP SP2 Professional

As diferenças entre o ambiente de teste original do var/log/mind e o meu ambiente de teste:

MeuPost.com var/log/mind
Processador AMD 4200+ X2 64 Bits Intel® Core™ Duo CPU T2600 @ 2.16GHz
Memória 3GB RAM 2GB
SO: Windows XP SP2 Professional Ubuntu Gutsy Gibbon 7.10

Compiladores e Interpretadores usados:

  • C++: MinGW32 GCC: 3.4.2
  • JAVA 1.6
  • Ruby 1.8.6-26
  • JRuby 1.1
  • PHP 5.2.3

Teste:

Para realizar o teste rodei 5 vezes o código de cada linguagem diferente e anotei todos os resultados em nanossegundos. Depois tirei a média dos resultados.

Resultados

Seguinte a tendência dos resultados, em meu ambiente JAVA também foi superior ao C++. Os resultados também mostraram que o Python está mais maduro que Ruby embora nas últimas versões do Ruby (1.9) o desempenho tenha melhorado muito.

Outro fato interessante é que o JRuby está também mais rápido que Ruby. Já o PHP mostra a força de sua maturidade e popularidade ganhando tanto do Ruby como do JRuby, isso em meu ambiente, porque no var/log/mind tanto o Ruby como o JRuby ganharam do PHP.

Acredito que esse desempenho superior do Ruby no var/log/mind está diretamente associado ao sistema operacional. O Ruby está rodando muito mais rápido em ambientes Linux do que em Windows.

Abaixo segue a tabela de resultados:

Iteração 1 Iteração 2 Iteração 3 Iteração 4 Iteração 5 Média Total
C++ 12000 12000 12000 12000 12000 12000 60000
Java 1509 1369 1384 1376 1385 1404,6 7023
Ruby 491990 493750 494690 493280 494840 444232 2468550
Jruby 244040 246360 243830 243450 246750 244886 1224430
PHP 125200 125030 124880 124890 124900 124980 624900
1 segundo = 1000000 microssegundo = 1000000000 nanossegundo

Para facilitar a visualização dos resultados, abaixo segue um gráfico com o resultado do teste:

Desempenho das Linguagens de Programação

Códigos

Abaixo segue os códigos utilizados nos testes. São os mesmos do var/log/mind com uma única diferença no código C++. Como o var/log/mind usou o Linux, ele usou a função gettimeofday(), em Windows essa função não existe, então eu como não sou um programador C++, achei essa função no Blog OpenAsthra e coloquei no código.

Todos os outros códigos são exatamente iguais.

C++

#include <time.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
 
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
#else
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
#endif
 
struct timezone 
{
  int  tz_minuteswest; /* minutes W of Greenwich */
  int  tz_dsttime;     /* type of dst correction */
};
 
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
  FILETIME ft;
  unsigned __int64 tmpres = 0;
  static int tzflag = 0;
 
  if (NULL != tv)
  {
    GetSystemTimeAsFileTime(&ft);
 
    tmpres |= ft.dwHighDateTime;
    tmpres <<= 32;
    tmpres |= ft.dwLowDateTime;
 
    tmpres /= 10;  /*convert into microseconds*/
    /*converting file time to unix epoch*/
    tmpres -= DELTA_EPOCH_IN_MICROSECS; 
    tv->tv_sec = (long)(tmpres / 1000000UL);
    tv->tv_usec = (long)(tmpres % 1000000UL);
  }
 
  if (NULL != tz)
  {
    if (!tzflag)
    {
      _tzset();
      tzflag++;
    }
    tz->tz_minuteswest = _timezone / 60;
    tz->tz_dsttime = _daylight;
  }
 
  return 0;
}
 
#define TEST
#ifdef TEST
#endif
 
class Person
{
 
    public:
 
        Person(int count) : _next(NULL), _prev(NULL) { _count = count; }
        int shout(int shout, int nth)
        {
            if (shout < nth) return (shout + 1);
            _prev->_next = _next;
 
            _next->_prev = _prev;
            return 1;
        }
        int count() { return _count; }
        Person* next() { return _next; }
        void next(Person* person) { this->_next = person ; }
        Person* prev() { return _prev; }
        void prev(Person* person) { this->_prev = person; }
    private:
        int _count;
        Person* _next;
        Person* _prev;
};
 
class Chain
{
    public:
        Chain(int size) : _first(NULL)
        {
            Person* current = NULL;
            Person* last = NULL;
            for(int i = 0 ; i < size ; i++)
            {
                current = new Person(i);
                if (_first == NULL) _first = current;
                if (last != NULL)
                {
                    last->next(current);
                    current->prev(last);
                }
                last = current;
            }
            _first->prev(last);
            last->next(_first);
        }
        Person* kill(int nth)
        {
            Person* current = _first;
            int shout = 1;
            while(current->next() != current)
            {
                Person* tmp = current;
                shout = current->shout(shout,nth);
                current = current->next();
                if (shout == 1)
                {
                    delete tmp;
                }
            }
            _first = current;
            return current;
        }
        Person* first() { return _first; }
    private:
        Person* _first;
};
 
int main(int argc, char** argv)
{
    int ITER = 100000;
    Chain* chain;
    struct timeval start, end;
    gettimeofday(&start,NULL);
    for(int i = 0 ; i < ITER ; i++)
    {
        chain = new Chain(40);
        chain->kill(3);
        delete chain;
    }
    gettimeofday(&end,NULL);
    fprintf(stdout,"Time per iteration = %d microsecondsnr", (((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec - start.tv_usec)) / ITER);
    //fprintf(stdout,"Last man standing is %dnr", (chain->first()->count() + 1));
    return 0;
}
 
}

JAVA:

package com.dnene.josephus;
 
public class Chain {
 
	private Person first = null;
 
	public Chain(int size) {
		Person last = null;
		Person current = null;
 
		for (int i = 0; i < size; i++) {
			current = new Person(i);
 
			if (first == null)
				first = current;
 
			if (last != null) {
				last.setNext(current);
				current.setPrev(last);
			}
 
			last = current;
		}
 
		first.setPrev(last);
		last.setNext(first);
	}
 
	public Person kill(int nth) {
		Person current = first;
		int shout = 1;
 
		while (current.getNext() != current) {
			shout = current.shout(shout, nth);
			current = current.getNext();
		}
 
		first = current;
 
		return current;
	}
 
	public Person getFirst() {
		return first;
	}
 
	public static void main(String[] args) {
 
		int ITER = 100000;
		long start = System.nanoTime();
 
		for (int i = 0; i < ITER; i++) {
			Chain chain = new Chain(40);
			chain.kill(3);
		}
 
		long end = System.nanoTime();
 
		System.out.println("Time per iteration = " + ((end - start) / (ITER))
				+ " nanoseconds.");
	}
}
 
package com.dnene.josephus;
 
public class Person {
 
	int count;
	private Person prev = null;
	private Person next = null;
 
	public Person(int count) {
		this.count = count;
	}
 
	public int shout(int shout, int deadif) {
		if (shout < deadif)
			return (shout + 1);
 
		this.getPrev().setNext(this.getNext());
		this.getNext().setPrev(this.getPrev());
 
		return 1;
	}
 
	public int getCount() {
		return this.count;
	}
 
	public Person getPrev() {
		return prev;
	}
 
	public void setPrev(Person prev) {
		this.prev = prev;
	}
 
	public Person getNext() {
		return next;
	}
 
	public void setNext(Person next) {
		this.next = next;
	}
}

Ruby / JRuby

class Person
    attr_reader :count, :prev, :next
    attr_writer :count, :prev, :next
 
    def initialize(count)
        #puts 'Initializing person : ' + count.to_s()
        @count = count
        @prev = nil
        @next = nil
    end
 
    def shout(shout, deadif)
        if shout < deadif
            return shout + 1
        end
        @prev.next = @next
        @next.prev = @prev
        return 1
    end
end      
 
class Chain
    attr_reader :first
    attr_writer :first
 
    def initialize(size)
        @first = nil
        last = nil
        for i in (1..size)
            current = Person.new(i)
            if @first == nil
                @first = current
            end
            if last != nil
                last.next = current
                current.prev = last
            end
            last = current
        end
        @first.prev = last
        last.next = @first
    end
 
    def kill(nth)
        current = @first
        shout = 1
        while current.next != current
            shout = current.shout(shout,nth)
            current = current.next
        end
        @first = current
        return current
    end
end
 
ITER=100000
start = Time.now
ITER.times { |i|
chain = Chain.new(40)
chain.kill(3)
}
ends = Time.now
puts 'Time per iteration = ' + ((ends - start) * 1000000 / ITER).to_s() + " microseconds"

PHP

<?php
class Person
{   
    function __construct($c)
    {   
        $this->count = $c;
    }       
 
    function getPrev()
    {       
        return $this->prev; 
    }           
 
    function setPrev($pr)
    {   
        $this->prev = $pr;
    }   
 
    function getNext()
    {
        return $this->next;
    }
 
    function setNext($nxt)
    {
        $this->next = $nxt;
    }
 
    function shout($shout, $nth)
    {
        if ($shout < $nth)
        {
            return $shout + 1;
        }
        $this->getPrev()->setNext($this->getNext());
        $this->getNext()->setPrev($this->getPrev());
        return 1;
    }
}
 
class Chain
{
    var $first;
 
    function __construct($size)
    {
        for($i = 0; $i < $size ; $i++)
        {
            $current = new Person($i);
            if ($this->first == null) $this->first = $current;
            if ($last != null)
            {
                $last->setNext($current);
                $current->setPrev($last);
            }
            $last = $current;
        }
        $this->first->setPrev($last);
        $last->setNext($this->first);
    }
 
    function kill($nth)
    {
        $current = $this->first;
        $shout = 1;
        while($current->getNext() !== $current)
        {
            $shout =  $current->shout($shout,$nth);
            $current = $current->getNext();
        }
        $this->first = $current;
    }
}
 
$start = microtime(true);
$ITER = 100000;
for($i = 0 ; $i < $ITER ; $i++)
{
    $chain = new Chain(40);
    $chain->kill(3);
}
$end = microtime(true);
printf("Time per iteration = %3.2f microsecondsnr",(($end -  $start) * 100000 / $ITER));
?>

É isso pessoal.

Esse tema de desempenho sempre gera polêmica pois sempre há brechas e margens para discussão. Então, quem quiser debater sobre o assunto.

Até a próxima.