Linux® vs Windows®: um benchmark com algoritmos de ordenação Negreiros, J1, Painho, M2, Lopes, I3, Malta, P4 [email protected], [email protected], ilí[email protected], [email protected] 1 Instituto Superior de Línguas e Administração, Quinta do Bom Nome, Lisboa, Portugal Instituto Sup. Estatística Gestão Informação-UNL, Campus de Campolide, Lisboa, Portugal 3 Instituto Superior de Gestão de Santarém, Complexo Andaluz, 2001-904, Santarém, Portugal 4 Universidade Lusófona, Campo Grande 376, Lisboa, Portugal 2 Resumo: Normalmente, os algoritmos apresentam como função resolver um problema específico a partir de um conjunto predefinido de passos singulares. Neste artigo, pretende-se utilizar cinco algoritmos de ordenação (Heap, Inserção, Selecção, Quick e Shell) como ferramenta de comparação de performance entre o sistema operativo SuSe Linux® e o Windows® XP Professional e, implicitamente, os respectivos compiladores (gcc e cl). Paralelamente, é apresentado o estado da arte relativo à complexidade logarítmica, tipos de algoritmos e outros estudos de benchmark envolvendo outros factores como o TCO (Total Cost of Ownership). Linux®; Windows®; Benchmark; Algoritmos de ordenação; Compiladores C/C++. Palavras-chave: 1. Complexidade Algorítmica: Introdução Os algoritmos são uma parte diária na nossa vida reflectindo-se, por exemplo, em pesquisas operacionais, acessos na Web ou problemas de optimização. Genericamente, um algoritmo pode ser visto como uma sequência de acções executáveis para a obtenção de uma solução relativo a um determinado problema. Na medição da execução de um algoritmo, é comum definir uma função de custo ou função de complexidade f(t,s,n) em que t e s representam o tempo e o espaço de memória necessários para executar uma sequência de passos para a resolução de um problema de dimensão n (quantidade de input). Assim, é necessário distinguir três cenários: A) Melhor caso – Corresponde ao menor tempo de execução sobre todas as possibilidades de input de tamanho n; B) Pior caso – Corresponde ao maior tempo de execução; C) Caso médio – Corresponde à média dos tempos de todas as entradas de tamanho n. Logicamente, o tempo de resposta de um algoritmo pode ser bastante diverso e por isso, a análise do comportamento da distribuição de probabilidades sobre o conjunto do input é de difícil estimação. Se para valores suficientemente pequenos de n qualquer algoritmo apresenta um pequeno custo para ser executado, mesmo para algoritmos ineficientes, a escolha do algoritmo é fundamental para grandes valores de input de modo a obter-se uma análise assintótica O(n) ou função de complexidade do algoritmo em tempos razoáveis. Em termos de literatura clássica [Preiss, 2001, Loudon, 1999], independentemente do tipo de paradigma associado aos algoritmos (indução, recursividade, tentativa e erro, divisão e conquista, balanceamento, programação dinâmica, algoritmos gulosos e aproximados), tem-se a seguinte classificação de classes de função de complexidade: Constante f(n)=O(1): O tempo de resolução do algoritmo é independente da quantidade de input. Logarítmica f(n)=O(log n): O tempo de execução varia relativamente pouco aquando um aumento significativo da quantidade de registos à entrada. Linear f(n)=O(n): O tempo de resposta depende directamente da quantidade de dados. Linear logarítmica f(n)=O(n log n): A solução do problema é linearmente complexa mas acentuando-se à medida que o n cresce. Quadrática f(n)=O(n2): Sempre que os dados de input duplicam, o tempo da solução quadruplica. Cúbica f(n)=O(n3): Sempre que a quantidade de input duplica, o tempo de execução é multiplicada por 8. Exponencial f(n)=O(2n): Quando o n dobra, o tempo de execução fica elevado ao quadrado. Factorial f(n)=O(n!): O pior dos problemas para solucionar pois o conceito da força bruta pode requerer um tempo virtualmente infinito para a obtenção da solução óptima. Imagine uma problema deste tipo com n=50. O tempo de resposta seria 3.0414E64 unidades de tempo. Por curiosidade, Garey e Johnson [1979] apresentam o seguinte quadro na qual mostra a razão de crescimento de várias funções de complexidade para tamanhos diferentes de n. Quadro 1 – O tempo de execução varia entre milésimos de segundo e biliões de séculos. Função de custo n n2 n3 n5 2n 3n 10 20 0.00001s 0.0001s 0.001s 0.1s 0.001s 0.059s 0.00002s 0.0004s 0.008s 3.2s 1s 58min Tamanho n 30 40 0.00003s 0.0009s 0.027s 24.3s 17.9min 6.5anos 0.00004s 0.0016s 0.64s 1.7min 12.7dias 3855séc 50 60 0.00005s 0.035s 0.125s 5.2min 35.7anos 108séc 0.00006s 0.0036s 0.316s 13min 366séc 1013séc Uma outra visão interessante e apresentado por Ziviani [2004] é o efeito causado pelo aumento da velocidade dos computadores sobre os algoritmos. Repare-se que para um aumento de 1000 vezes na velocidade de computação este apenas resolve 10 vezes mais rápido, se na presença de um algoritmo de complexidade O(2 n). Quadro 2 – Influência do aumento de velocidade de computação no tempo de resolução de problemas pertencentes a várias classes. Função de custo de tempo N n2 n3 Computador actual t1 t2 t3 Computador 100 vezes + rápido 100 t1 10 t2 4.6 t3 Computador 1000 vezes + rápido 1000 t1 31.6 t2 10 t3 Infelizmente, não existe um conjunto completo de regras para analisar programas [Aho, Hopcroft, Ullman, 1983]. Por exemplo, o tempo de escrita num disco ou de leitura de uma banda magnética não pode ser considerado como O(1). Se o programa possui funções não recursivas associado a condições de execução, o cálculo torna-se mais complicado pois não se sabe, à partida, o número total de vezes que esse procedimento vai ser executado. Só o próprio tempo de execução da condição de teste é constante. Se a função é recursiva então é fundamental o recurso a equações de recorrência para se calcular o tipo de complexidade associado. Baseado no trabalho de Ziviani [2004], imagine o seguinte procedimento recursivo de pesquisa de um determinado número inteiro numa possível tabela unidimensional com z elementos: Naturalmente, a complexidade desta função de pesquisa depende de cada uma das instruções em causa. Assim, a primeira e a segunda instrução tem uma complexidade O(1) enquanto a terceira é de ordem O(n). Contudo, o principal problema de calculo é a instrução recursiva Pesquisa(n/3). Considere a equação de recorrência para o bloco else begin...end: T(n)=n+T(n/3). Se n=1 então a complexidade T(1)=1. Globalmente, tem-se T(n)=n+T(n/3) com T(n/3)=n/3+T(n/3/3). Consequentemente, T(n/3/3)=n/3/3+T(n/3/3/3) e assim sucessivamente. Adicionando todos estes termos, a complexidade do problema inicial significa uma complexidade O(n) dado que T ( n) (1/ 3)i n i 0 1 n 1 1 3 3n 2 . Embora a complexidade de um algoritmo seja um factor importante para determinar o quão eficiente este pode ser, o facto é que o tempo de resposta de um algoritmo é um parâmetro extremamente importante a ter em consideração em qualquer análise de benchmark. Preiss [2001] confirma esta afirmação usando código Java no problema do somatório da série geométrica n xi (ver figura 1). Note que i 0 diferentes estratégias podem implicar diferentes performances. Como alternativa, este artigo apresenta como objectivo primário uma comparação de performance de dois sistemas operativos de 32 bits, SuSe Linux® e Windows XP® Professional, e respectivos compiladores (gcc 4.0.2 e C/C++ 14.00.5) baseado no tempo de resposta na resolução de cinco algoritmos de ordenação. Figura 1 – Tempo de processamento versus quantidade de input relativo ao algoritmo somatório da série geométrica perante três estratégias diferentes. 2. Linux® VS Windows®: Estado da Arte Seguindo a luta actualmente existente entre estes dois sistemas operativos (SO) na conquista e reconversão de clientes, a realidade é que existem testes de comparação de performance para todos os gostos. Contudo, é de evitar análises independentes com a marca “conducted at the request of Microsoft ®” [Trezentos, 2005]. Mindcraft® é uma empresa independente que se dedica a realizar testes de comparação entre diversos softwares e hardware. Num desses estudos, utilizou-se as ferramentas de benchmark Netbench® 5.01 para analisar a performance de gestão de acesso a ficheiros em termos de servidor para n clientes (usou-se a plataforma Samba® 2.03 em termos de Linux®) e o Webbench® 2.0 para testar o servidor Web (Apache® 1.3.6 para Linux®). Em termos de hardware, o servidor usado foi o Dell ® PowerEdge 6300 com 4 processadores. Baseado nos seus testes de Junho de 1999 entre o Windows® NT Server 4.0 e o Red Hat® Linux 5.2, esta fonte claramente mostrou uma performance de superioridade em termos de servidor de ficheiros do Windows®: 2.43 mais rápido. A mesma análise se verifica quando testados os respectivos servidores Web: 2.58. A empresa VeriTest® confirma esta superioridade em termos de servidor de ficheiros quando em Junho de 2004 comparou o Windows 2003 Server ® com o Samba® 3.0 num servidor HP® DL 380. O teste indicou uma melhor performance de requests de ficheiros na ordem dos 46%. Contudo, o Linux® é reconhecido pela sua fiabilidade. Um servidor pode estar em funcionamento durante vários anos sem qualquer problema. Ao invés, todos os utilizadores de Windows® estão familiarizados com o ecrã “blue screen of death”. Além disso, é muito difícil manter o sistema sem fragmentação por um período longo dado ser um excelente devorador de recursos físicos do sistema [Bruce, Stokely, 2001]. Em termos de segurança, pouco se sabe da construção interna da plataforma Windows® pois é código fechado. No entanto, existem empresas que sofrem diariamente danos por motivos de buracos de segurança [Bruce, Stokely, 2001]. O facto do Linux® ser código aberto também constitui um problema por si só. Analise-se a falta de device drivers para alguns dispositivos físicos. Esta situação deve-se à não intenção dos fornecedores de devices em fornecer o código original provocando assim um problema ainda maior para os clientes. A mesma situação se passa com a criação de aplicações pessoais que podem compilar no Red Hat® e não no Slackware®. Claramente, a falta de aplicações comerciais (ferramentas Macromedia®, por exemplo) é muito mais grave para o Linux® do que para a plataforma Windows®. Factualmente, o famoso TCO (Total Cost of Ownership) inicial é mais baixo para o Linux® embora, no longo prazo, é uma situação que ainda requer mais estudos detalhados a nível financeiro. É importante não esquecer que a manutenção de um serviço informático é um pesadelo ainda maior que a compra deste. Actualmente, é mais dispendioso manter um administrador de Linux® do que Windows®. Por outro lado, é mais trabalhoso manter um servidor Windows ® do que Linux® [Trezentos, 2005]. Quadro 3 – Solução Linux® standard versus Microsoft® em termos de TCO. Os custos assinalados representam dólares australianos e incluem diversos serviços como antivírus, base de dados, mail server, firewall e custos de pessoal de administração [Cybersource, 2004]. Utilizando a actual infraestrutura Comprando uma nova estrutura de hardware Microsoft® $1,066,712 Linux® $682,090 % 36% $1,366,833 $1,012,260 26% É também possível encontrar benchmarks de TCO que contraria as conclusões anteriores. Por exemplo, a Bearing Point [2004] apresenta um estudo equiparado ao anterior entre o Windows® 2003 Server e o Red Hat® Enterprise Linux 3. De acordo com esta análise, os custos totais são relativamente iguais ao longo de um período de 5 anos. Utilizando um CPU AMD Athlon 64 3800+ e o famoso jogo Doom 3, testaram-se quatro placas de vídeo (ABIT GeForce 3 Vio com 64MB, ABIT GeForce 4 Ti 4600 com 128MB, NVIDIA GeForce FX 5900 Ultra com 256MB e GeForce 6800GT AGP com 256 MB) em função de dois sistemas operativos: Gentoo Linux ® e o Windows® XP SP2. De acordo com www.linuxhardware.org [2005], o Windows® consegue uma performance mais rápida que o Linux®. Usando a placa de vídeo mais sofisticada, por exemplo, a diferença do número de frames por segundo varia entre 20-36% mais rápido (naturalmente, maior definição no ecrã) e com placas de vídeo mais lentas, esta margem pode atingir 50%. Um outro facto sociológico interessante é o reaparecimento do Unix ®, um sistema operativo criado em C na AT&T ® em 1969, na forma de Linux® devido a um jovem Finlandês que se entreteu a realizar um sistema alternativo ao actual vigente Windows® e que está a tornar-se num caso muito interessante, nomeadamente em termos de servidores de base de dados, E-mail, FTP, Newsgroups e WWW. Casos como a cidade de Munique, a administração Chinesa ou a região administrativa espanhola da Extremadura, que estão a viver tempos de reconversão, poderão ser casos de alguma preocupação para o Golias Microsoft®. 3. Algoritmos de Ordenação Ordenação significa arrumar um conjunto de elementos numa determinada ordem (ascendente ou descendente). Teoricamente, os métodos de ordenação são classificados em dois grupos: A) Interna (caso o ficheiro ou estrutura a ser ordenado resida em RAM); B) Externa (caso o arquivo esteja armazenado em disco, DVD ou fita magnética, por exemplo). Os algoritmos aqui considerados apenas focam o primeiro grupo e funcionam sobre uma parte do registo designada chave (este é por sua vez um campo de uma estrutura específica). O princípio da distribuição (como a ordem de um baralho de cartas com joker, reis, damas, valetes e ases) não foi considerado. Assim, apresentamse nas seguintes subsecções os cinco métodos de ordenação que servirão de ferramenta de teste. 3.1. Heap O algoritmo Heap consiste numa árvore binária completa (cada nó tem dois e apenas dois filhos) com as seguintes propriedades específicas de ordenação: A) Todos os nós descendentes de um determinado nó N são menores ou iguais ao conteúdo de N; B) Todos os nós ascendentes de N são maiores ou iguais ao conteúdo de N; C) Consequentemente, todos os nós são menores que a raiz. Uma característica curiosa desta metodologia consiste na sua representação em vectores. Os filhos de um nó i estão nas posições 2i e 2i+1 (caso existam) e o pai de um nó i está na posição (int)i/2. Assim, por exemplo, a raiz está no índice zero do vector (o maior valor de todos os elementos) e os seus descendentes directos estão no índice 1 e 2. 3.2. Inserção Esta metodologia consiste em percorrer sequencialmente o vector a ordenar elemento por elemento, deslocando-o e inserindo-o na posição correcta. Inicialmente, são formados dois blocos: Um de valores ordenados e outro de não ordenados. Este algoritmo consiste em passar sucessivamente os valores de um bloco para o outro. Assim e caso os registos sejam inseridos sequencialmente por uma determinada aplicação, este procedimento torna-se muito útil. 3.3. Selecção Apesar de ser um algoritmo muito ineficiente para listas de valores muito extensas, este método simples de ordenação poderia ser apresentado do seguinte modo: A) Seleccionar o menor dos valores e trocá-lo por v[0]; B) Seleccionar o menor entre os restantes e trocá-lo por v[1]; C) Realizar sucessivamente os passos anteriores até todos os elementos estarem ordenados. 3.4. Quick Este algoritmo funciona do seguinte modo: Dado um vector de elementos, T[n], escolhe-se arbitrariamente um pivot x tal que todos os elementos menores que x ficam do lado esquerdo do vector enquanto os elementos maiores fiquem do lado direito. Normalmente, esse pivot costuma ser a mediana ou a média do conjunto de elementos de modo a obter uma performance equilibrada. Ao realizar-se este procedimento, a lista não fica necessariamente ordenada na primeira iteração. Em termos computacionais, portanto, o algoritmo passa pelas seguintes operações: A) Percorrer o vector T a partir da esquerda até que T[i]>=x; B) Percorrer o vector a partir da direita até que T[j]<=x; C) Trocar T[i] com T[j]; D) Continuar este processo até que os índices i e j se cruzem. Realizado estes quatro passos, o vector T[Esq...Dir] está dividido de tal forma que: A) Os valores T[Esq], T[Esq + 1],..., T[j] são menores ou iguais a x; B) Os valores T[i], T[i + 1],..., T[Dir] são maiores ou iguais a x (com i=j+1). Utilizando a estratégia da divisão e conquista, basta agora dividir a tabela dada, T[n]=T[Esq...Dir], em duas tabelas tal que T[n]=T[Esq...Dir]=T[Esq...j]+T[i...Dir]. Quando o cardinal do domínio [Esq...j] for zero ou um, então tem-se a primeira condição de paragem activa. Analogamente, quando o cardinal do domínio [i...Dir] for zero ou um, tem-se a segunda condição de paragem activa o que significa que este ramo do vector já foi ordenado. 3.5. Shell Metodologia proposta em 1959, este é uma extensão do algoritmo da ordenação de Inserção e que se resume a dar um valor de salto para o teste de inserção. Como já foi referido anteriormente, no método de Inserção existe uma troca de itens adjacentes quando se está à procura do ponto de inserção na sequência destino. No caso do Shell, a troca dos registos estão separados h posições de tal forma que o vector está reorganizado ao nível h. 4. Análise Comparativa Na escolha de um algoritmo de ordenação interna, este procedimento deve considerar o tempo de resposta da metodologia em causa como factor crítico. Sendo n o número de registos do arquivo a ordenar, as medidas de complexidade relevantes nos diversos métodos são o número de comparações entre chaves, C(n), e o número de movimentações de itens dentro do arquivo, M(n). Claramente, o uso económico da memória disponível é também um requisito primordial na ordenação interna. Esta secção pretende relacionar e comparar os métodos de ordenação anteriormente referidos em vectores de diferentes dimensões e em função de dois sistemas operativos: Windows® XP Professional SP2 e Linux® SuSex 10. Considerase que ambos os sistemas operativos têm apenas os serviços mínimos para estes estarem operacionais. Mais, o computador (Pentium III a 1GHz com 1GB de RAM) deste benchmark é o rigorosamente o mesmo (a memória livre rondava 630MB em ambos os SO). A mesma situação acontece com o código C dos cincos algoritmos. Para facilitar a análise, todos os vectores considerados estavam ordenados descendentemente (o que significa que foi apenas simulado uma só vez para cada dimensão do vector a ordenar). Pretende-se uma ordenação ascendente. A compilação dos mesmos programas foi realizada pelo Microsoft® Visual Studio .Net 2.0 e pelo GNU C/C++ 4.0.2, no caso do SuSe® 10. É ainda de referir que no código usado, não foram usadas nenhumas threads como não foi usado qualquer outro software de medida de performance. Finalmente, o tempo de resposta de cada um dos algoritmos foi calculado pela diferença do tempo de resolução através da chamada da seguinte função C: Com base na figura 4, apresentam-se os seguintes comentários: Para todas as formas de algoritmos de ordenação considerados, o seu tempo de resolução é geralmente menor para o ambiente Linux®. Contudo, esta regra é fatalmente quebrada quando o número de registos a ordenar atinge a ordem dos 300 milhões (Shellsort e Heapsort), 400 milhões (Quicksort) e 1 milhão (Seleção). No método de Shellsort, Heapsort, Quicksort e Seleção, a diferença do tempo de resposta entre o Linux® e o Windows® aumenta directamente com a quantidade de input considerada. Inexplicavelmente, este padrão não se verifica quando o vector a ordenar atinge as maiores dimensões consideradas neste estudo. Considerando o método de Inserção, a plataforma Windows® consegue superar a performance do SuSe Linux® numa proporcionalidade que atinge 4.5 vezes. Esta proporcionalidade desce para 2 (Quicksort), 1.97 (Heapsort) e 1.8 (Shellsort). Contrariando a conclusão anterior, o SuSe Linux® apresenta um tempo de resposta inferior com a ordenação de Seleção, independentemente da dimensão do vector a ordenar. De facto, a proporcionalidade referida no ponto anterior inverte para 3.86 vezes mais lento (Windows®). Analisando a imagem seguinte, verifica-se uma tendência de um comportamento linear positivo no tempo de resposta dos algoritmos, em geral. Performance (métodos de ordenação) 300 ShellWindow s 200 150 100 Segundos 250 50 ShellLinux QuickWindow s QuickLinux HeapWindow s HeapLinux 0 10 30 50 60 100 200 Dim ensão do vector a ordenar (m ilhões) Figura 2 – Análise de comparação de resultados dos cinco algoritmos de ordenação para dimensões inferiores a 200 milhões de registos. É ainda de notar diferenças substanciais entre os diversos métodos para um vector de 300 milhões de valores inteiros. Performance (métodos de ordenação) 20000 ShellWindow s Segundos 15000 ShellLinux QuickWindow s 10000 QuickLinux HeapWindow s 5000 HeapLinux 0 300 Milhões Dim ensão do vector a ordenar Figura 3 – Análise de comparação de resultados dos cinco algoritmos de ordenação para uma dimensão de 300 milhões de registos. Retirando a análise de todos os algoritmos para a respectiva maior dimensão do vector a ordenar, verificou-se um outlier no comportamento destes algoritmos. No algoritmo de Inserção, quando a dimensão do vector é de 100 mil registos, o tempo de resposta em Windows® é menor que em Linux®. Quer em Windows® XP quer em SuSe Linux®, as metodologias Quicksort e Heapsort são os algoritmos mais rápidos de ordenação, confirmando as afirmações comuns da literatura especializada. Esta diferença de performance com os restantes métodos verifica-se, nomeadamente, para vectores com médias e grandes dimensões. Os métodos com pior performance, independentemente do SO, são os métodos de Inserção e Seleção. Figura 4 – Comparação de tempos de resposta das cinco metodologias de ordenação. 5. Conclusão Nesta análise prática sobre a performance de algoritmos de ordenação em função do sistema operativo e dos respectivos compiladores, este artigo não pretende indicar qual o melhor sistema operativo na sua globalidade. Como já foi referido anteriormente e embora estes sejam concorrentes directos, ambos os SO tem um historial e uma realidade bastante diferente. Mais, esta análise aqui descrita é apenas e simplesmente mais uma outra análise de bechmark a juntar a tantos outros testes já realizados por outras empresas e particulares. De qualquer modo, os autores garantem o factor de imparcialidade e honestidade nos resultados finais apresentados. Contudo e fundamentalmente, estes testes indicam claramente que considerando o tempo de resolução de uma ordenação de um vector, o mesmo código C torna-se mais rápido quando em plataforma Linux® do que em Windows® para vectores com dimensão inferior a 200 milhões (Shell, Quick e Heap). Contrariamente, a situação inverte-se completamente quando a dimensão cresce para 300 milhões. Em relação ao porquê deste comportamento, é uma questão que fica em aberto para especialistas de compiladores e sistemas operativos. Colateralmente, as metodologias de pior performance, Inserção e Seleção, apresentam piores resultados em ambiente Windows ® do que em Linux®, independentemente da dimensão considerada. Será que existe alguma dimensão superior ainda não testada que inverta este padrão? É ainda de referir a falta da medição da performance recorrendo ao uso de threads na codificação dos algoritmos em causa, um outro factor importante a considerar numa próxima análise de benchmark. Agradecimentos Pelo apoio prestado neste artigo, os presentes autores gostariam de agradecer aos alunos António Vargas, Telma Filipa e Jefra Araújo do actual 2º ano de Engenharia Informática da Universidade Lusófona. Referências Aho, A., Hopcroft, J., Ullman, J. (1983). Data Structures and Algorithms. AddisonWesley. Bearing Point. (2004). http://www.bearingpoint.com. Cybersource. (2004). http://www.cybersource.com.au/about/linux_vs_windows_tco_ comparison.pdf Garey, M., Johnson, D. (1979). Computers and Intractability A Guide to the Theory of NP-Completeness. Freeman Press. Loudon, K. (1999). Mastering Algorithms with C. O’Reilly Media, Inc. Mindcraft, 1999. www.mindcraft.com/whitepapers. Preiss, B. (2001). Estruturas de Dados e Algoritmos. Editora Campus. Trezentos, P. (2005). Linux para PCs - Caixa Mágica. FCA-Lidel. Veritest. (2004). www.veritest.com. Ziviani, N. (2004). Projecto de Algoritmos (2ª edição). Pioneira Thomson Learning.