Professional Documents
Culture Documents
RELATRIO TCNICO:
MEDIES DE DESEMPENHO CLCULO DE NMEROS PRIMOS
Andriy Mazayev (N 39004) Filipe Reis (N 39015) Florentino Bexiga (N 37032) Lus Jacinto (N 39025)
Resumo
Este trabalho foi feito no mbito da disciplina de Sistemas Paralelos e Distribudos e consiste primariamente na anlise do desempenho de uma determinada mquina atravs de algumas experincias e na anlise de um algoritmo sequencial para o clculo de nmeros primos. Este trabalho prtico est assim dividido em duas partes fundamentais: Uma primeira parte que consiste na resposta aos primeiros sete pontos de um guia [1], que tem como objectivo medir o desempenho de chamadas locais e remotas em Linux. Para cada experincia, cada ponto do guia, vamos descrever os nossos mtodos: como o nmero de repeties da operao que est sendo medida, o nmero de experincias/medies efectuadas, o resultado final, e a preciso destas medies. Uma segunda parte que consiste na implementao e anlise de um algoritmo sequencial para a determinao de nmeros primos, visto que estes nmeros so estudados, desde h muito tempo atrs, e ainda persistem como elemento de estudo para muitas pessoas devido aos seus mltiplos usos como por exemplo na implementao de algoritmos criptogrficos. Foi feita a medio do tempo de execuo do algoritmo implementado, pondo em prtica tudo o que foi aprendido na primeira parte deste trabalho.
ndice 1 INTRODUO ....................................................................................................... 6 1.1 Objectivos ........................................................................................................... 6 1.2 Motivao ........................................................................................................... 6 1.3 Metodologia ........................................................................................................ 7
4.6 Invocaes entre processos na mesma mquina (Inter-process, intra-machine invocation) .............................................................................................................. 26 4.6.1. Tempo de uma interaco pedido-resposta local ................................... 26 4.6.2. Gasto do tempo de uma interaco pedido-resposta local ....................... 28 4.7 Invocaes entre mquinas (Inter-machine invocation) ................................. 29 4.7.1. Tempo de uma interaco pedido-resposta remota ............................... 29 4.7.2. Gasto do tempo de uma interaco pedido-resposta remota ................... 30 5 SEGUNDA PARTE: DETERMINAO DE NMEROS PRIMOS ............... 32 5.1 Estudo do caso .................................................................................................. 32 5.1.1. Descrio do problema .............................................................................. 32 5.1.2. Alternativas criadas a avaliar .................................................................. 33 5.1.3. Implementaes e recolha de resultados.................................................. 33 6 CONCLUSES E SUGESTES DE TRABALHO FUTURO ......................... 37 7 REFERNCIAS .................................................................................................... 38 ANEXOS ................................................................................................................... 40
ndice de Figuras
Figura 1. Excerto da lista de todos os comandos, chamadas do sistema e bibliotecas, obtidas atravs de apropos time. ....................................................................................................................... 13 Figura 2. Exemplo de uma listagem dos endereos das variveis de um programa. .............. 22 Figura 3. Mapa de memria do processo. .......................................................................................... 22 Figura 4 - Execuo do comando size no ficheiro binrio de um programa. .............................. 23 Figura 5. Programa que percorre os endereos da pilha at devolver erro (Segmentation fault).......................................................................................................................................................... 24
ndice de Grficos
Grfico 1. Tempos de execuo de dois programas, com e sem loops, que executam a chamada ao sistema getpid 50000 vezes. .......................................................................................... 16 Grfico 2. Tempo de execuo do algoritmo para vrios tamanhos de intervalos de nmeros. .................................................................................................................................................................... 36
ndice de Tabelas
Tabela 1. Tempo de execuo da chamada getpid. .......................................................................... 15 Tabela 2. Tempos de execuo de dois programas, com e sem loops, que executam a chamada ao sistema getpid 50000 vezes. .......................................................................................... 16 Tabela 3. Medio do nmero de processos criados e do tempo de execuo, da mquina 1 (Austrlia). ............................................................................................................................................... 18 Tabela 4. Medio do nmero de processos criados e do tempo de execuo, da mquina 2. 19 Tabela 5. Tempos de execuo de fork+wait. ................................................................................... 20 Tabela 6. Tempos de execuo de fork+exec+wait. ......................................................................... 20 Tabela 7. Mapa de endereamento de um programa (comando nm). .......................................... 23 Tabela 8. Tempo de execuo de uma chamada de uma funo sem corpo e sem argumentos. .................................................................................................................................................................... 25 Tabela 9. Tempo de execuo de uma chamada de uma funo sem corpo e com 10 argumentos. ............................................................................................................................................. 26 Tabela 10. Tempo de uma interaco pedido-resposta local com tamanho de dados de 4 bytes. ......................................................................................................................................................... 27 Tabela 11. Tempo de uma interaco pedido-resposta local com tamanho de dados de 1024 bytes. ......................................................................................................................................................... 28 Tabela 12. Tempo de uma interaco pedido-resposta remota com tamanho de dados de 4 bytes. ......................................................................................................................................................... 29 Tabela 13. Tempo de uma interaco pedido-resposta remota com tamanho de dados de 10244 bytes. ............................................................................................................................................. 30 Tabela 14. Tempo de execuo da funo que cria a lista de todos os nmeros. ...................... 33 Tabela 15. Tempo de execuo da funo que marca os mltiplos de um nmero. ................. 34 Tabela 16. Tempo de execuo da funo de encontra o prximo primo mais pequeno. ......... 34 Tabela 17. Tempo de execuo da funo de mostrar os resultados encontrados. ................... 35 Tabela 18. Tempo de execuo do algoritmo para vrios tamanhos de intervalos de nmeros. ................................................................................................................................................... 35
INTRODUO
1.1 Objectivos
Este relatrio tem como objectivo geral a medio de desempenho de uma mquina. Com isto pretende-se atingir os seguintes objectivos prticos: Medir o desempenho de invocaes/chamadas locais ou remotas em Linux; Aprender tcnicas experimentais bsicas, para desenvolver a familiarizao com as caractersticas de invocao; Adquirir alguma experincia em medies de tempos de execuo e em anlise com um exemplo de um caso de estudo, o de um algoritmo sequencial que determina os nmeros primos at um limite dado; Desenvolver o esprito crtico em relao s medies e aos resultados obtidos a partir destas; Adquirir alguma experincia no desenvolvimento de relatrios tcnicos fundamentados, de maneira a mostrar todos os resultados obtidos e concluses ao realizar este trabalho prtico.
1.2 Motivao
O aumento vertiginoso ao longo dos tempos, do desempenho das mquinas de computao, ao nvel das suas arquitecturas fez com que cada vez mais fosse esquecida a parte da eficincia de um programa, ou de um algoritmo em particular. Anteriormente as mquinas que existiam eram muito limitadas ao nvel do hardware, e para isso era necessrio fazer anlises minuciosas ao desempenho de algoritmos. Esta anlise consistia em ter em ateno de que quando havia um grande nmeros de dados a processar, este poderia ser feito de maneira a minimizar operaes e assim acelerar o processo. Para isso estudava-se o desempenho dos algoritmos, para que o tempo de processamento melhorasse, de forma a tentar obter um resultado em tempo til. Contudo, verificou-se que era possvel tambm fazer com que vrias unidades de processamento trabalhassem em conjunto, ligados entre si, com o intuito de conseguir melhorar o tempo de processamento; seja atravs da partilha da mesma memria e espao de endereamento (Multiprocessadores) ou por conexes de rede de alto desempenho (Multicomputadores). Com o aparecimento de mquinas com um maior poder de computao, este problema deixou de ser to relevante, visto que permitiam o processamento de
grandes volumes de dados/informao em muito menos tempo, e sem a preocupao de analisar minuciosamente o programa. No entanto, como futuros engenheiros informticos devemos ter noo que melhorar o desempenho de um algoritmo permite aumentar a eficincia deste e da mquina em que estamos a trabalhar. Assim, com este trabalho prtico, pretende-se o estudo de como realizar adequadamente medies de desempenho e posteriormente como caso de estudo, anlise de algoritmos relacionados com a determinao de nmeros primos, de forma a ver quais os mais rpidos e eficientes.
1.3 Metodologia
Para a elaborao deste trabalho prtico foi inicialmente seguido um guia [1] que ditava algumas experincias a executar de forma a ganhar sensibilidade em como analisar tempos de execuo. Desta forma, para esta parte do trabalho, e para cada experincia que era proposta, crimos algum cdigo fonte (ou usamos o cdigo j disponibilizado) e analisamos o mesmo de forma a poder fazer medies correctamente. Aps estas medies, procurmos explicar os resultados obtidos de forma a obter maior conhecimento acerca dos factores que influenciam os tempos de execuo. Durante a realizao deste trabalho prtico tentmos ser claros e objectivos. Para este efeito abordamos os assuntos de forma directa e simples, e recorrendo a algumas referncias (pesquisa web na sua maioria) sempre que necessrio de forma a certificarmo-nos do que estaria a ser relatado e tambm de forma a aprofundar um pouco o conhecimento. Para a realizao de medies de desempenho e de tempos de execuo, utilizamos o servidor Austrlia que faz parte da Universidade do Algarve, para que assim, trabalhando numa plataforma acessvel por vrios, fosse possvel comparar e discutir os resultados obtidos. Ao longo do desenvolvimento do relatrio prtico, foram apresentados vrios grficos, figuras e quadros, com o objectivo principal de fornecer uma melhor percepo da experincia feita. A seguir a cada experincia foram demonstrados os mtodos usados, nomeadamente o nmero de repeties da operao que est a ser medida, o nmero de experincias/medies efectuadas, e qual o resultado final, seguido da preciso destas medies. A segunda parte do trabalho, que consistiu na anlise de algoritmos sequenciais para o clculo de nmeros primos, centrou-se em pesquisar e analisar algoritmos j existentes para o efeito, com o objectivo principal de obter o algoritmo mais
eficiente e rpido, mas tambm analisando a possibilidade da paralelizao destes algoritmos de forma a incentivar um trabalho futuro.
Um segundo captulo de enquadramento, onde so introduzidos conceitos fundamentais para a leitura deste relatrio prtico, e que permita ao leitor
desenvolver competncias para compreender todo o contedo presente no mesmo. Este captulo est dividido em vrios pontos, cada um referente a uma parte diferente da teoria necessria. Um terceiro captulo de anlise das mquinas, onde so apresentadas as caractersticas das mquinas, que foram utilizadas para correr os programas, e para medies de tempo. Um quarto captulo, que contm a primeira parte deste trabalho prtico, onde so apresentadas as respostas a vrios pontos do guia [1], que tem como principal objectivo medir o desempenho de invocaes/chamadas locais e remotas em Linux. Um quinto captulo, que contm a segunda parte deste trabalho prtico, onde se pode encontrar o estudo do caso da determinao dos nmeros primos. Este estudo ir ser feito a partir de vrios pontos: O primeiro ponto a anlise do problema, onde explicitado o caso em questo que nos lanado neste trabalho prtico, ou seja, a implementao de um algoritmo sequencial para a determinao de nmeros primos. O segundo ponto consiste nas alternativas criadas a avaliar, onde so explicitadas algumas das alternativas existentes para a determinao de nmeros primos e dizer qual destas obtm maior desempenho. O terceiro ponto consiste nas implementaes e recolha de resultados, onde mostrada a implementao do algoritmo que determina os nmeros primos e onde mostrado o resultado da anlise do desempenho a este algoritmo. Iro ser tambm apresentados grficos que permitem demonstrar os resultados da anlise de desempenho do algoritmo de uma forma muito mais clara. Um sexto captulo de concluses e sugestes de trabalho futuro, onde so apresentados alguns comentrios finais a este trabalho prtico, e onde discutido o resultado da aprendizagem e se foram deixadas algumas questes em aberto, para que num trabalho futuro sejam respondidas e abordadas. Um ltimo captulo de referncias, onde so apresentadas todas as referncias que foram utilizadas para o desenvolvimento deste trabalho prtico. Por fim, vo estar inseridos alguns anexos, como os algoritmos utilizados no estudo do caso, e outros objectos necessrios que complementem alguma parte do que foi falado neste relatrio.
ENQUADRAMENTO
10
N criado atravs do produto de dois outros nmeros primos. Este nmero N ento usado como chave de encriptao de uma mensagem. Para desencriptar esta mensagem seria necessrio descobrir os nmeros primos que compem N, mas esta tarefa bastante complicada o que resulta na eficcia do algoritmo RSA. Este tipo de propriedades d origem a que seja possvel criar algoritmos bastante complexos para proteco de dados, que um dos problemas mais comuns na rea da informtica. Como tal, interessante estudar algoritmos capazes de produzir rapidamente nmeros primos [7].
11
Durante a realizao deste trabalho prtico foram utilizadas vrias mquinas para a realizao das medies de tempo e para outros testes, com as seguintes caractersticas:
Mquina 1 (Austrlia): Processador (modelo): AMD Athlon 64 Processor 3000+ Frequncia do Processador: 1809.275 MHz Cache: 512 KB Memria RAM: 2 GB Sistema Operativo: Debian Linux 3.1 Kernel: 2.4.33.2 Compilador: gcc 3.3.5
Mquina 2 (Mquina Virtual Debian): Processador (modelo): Intel Core 2 Duo CPU P8700 Frequncia do Processador: 2526.387 MHz Cache: 3072 KB Memria RAM: 1 GB Sistema Operativo: Debian Linux 6.0 Kernel: 2.6.35 Compilador: gcc 4.4.5
12
Esta primeira parte do trabalho prtico, consiste na resposta a vrios pontos do guia indicado em [1], que tem como objectivo principal medir o desempenho de invocaes/chamadas locais e remotas num ambiente Unix, e ao mesmo tempo aprender tcnicas experimentais bsicas e a familiarizao com as caractersticas de invocao. Para cada uma destas experincias, iremos descrever os seus mtodos: o nmero de repeties da operao que est a ser medida, o nmero de experincias/medies efectuadas, qual o resultado final, e preciso destas medies.
Figura 1. Excerto da lista de todos os comandos, chamadas do sistema e bibliotecas, obtidas atravs de apropos time.
13
Analisamos em particular a funo gettimeofday, a partir do comando man 2 gettimeofday, e retirmos as seguintes informaes: Prottipo da funo: #include <sys/time.h> int gettimeofday (struct timeval *tv, struct timezone *tz) O que faz: Atribui a uma estrutura timeval o tempo decorrido em segundos e microsegundos desde 1 de Janeiro de 1970 (UTC). Retorna 0 em caso de sucesso ou -1 caso contrrio. Tipos de erros: -EFAULT: os ponteiros tv ou tz so invlidos; -EINVAL: valor no timezone, ou algum outro valor invlido; Podemos assumir que esta funo tem pelo menos a preciso de 0.01s uma vez que a resoluo do relgio do sistema de 100Hz.
4.2 Medio do tempo de chamada do sistema (Measuring System Call Time) 4.2.1 . Uso da funo gettimeofday para medies de tempos
Para medir o tempo gasto para realizar uma chamada ao sistema necessrio utilizar a funo gettimeofday, que fornece o tempo decorrido. A chamada desta funo deve ser feita antes e depois de executar a chamada, e assim conseguimos encontrar o tempo de execuo. A velocidade de execuo de uma chamada ao sistema muito inferior a 0,01 segundo, logo o seu tempo no pode ser medido em segundos. No entanto fazendo uma pequena alterao na linha abaixo apresentada: return ( (double)(time.tv_sec*1000000+time.tv_usec)/1000000 ); para return ( (double)(time.tv_sec*1000000+time.tv_usec)/1000 ); Obtemos o tempo em milissegundos e desta forma podemos observar o tempo que a chamada ao sistema demora a ser executada, usando os especificadores de converso disponveis com a funo printf para apresentar o resultado com a preciso que desejada. A funo gettimeofday retorna uma estrutura que guarda em si, os segundos e microssegundos passados deste a inicializao do sistema. A operao acima apresentada, transforma os segundos em microssegundos de maneira a que se
14
possa incluir os microssegundos e assim aumentar a preciso, finalizando com uma passagem de volta para segundos ou neste caso milissegundos como era pretendido. Para uma nica chamada ao sistema getpid, esta preciso no chega, pelo que para este efeito necessrio usar um nmero superior de casas decimais ou usar microssegundos em vez de milissegundos que foi a opo tomada. Esta opo no entanto tem algum erro de clculo uma vez que a chamada ao sistema muito rpida e para ser medida com exactido, a medida de milissegundos no chega. Assim o tempo de uma chamada getpid : N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Tempo (s) 3,00 3,00 3,00 2,00 2,00 2,00 2,00 3,00 2,00 2,00 2,40 5,16 x 10-1
Tambm seria possvel criar um ciclo de repetio para repetir vrias vezes a chamada, e por fim dividir o tempo resultante pelo nmero de repeties, mas este processo tambm traria algum erro de clculo, derivado do uso do ciclo de repetio.
15
Aps execuo de ambos programas obtivemos os seguintes dados: N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Com loops (ms) 8,17 8,16 8,18 8,16 8,16 8,17 8,16 8,17 8,20 8,18 8,17 1,29 x 10-2 Sem loops (ms) 8,17 8,15 8,16 8,15 8,16 8,15 8,17 8,15 8,16 8,14 8,16 9,66 x 10-3
Tabela 2. Tempos de execuo de dois programas, com e sem loops, que executam a chamada ao sistema getpid 50000 vezes.
8,21 8,20 8,19 8,18 8,17 8,16 8,15 8,14 8,13 0 2 4 6 8 10 12 Com Loop Sem Loop
Grfico 1. Tempos de execuo de dois programas, com e sem loops, que executam a chamada ao sistema getpid 50000 vezes.
A partir dos dados do Grfico 1 podemos concluir, que o programa que usa ciclos de repetio mostra ser ligeiramente mais lento que o programa que no usa. O parmetro funroll-loops aumenta assim a performance do programa, pois optimiza os registos e os dados de cache [8].
16
4.3 Medio do Tempo de Criao de um Processo (Measuring Process Creation Time) 4.3.1 . Nmero mximo de processos permitidos
Para criar um novo processo num sistema Unix, usa-se a chamada do sistema fork. Esta chamada retorna o PID do processo filho no processo pai e retorna 0 no processo filho. Em caso de erro, retornado -1 ao processo pai. Para tentar determinar o nmero mximo de processos permitidos por utilizador, foi criado um pequeno programa que chama infinitamente a chamada fork at retornar erro. Aps a chamada fork, o processo pai ir aguardar que os filhos terminem a sua execuo atravs da chamada wait, enquanto os filhos apenas iro apenas terminar os seus processos.
17
Mquina 1 (Austrlia) N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Tempo (s) 12,58 12,60 12,58 12,60 12,59 12,60 12,57 12,61 12,56 12,60 12,59 1,60 x 10-2 N Processos 14261 14261 14261 14261 14261 14261 14261 14261 14261 14261 14261 0
O nmero mximo de processos criados desta forma foi 14261. No entanto h pormenores a ter em conta. Um deles que este valor no o nmero mximo de processos permitidos pelo sistema pois existem vrios processos a correr no background por exemplo. Outro pormenor a ter em conta que este valor no absoluto, ou seja, varia de mquina para mquina. Executando o comando ulimit -a podemos observar que o nmero de processos permitido pela mquina ilimitado, mas no entanto condicionado pelos recursos disponveis, como por exemplo a memria RAM. Para demonstrar esta ideia realizou-se uma pequena experincia na Mquina 2. A experincia consistiu em executar o mesmo programa duas vezes na mquina, uma das vezes configurando a mquina para usar 512 MB de memria RAM e outra das vezes usando 1GB de RAM, sendo os resultados os seguintes:
18
Mquina 2 (512 MB) N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Tempo (s) 20,93 4,80 4,09 4,08 4,37 4,00 4,24 4,17 4,69 4,19 4,22 5,27 N Processos 7673 7673 7673 7673 7673 7673 7673 7673 7673 7673 7673 0
Mquina 2 (1024 MB) Tempo (s) 15,90 15,92 15,88 16,52 19,66 15,58 16,21 15,58 16,28 16,15 16,04 1,19 N Processos 15805 15805 15805 15804 15804 15804 15805 15805 15805 15805 15805 4,83 x 10-1
Desta forma pode-se observar que o nmero mximo de processos permitidos por utilizador est directamente relacionado com os recursos da mquina.
4.3.2 . Tempo de execuo das instrues fork + wait e fork + exec + wait
Interessa tambm descobrir qual o tempo de uma nica chamada fork em conjunto com a chamada wait. Para este efeito, realizamos uma nova experincia em que verificamos o tempo de uma nica chamada fork, onde o processo filho mais uma vez apenas iria terminar o processo. De notar que nesta experincia o tempo de execuo muito curto, e como tal, foi necessrio usar microssegundos.
19
Tempo de Execuo N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Fork+Wait (ms) 0,09 1,01 1,02 1,01 1,01 1,02 1,01 1,01 1,03 1,01 1,01 0,29
Outra maneira de realizar a experiencia anterior consiste em: no lugar de criar um processo filho que se limita a terminar o processo, criar um processo filho que ir usar a chamada ao sistema execv para chamar um novo programa (vamos chamarlhe fred) que se limita a terminar a sua execuo. A tabela seguinte mostra os tempos obtidos com esta experincia:
Tempo de Execuo N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Fork+Exec+Wait (ms) 5,35 2,11 2,12 2,11 2,13 2,11 2,11 2,11 2,11 2,11 2,11 1,02
20
21
Os endereos retornados pelo output so os endereos onde as variveis usadas, e a funo main, se encontram na memria aquando de uma execuo do programa. Alguns dos endereos encontram-se mais prximos de uns do que de outros. Isto ocorre devido s diferentes zonas da memria de um processo. Para melhor percepo acerca destas zonas de memria, atentemos na figura que se segue:
22
Como podemos ver pelo mapa de memria, a funo main fica alojada no bloco Text, as variveis definidas e inicializadas globalmente ficam registadas no segmento Data, por sua vez as variveis definidas globalmente mas no inicializadas so guardadas no BSS (Block Started by Symbol). Na stack ficam guardadas as variveis locais tal como o apontador cp e a varivel aCharacter. A zona entre a stack e o heap consiste na memria que pode ser alocada posteriormente, aumentando assim o tamanho da respectiva zona de memria. Ao executar o comando nm address --format=sysv podemos com clareza todos os elementos do programa. No entanto para o nosso caso apenas vamos considerar as variveis e a prpria funo obtendo-se assim a seguinte tabela.
Class d b d T
Como podemos ver, os dados devolvidos pelo comando correspondem ao mapa de memria anteriormente apresentado. Os comandos de mapeamento de memria no apresentam no entanto os segmentos stack e heap. Podemos ainda usar o comando size x no ficheiro binrio do programa que permite mostrar o tamanho que o programa ocupa em cada uma dessas zonas de memria.
23
Figura 5. Programa que percorre os endereos da pilha at devolver erro (Segmentation fault).
Uma vez que o programa terminou com erro aps aceder o endereo 0xbffffb33, podemos assumir que este endereo o endereo do topo da pilha de execuo, ou seja o endereo de topo, da memria que foi alocada na zona da stack.
24
Tabela 8. Tempo de execuo de uma chamada de uma funo sem corpo e sem argumentos.
25
Tabela 9. Tempo de execuo de uma chamada de uma funo sem corpo e com 10 argumentos.
O que podemos ver pelos resultados, que estes so relativamente idnticos, o que nos leva a querer que no existe diferena no tempo entre no usar ou usar argumentos nas funes. No entanto, no se pode dizer isto com certeza, pois a preciso do gettimeofday pode no ser a melhor e estas so operaes demasiadamente rpidas.
Os resultados apresentados incluem o tempo em microssegundos para todo o ciclo de interaces e para uma nica interaco (ao dividir o tempo todo pelo nmero de repeties), que neste caso seria o pretendido. Foram apenas consideradas medies na aplicao cliente uma vez que a aplicao inicia e termina a comunicao no cliente. Inicialmente usamos o tamanho de dados como sendo 4 bytes: N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Tempo (s) 7672,00 7943,00 8201,00 8262,00 8399,00 8271,00 7874,00 8055,00 8353,00 8279,00 8231,50 2,36 x 10+02 Tempo per rpc (s) 7,00 7,00 8,00 8,00 8,00 8,00 7,00 7,00 8,00 8,00 8,00 5,16 x 10-01
Tabela 10. Tempo de uma interaco pedido-resposta local com tamanho de dados de 4 bytes.
27
Tempo (s) 9017,00 9136,00 9172,00 9434,00 8644,00 9351,00 9197,00 8953,00 8878,00 9065,00 9100,50 2,30 x 10+2
Tempo per rpc (s) 8,00 8,00 8,00 9,00 8,00 9,00 8,00 8,00 8,00 8,00 8,00 4,22 x 10-1
Tabela 11. Tempo de uma interaco pedido-resposta local com tamanho de dados de 1024 bytes.
Para obter um valor nico, fizemos uma mdia aritmtica uma vez que a preciso destes valores est dependente da preciso da funo gettimeofday, que como foi referido anteriormente, no mnimo tem uma preciso de 0.01s derivado da resoluo do relgio do sistema ser 100Hz. Como podemos observar, o tempo ligeiramente diferente usando diferentes tamanhos de dados. Este comportamento era esperado pois uma vez que como ambos os processos esto a correr na mesma mquina, a comunicao bastante rpida.
28
4.7 Invocaes entre mquinas (Inter-machine invocation) 4.7.1 . Tempo de uma interaco pedido-resposta remota
Em semelhana experincia anterior, vamos usar uma aplicao cliente e uma aplicao servidor para efectuar comunicao entre elas. No entanto usamos desta vez duas mquinas diferentes conectadas por uma rede local de Mbps. A Mquina 1 (Austrlia) ir servir de servidor, enquanto que a Mquina 2 ir ser o cliente. As medies sero feitas tal como anteriormente e os resultados apresentados microssegundos representam os tempos de execuo de um ciclo de 1024 interaces e de uma nica interaco. Primeiro efectuamos a experincia usando um tamanho de dados de 4 bytes: N De Medies 1 2 3 4 5 6 7 8 9 10 Mdia Desvio Padro Tempo (s) 954173,00 946518,00 949699,00 946773,00 846193,00 956107,00 948153,00 948185,00 949144,00 949852,00 948664,50 3,29 x10+04 Tempo per rpc (s) 931,00 924,00 927,00 924,00 826,00 933,00 925,00 925,00 926,00 927,00 925,50 3,20 x 10+01
Tabela 12. Tempo de uma interaco pedido-resposta remota com tamanho de dados de 4 bytes.
29
Usando seguidamente, um tamanho de dados de 1024 bytes: Tempo (ms) 2235088,00 1283488,00 2174512,00 2178753,00 2155940,00 2159427,00 2158479,00 2160913,00 2168344,00 2148887,00 2160170,00 2,82 x 10+5 Tempo per rpc (ms) 2182,00 2132,00 1213,00 2127,00 2105,00 2108,00 2107,00 2110,00 2117,00 2098,00 2109,00 2,88 x 10+2
Tabela 13. Tempo de uma interaco pedido-resposta remota com tamanho de dados de 10244 bytes.
Para obter um valor nico, mais uma vez, fizemos uma mdia aritmtica que como foi referido anteriormente, est dependente da preciso da funo gettimeofday. Os tempos obtidos desta vez so muito mais distantes do que anteriormente, o que seria de esperar devido a ser usado comunicao na rede entre mquinas diferentes, comunicao esta que est muito sujeita a deteriorao.
30
banda o que resulta em: 2*(64*8)/10000000 = 0.0001024 segundos ou 102.4 microssegundos. Para um tamanho de dados de 1024 bytes temos: 2*((1024+20+18+8)*8)/10000000 = 0.001712 segundos ou 1712 microssegundos. Nesta experincia, contrariamente anterior, foram usadas duas mquinas diferentes para correr os processos cliente e servidor. Estas mquinas encontravam-se ligadas por uma rede local de 10 Mbps. Desta forma, uma vez que no temos os processos na mesma mquina, a comunicao j est sujeita a interferncias e ao volume do trfego da rede o que provoca latncia. Ao observar os resultados obtidos, possvel verificar que contrariamente experincia anterior (onde a diferena nos era mnima), desta vez a diferena temporal entre tamanhos de dados um pouco maior que o dobro. Seria esperado um maior tempo uma vez que a comunicao remota, e como tal no pode ser imediata, no entanto ao observar a grande diferena entre os tamanhos de dados, e observando a grande diferena entre os resultados experimentais e os resultados calculados, pode-se aferir que neste caso a comunicao j no feita de forma to eficaz devido a vrios factores como por exemplo: a computao do checksum de cada pacote, a deteco de erros das camadas de rede e de ligao de dados, entre outras causas de delay.
31
32
Tabela 14. Tempo de execuo da funo que cria a lista de todos os nmeros.
33
Tempo (ms) 1,50E-05 1,40E-05 1,50E-05 1,50E-05 1,50E-05 1,50E-05 1,50E-05 1,50E-05 1,50E-05 1,50E-05 1,50E-05 3,16E-07
Tabela 16. Tempo de execuo da funo de encontra o prximo primo mais pequeno.
34
Tempo (s) 3,88E-04 3,89E-04 3,88E-04 3,88E-04 3,70E-04 3,70E-04 3,89E-04 3,88E-04 3,89E-04 3,70E-04 3,88E-04 8,91E-06
Finalmente testamos o nosso algoritmo para vrios tamanhos de intervalos, sempre comeando no nmero 2 at um dado n.
Tabela 18. Tempo de execuo do algoritmo para vrios tamanhos de intervalos de nmeros.
35
Eratstenes
90,000000 80,000000 70,000000
60,000000
50,000000 40,000000 30,000000 20,000000 10,000000 0,000000 1000 10000 100000 1000000 10000000
Como podemos ver pelo grfico acima, este algoritmo aumenta rapidamente conforme o tamanho do input. Existem alguns melhoramentos que poderiam ser feitos de forma a aumentar a rapidez deste. Poderiam ter sido usadas listas, e alguns passos do algoritmo poderiam ter sido feitos em conjunto, mas para clareza do cdigo resolvemos criar o algoritmo desta forma.
36
Com este trabalho prtico foi possvel experimentar e aprofundar diversos conhecimentos nas reas de Sistemas Operativos e Sistemas Paralelos e Distribudos. Num panorama mais geral, este trabalho permitiu-nos aprender como realizar adequadamente um relatrio tcnico, assim como ajudou-nos a adquirir esprito crtico em relao a anlises de desempenho de mquinas e/ou algoritmos. Tivemos oportunidade de experimentar tcnicas de anlise de cdigo e de tempos de execuo de forma a determinar se um algoritmo ptimo ou no e assim estudar a progresso destes tempos de execuo em funo do tamanho do problema. Numa componente mais tcnica, este trabalho comeou pelo seguimento de um guia que nos permitiu estudar mais a fundo algumas caractersticas dos Sistemas Operativos. Nomeadamente falando, permitiu-nos analisar caractersticas de invocaes e chamadas de sistema, assim como estudar a acerca da memria de um processo e acerca de comunicao entre processos (sejam estes locais ou remotos). Permitiu tambm estudar vrios algoritmos de gerao de nmeros primos, a fim de determinar eficincia e eventualmente determinar qual o melhor. Neste trabalho em particular foram analisados dois algoritmos: o Crivo de Atkin e o Crivo de Eratstenes. Atravs das experincias realizados e do estudo seguido a estas, determinamos que o Crivo de Atkin o algoritmo sequencial, dentro dos algoritmos mais acessveis em termos de complexidade, mais rpido. No entanto, de relevar que este algoritmo, da forma que se encontra desenhado, no facilita a paralelizao no caso de eventualmente se desejar criar uma verso do algoritmo para correr num Sistema Paralelo. Para o efeito de paralelizao, o Crivo de Eratstenes o mais adequado devido sua simplicidade, e como tal, pode ser futuro objecto de estudo a fim de criar estas implementaes. De modo final, conclui-se que este trabalho prtico permitiu o aprofundamento de novos contedos para alm do desenvolvimento de caractersticas de organizao e anlise, caractersticas estas, que para um engenheiro, se assumem fundamentais.
37
REFERNCIAS
[1] Kindberg, T. (s.d.). Project 5: Operating Systems Exercicses. Obtido em 21 de Outubro de 2011, de http://www.cdk4.net/instructors/projects/Project5/OSexperiments.html [2] NUNES, L. (2008). Regras para elaborao de relatrios tcnicos e cientficos. Faro. [3] LOVSZ, L., PELIKN, J., & VESZTERGOMBI, K. (2003). Discrete Mathematics: Elementary and Beyond (1 Edio ed.). Springer. [4] Gimenes, L. P. (s.d.). Obtido em 21 de Outubro de 2011, de http://www.dma.uem.br/kit/arquivos/arquivos_pdf/teoremafundamental.pdf [5] Pombo, O. (s.d.). Teoria dos Nmeros. Obtido em 21 de Outubro de 2011, de http://www.educ.fc.ul.pt/docentes/opombo/seminario/fermat/teoria_dos_numeros.htm [6] SILVA, V., & VIEIRA, A. (2006). UFMG. Obtido em 24 de Outubro de 2011, de Nmeros Inteiro: Divisibilidade, Primos, MDC e MMC: http://www.mat.ufmg.br/olimpiada/cursos/InteirosApostila.pdf [7]. (s.d.). Clay Mathematics Institute: Dedicated to increasing and disseminating mathematical knowledge. Obtido em 26 de Outubro de 2011, de Prime numbers and cryptography: http://www.claymath.org/posters/primes/ [8] DAVID F. BACON, S. L. (1994). Compiler Transformations for High-Performance Computing. ACM Computing Surveys, 26, p. 24. [9] SILBERCHATZ, A., GALVIN, P. B., & GAGNE, G. (2005). Operating System Concepts (7 Edio ed.). Wiley. [10] LINDEN, P. (1994). Expert C Programming: Deep C Secrets. Prentice Hall. [11] VV. AA. (s.d.). Sieve of Eratosthenes. Obtido em 20 de Outubro de 2011, de Rosetta Code: http://rosettacode.org/wiki/Sieve_of_Eratosthenes#Java [12] LIGON, W., & STANZIONE, D. (2003). Sieve of Eratosthenes. Obtido em 20 de Outubro de 2011, de http://www.parl.clemson.edu/~walt/ece493/ece493-4.pdf *13+ ONEILL, M. (s.d.). The Genuine Sieve of Eratosthenes. Under consideration for publication in J. Functional Programming, p. 12. [14] Charles. (9 de Setembro de 2011). Understanding the Sieve of Atkin. Obtido em 20 de Outubro de 2011, de Mathematics: http://math.stackexchange.com/questions/59041/understanding-the-sieve-of-atkin
38
[15] Tharis. (s.d.). Sieve of Atkin - Crivo de Atkin. Obtido em 20 de Outubro de 2011, de Portugal-a-Programar: http://www.portugal-a-programar.org/forum/index.php?topic=26457.0 [16] Svish. (19 de Outubro de 2009). The Sieve of Atkin in C#. Obtido em 20 de Outubro de 2011, de Geekality: http://www.geekality.net/2009/10/19/the-sieve-of-atkin-in-c/ [17] QUINN, M. (2003). Parallel Programming in C with MPI and OpenMP. McGraw,Hill Education. [18] TANENBAUM, A. (2003). Computer Networks (4 ed.). New Jersey: Pearson Education, Inc. [19] STALLINGS, W. (1993). Computer Organization and Architecture: Principles of Structure and Fuction (3 ed.). Canada: Macmillan Publising Company. [20] KNUTH, D. (2000). The Art of Programming: Volume 1 - Fundamental Algorithms (3 ed.). Estados Unidos da America: Addison-Wesley.
39
ANEXOS
Programas utilizados na realizao do trabalho prtico
2.1.
#include #include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h> <errno.h>
double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1 ); } int main(){ double elapsed, rtc(); elapsed=rtc(); /* ------ */ getpid(); /* ------ */ elapsed=rtc()-elapsed; printf( " exit (0); } Elapsed time: Loop time = %.2lf \n", elapsed );
2.2.
#include #include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h> <errno.h>
40
} int main(){ int i, N = 50000; double elapsed, rtc(); elapsed=rtc(); for(i=0; i<N; i++){ /* ------ */ getpid(); /* ------ */ } elapsed=rtc()-elapsed; printf( " exit (0); } Elapsed time: Loop time = %.2lf \n", elapsed );
3.1.
#include #include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h> <errno.h>
double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1000000 ); } int main(){ int value=0, count=0; double elapsed, rtc(); elapsed=rtc(); /* ------ */ while(value != -1){ value = fork(); if(!value) exit(0); count++; } wait(NULL);
41
/* ------ */ elapsed=rtc()-elapsed; printf( " printf( " exit (0); } Elapsed time: Loop time = %.2lf Number of processes created: %d \n", \n", elapsed ); count );
3.21.
#include #include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h> <errno.h>
double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1000 ); } int main(){ int value=0; double elapsed, rtc(); elapsed=rtc(); /* ------ */ value = fork(); if(!value) exit(0); wait(NULL); /* ------ */ elapsed=rtc()-elapsed; printf( " exit (0); } Elapsed time: Loop time = %.2lf \n", elapsed );
3.22.
#include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h>
42
#include <errno.h> double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1000 ); } int main(int argc, char **argv){ int value=0; double elapsed, rtc(); elapsed=rtc(); /* ------ */ value = fork(); if(!value) execv("fred", argv); wait(NULL); /* ------ */ elapsed=rtc()-elapsed; printf( " exit (0); } Elapsed time: Loop time = %.2lf \n", elapsed );
4.1.
#include <stdio.h> static char *aStaticString = "fred"; static int aStaticDataItem; static int anInitialisedStaticDataItem = 0x666; int main(int argc, char *argv[]) { unsigned char *cp; unsigned char aCharacter; cp = &aCharacter; printf("address of printf("address of printf("address of printf("address of printf("address of return 0; }
character on stack = 0x%x\n", cp); initialised string = 0x%x\n", aStaticString ); uninitialised int = 0x%x\n", &aStaticDataItem); initialised int = 0x%x\n", &anInitialisedStaticDataItem); procedure main = 0x%x\n", main);
4.2.
#include <stdio.h> #define PAGESIZE 4096
43
static char *aStaticString = "fred"; static int aStaticDataItem; static int anInitialisedStaticDataItem = 0x666; int main(int argc, char *argv[]) { unsigned char *cp; unsigned char aCharacter; cp = &aCharacter; printf("address of printf("address of printf("address of printf("address of printf("address of
character on stack = 0x%x\n", cp); initialised string = 0x%x\n", aStaticString ); uninitialised int = 0x%x\n", &aStaticDataItem); initialised int = 0x%x\n", &anInitialisedStaticDataItem); procedure main = 0x%x\n", main);
4.3.
#include <stdio.h> #define PAGESIZE 4096 static char *aStaticString = "fred"; static int aStaticDataItem; static int anInitialisedStaticDataItem = 0x666; void recur_proc_call(unsigned char *cp){ unsigned char aCharacter = *cp; printf("made it to 0x%x!\n", cp); cp -= PAGESIZE; recur_proc_call(cp); } int main(int argc, char *argv[]) { unsigned char *cp; unsigned char aCharacter; cp = &aCharacter; printf("address of printf("address of printf("address of printf("address of printf("address of
character on stack = 0x%x\n", cp); initialised string = 0x%x\n", aStaticString ); uninitialised int = 0x%x\n", &aStaticDataItem); initialised int = 0x%x\n", &anInitialisedStaticDataItem); procedure main = 0x%x\n", main);
recur_proc_call(&aCharacter); return 0; }
44
5.1.
#include #include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h> <errno.h>
void noarguments(void){ } double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1 ); } int main(int argc, char **argv){ double elapsed, rtc(); elapsed=rtc(); /* ------ */ noarguments(); /* ------ */ elapsed=rtc()-elapsed; printf( " exit (0); } Elapsed time: Loop time = %.2lf \n", elapsed );
5.2.
#include #include #include #include #include #include #include #include #include <stdlib.h> <stdio.h> <sys/time.h> <time.h> <math.h> <unistd.h> <sys/types.h> <sys/wait.h> <errno.h>
void tenarguments(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10){ } double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1 ); } int main(int argc, char **argv){
45
double elapsed, rtc(); elapsed=rtc(); /* ------ */ tenarguments(1,1,1,1,1,1,1,1,1,1); /* ------ */ elapsed=rtc()-elapsed; printf( " exit (0); } Elapsed time: Loop time = %.2lf \n", elapsed );
Cliserv_rpc_cdk4.
#include #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <sys/time.h> <sys/types.h> <sys/socket.h> <netdb.h> <netinet/in.h>
struct timeval start, end; struct hostent *gethostbyname() ; void client(char *host, int port, int datasize); void server(int port, int datasize); void printSA(struct sockaddr_in fred); void makeDestSA(struct sockaddr_in *sa, char *hostname, int port); void makeLocalSA(struct sockaddr_in *sa); long time_diff(struct timeval *tp2, struct timeval *tp1); #define NTIMES 1024
/** * Act as a client or server, exchanging UDP messages */ int main(int argc, char **argv) { int port, datasize, error; port = 8000 /*getuid() + IPPORT_RESERVED*/; datasize = 0; error = -1; //gettimeofday(&start, NULL); switch(argc) { case 3: if(argv[1][0] == 's') { sscanf(argv[2], "%d", &datasize); if(datasize > 0) { server(port, datasize); error = 0; }
46
} break; case 4: if(argv[1][0] == 'c') { sscanf(argv[3], "%d", &datasize); if(datasize > 0) { client(argv[2], port, datasize); error = 0; } } break; default: break; } if(error) { printf("Usage: %s c/lient machine datasize (> 0) or\n", argv[0]); printf("%s s/erver datasize (>0)\n", argv[0]); } /*else { gettimeofday(&end, NULL); printf("time in microseconds = %ld\n", time_diff(&end, &start)); printf("time in microseconds per rpc = %ld\n", time_diff(&end, &start)/NTIMES); }*/ return 0; } /*print a socket address */ void printSA(struct sockaddr_in sa) { printf("sa = %d, %s, %d\n", sa.sin_family, inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); } /* make a socket address for a destination whose machine and port are given as arguments */ void makeDestSA(struct sockaddr_in *sa, char *hostname, int port) { struct hostent *host; sa->sin_family = AF_INET; if((host = gethostbyname(hostname))== 0) { perror("Unknown host name"); exit(-1); } sa-> sin_addr = *(struct in_addr *) (host->h_addr); sa->sin_port = htons(port); } /* make a socket address using any of the addressses of this computer for a local socket on any port */ void makeLocalSA(struct sockaddr_in *sa)
47
{ sa->sin_family = AF_INET; sa->sin_port = htons(0); sa-> sin_addr.s_addr = htonl(INADDR_ANY); } static char *msg = 0; /** * Act as a server, expecting UDP messages of given size on given port */ void server(int port, int datasize) { char *msg; struct sockaddr_in mySocketAddress, aSocketAddress; int s, aLength, n, reply; register int loop; if((msg = (char *)malloc(datasize)) == 0) { perror("server: not enough memory!"); return; } if((s = socket(AF_INET, SOCK_DGRAM, 0))<0) { perror("server: socket failed"); return; } mySocketAddress.sin_family = AF_INET; mySocketAddress.sin_port = htons(port); mySocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); if( bind(s, &mySocketAddress, sizeof(struct sockaddr_in))!= 0) { perror("server: Bind failed\n"); close(s); return; } /*printSA(mySocketAddress);*/ aLength = sizeof(aSocketAddress); aSocketAddress.sin_family = AF_INET; for(loop = 0; loop < NTIMES; ++loop) { n = recvfrom(s, msg, datasize, 0, &aSocketAddress, &aLength); if(n < datasize) { perror("server recvfrom"); printf("returned %d\n", n); return; } /*printf("received %d\n", loop);*/ n = sendto(s, (char *)&reply, sizeof(reply), 0, &aSocketAddress, sizeof(struct sockaddr_in)); if(n < sizeof(reply)) { perror("server sendto"); printf("returned %d\n", n); return; }
48
} close(s); }
/** * Act as a client, repeatedly sending UDP messages of given size to machine at given port */ void client(char *machine, int port, int datasize) { int s, n, reply, aLength; register int loop; struct sockaddr_in mySocketAddress, yourSocketAddress, aSocketAddress; if((msg = (char *)malloc(datasize)) == 0) { perror("client: not enough memory!"); return; } if(( s = socket(AF_INET, SOCK_DGRAM, 0))<0) { perror("client: socket failed"); return; } makeLocalSA(&mySocketAddress); if( bind(s, &mySocketAddress, sizeof(struct sockaddr_in))!= 0) { perror("client: Bind failed\n"); close (s); return; } printSA(mySocketAddress); makeDestSA(&yourSocketAddress, machine, port); /*printSA(yourSocketAddress);*/ aLength = sizeof(aSocketAddress); /* ********************************* */ gettimeofday(&start, NULL); for(loop = 0; loop < NTIMES; ++loop) { n = sendto(s, (char *)&msg, datasize, 0, &yourSocketAddress, sizeof(struct sockaddr_in)); if(n < datasize) { perror("client sendto"); printf("returned %d\n", n); return; } n = recvfrom(s, (char *)&reply, sizeof(reply), 0, &aSocketAddress, &aLength); if(n < sizeof(reply)) { perror("client recvfrom"); printf("returned %d\n", n); return;
49
} } /* ************************************ */ gettimeofday(&end, NULL); printf("time in microseconds = %ld\n", time_diff(&end, &start)); printf("time in microseconds per rpc = %ld\n", time_diff(&end, &start)/NTIMES); close(s); } long time_diff(struct timeval *tp2, struct timeval *tp1) { long secs, microsecs; secs = (tp2->tv_sec - tp1->tv_sec); microsecs = (tp2->tv_usec - tp1->tv_usec); return secs * 1000000 + microsecs ; }
Fred.
#include <stdlib.h> int main(){ exit(0); }
Eratosthenes.
#include #include #include #include #include <stdio.h> <stdlib.h> <math.h> <time.h> <sys/time.h>
long N, NF; long *primes; long N_primes; double rtc(){ struct timeval time; gettimeofday(&time,NULL); return ( (double)(time.tv_sec*1000000+time.tv_usec)/1000000 ); } void prime_create(void){ long i; primes = (long *)calloc(NF+1, sizeof(long)); for(i=2; i<=NF; i+=1) primes[i] = i; } void prime_mark(long x){ long i; for(i=x*x; i<=NF; i+=1) if(i % x == 0 && primes[i] != 0){ primes[i] = 0;
50
if(i >= N) N_primes--; } } void prime_find(long *x){ long i, tmp = (*x); for(i=tmp+1; i<=NF; i+=1) if(primes[i] != 0){ (*x) = i; return; } } void prime_show(){ long i; printf("Number of primes found: %ld\n", N_primes); for(i=N; i<=NF; i+=1) if(primes[i] != 0) printf("%ld\t", i); printf("\n"); } int main(int argc, char **argv){ long k, N2; double elapsed; N = atol(argv[1]); NF = atol(argv[2]); N_primes = NF - N + 1; elapsed=rtc(); prime_create(); k = 2; N2 = sqrt((double)NF); do{ prime_mark(k);
prime_find(&k); } while(k <= N2); prime_show(); elapsed=rtc()-elapsed; printf("Elapsed time: = %.8lf\n", elapsed ); return 0; }
51