Experimentos com o Cache



Documentos relacionados
Experimentos com a memória cache do CPU

Sistemas Operacionais. Prof. André Y. Kusumoto

O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware

A memória é um recurso fundamental e de extrema importância para a operação de qualquer Sistema Computacional; A memória trata-se de uma grande

Memória cache. Prof. Francisco Adelton

Ministério da Educação Secretaria de Educação Profissional e Tecnológica Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Sul

Programação de Sistemas

Capacidade = 512 x 300 x x 2 x 5 = ,72 GB

CENTRAL PRCESSING UNIT

ORGANIZAÇÃO DE COMPUTADORES MÓDULO 10

ORGANIZAÇÃO DE COMPUTADORES MÓDULO 8

BACHARELADO EM SISTEMAS DE INFORMAÇÃO EaD UAB/UFSCar Sistemas de Informação - prof. Dr. Hélio Crestana Guardia

FACULDADE PITÁGORAS DISCIPLINA: ARQUITETURA DE COMPUTADORES

Sistemas Computacionais II Professor Frederico Sauer

Técnicas de Manutenção de Computadores

AULA4: PROCESSADORES. Figura 1 Processadores Intel e AMD.

Escalonamento no Linux e no Windows NT/2000/XP

Memória Cache. Prof. Leonardo Barreto Campos 1

Tais operações podem utilizar um (operações unárias) ou dois (operações binárias) valores.

ULA Sinais de Controle enviados pela UC

Exercícios de revisão V2. FAT: 300 GB / 2KB = 150MB X 8 bytes (64 bits / 8) = 1.2GB

ARQUITETURA DE COMPUTADORES


Introdução a Informática. Prof.: Roberto Franciscatto

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

Evolução dos Processadores

Bits internos e bits externos. Barramentos. Processadores Atuais. Conceitos Básicos Microprocessadores. Sumário. Introdução.

AULA 1. Informática Básica. Gustavo Leitão. Disciplina: Professor:

Sistemas Operacionais

O quê um Processador e qual a sua função?

Memórias Prof. Galvez Gonçalves

Organização e Arquitetura de Computadores I

O Hardware Dentro da Unidade do Sistema

AULA 5 Sistemas Operacionais

Manutenção de Computadores Montagem de microcomputadores: Entendendo melhor os processadores. Professor: Francisco Ary

CPU - Significado CPU. Central Processing Unit. Unidade Central de Processamento

Tecnologias de Construção de Memórias e Memórias RAM, entrelaçada e Virtual

Diminui o gargalo existente entre processador e memória principal; 5 a 10 vezes mais rápidas que a memória principal; Ligada diretamente à MP;

R S Q Tabela 17 - Tabela verdade NOR

Infraestrutura de Hardware. Memória Virtual

Processadores clock, bits, memória cachê e múltiplos núcleos

Dispositivos de Memória

INSTITUTO DE EMPREGO E FORMAÇÃO PROFISSIONAL, I.P.

Hardware Avançado. Laércio Vasconcelos Rio Branco, mar/2007

Processamento de Dados

Sistema de Computação

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira

Sistemas Operacionais

3. O NIVEL DA LINGUAGEM DE MONTAGEM

Hardware - Microprocessador

Estruturas do Sistema de Computação

Processadores. Guilherme Pontes

FUNDAMENTOS DE HARDWARE PROCESSADORES. Professor Carlos Muniz

Unidade 14: Arquiteturas CISC e RISC Prof. Daniel Caetano

Arquitetura de Rede de Computadores

Introdução a Informática. Prof.: Roberto Franciscatto

Curso de Instalação e Gestão de Redes Informáticas

discos impressora CPU memória AULA 04 - Estruturas de Sistemas Computacionais Operação dos sistemas de computação Controlador de disco

Memória RAM. A memória RAM evolui constantemente. Qual a diferença entre elas? No clock (velocidade de comunicação com o processador)

Contil Informática. Curso Técnico em Informática Processadores Core

Tipos de Dados, Tipos Abstratos de Dados Estruturas de Dados

Gerenciamento de memória virtual no Kernel Linux conceitos básicos

LÓGICA DE PROGRAMAÇÃO

Montagem e Manutenção. Luís Guilherme A. Pontes

ARQUITETURA DE COMPUTADORES

Introdução à Organização de Computadores Memória Principal

Gerenciamento de Memória

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 03 Conceitos de Hardware e Software parte 01. Cursos de Computação

Algoritmos e Programação Estruturada

Microprocessadores. Prof. Leonardo Barreto Campos 1

Fundamentos de Hardware

Gerência de processos Estudos de caso - BSD Unix

Unidade 13: Paralelismo:

Arquitetura de Computadores. Tipos de Instruções

OPERADORES E ESTRUTURAS DE CONTROLE

PROCESSADORES INTEL E AMD TABELAS E PARÂMETROS DE CONFIGURAÇÃO VCORE CLOCK FSB PINAGEM - TEMPERATURA

Algoritmos de Busca em Tabelas

29/3/2011. Primeira unidade de execução (pipe U): unidade de processamento completa, capaz de processar qualquer instrução;

Introdução aos Computadores

Backup. Permitir a recuperação de sistemas de arquivo inteiros de uma só vez. Backup é somente uma cópia idêntica de todos os dados do computador?

Sistemas Operacionais Processos e Threads

armazenamento (escrita ou gravação (write)) recuperação (leitura (read))

Sistema de Memórias de Computadores

Desempenho DESEMPENHO DE COMPUTADORES

Curso de Instalação e Gestão de Redes Informáticas

Memória Virtual. Prof. Dr. José Luís Zem Prof. Dr. Renato Kraide Soffner Prof. Ms. Rossano Pablo Pinto

Hardware de Computadores

CPU Fundamentos de Arquitetura de Computadores. Prof. Pedro Neto

MEMÓRIA. 0 e 1 únicos elementos do sistema de numeração de base 2

RISC - Reduced Instruction Set Computer

O processador é composto por: Unidade de controlo - Interpreta as instruções armazenadas; - Dá comandos a todos os elementos do sistema.

Capítulo 4. MARIE (Machine Architecture Really Intuitive and Easy)

Análises Geração RI (representação intermediária) Código Intermediário

ARQUITETURA DE COMPUTADORES

Informática Aplicada

Arquitetura e Organização de Computadores

Transcrição:

IME-USP Departamento de Ciência da Computação Experimentos com o Cache Diogo de Jesus Pina 6798294 (diogojpina@gmail.com) Disciplina: Organização de Computadores Ministrada por: Prof. Dr. Alfredo Goldman Setembro/2010

Índice Resumo...3 Abstract... 4 1. Introdução... 5 2. Testes...6 2.1. Percorrendo Matrizes... 8 2.2. Somando Vetores... 12 2.3. Memória Compartilhada entre Cores... 16 2.4. Acesso Aleatório a um Vetor... 18 3. Conclusão... 23 4. Referências Bibliográficas... 24 5. Anexos...25

Resumo Neste artigo iremos realizar experimentos com o cache para comprovar a sua importância na eficiência computacional, e como o mau uso pelos programadores pode acarretar em perda de desempenho. Palavras-chaves: cache, processador, eficiência, linguagem c.

Abstract In this article we ll experiment with the cache to prove their importance in computational efficiency, and how the misuse by developers can result in loss of performance. Keywords: cache, processor, efficiency, c language.

1. Introdução A memória cache surgiu quando percebeu-se que as memórias RAM não eram mais capazes de acompanhar a velocidade de processamento, evitando com que o processador ficasse esperando por dados. Apesar de ser uma memória muito menor se comparada a memória RAM, esta memória faz com que os programas possam ser processados mais rápidos, isso acontece pois o cache é mais rápido e o sistema operacional tenta sempre manter uma cópia do que é o mais provável de ser usado nele. O processador pode ter vários níveis de cache, os processadores comerciais atuais utilizam três camadas de cache, a primeira camada é a L1, este cache é muito rápido e capaz de acompanhar a velocidade do processador, porém ele é muito caro, por este motivo seu tamanho é pequeno; o L2, em geral, é compartilhado entre os cores e também ficam dentro do processador, esta camada é um pouco maior que a camada anterior; e o L3, em geral, fica externo ao chip e também é compartilhada entre os cores, esta camada costuma ser maior que a as demais. Existem diferentes técnicas para se utilizar a memória cache, algumas delas são: Write-Back Cache: o processador escreve dados diretamente no cache, ficando o sistema operacional responsável de escrever os dados na memória principal. Write-Throgh Cache: o sistema operacional escreve os dados tanto na memória quanto no cache, esta tecnica tem um desempenho menor, porém ela é mais simples que a anterior. Para o tratamento de Write Miss são usadas duas tecnicas: Write Allocate: O bloco de endereço é carregado seguindo-se uma ação de write hits No Write Allocate: o bloco de endereço é modificado diretamente na memória principal, não sendo carregado no cache. O cache é muito importante para o desempenho do computador, pois ele agiliza o acesso aos dados, porém se os programas forem criados de modo errado, podem causar falhas no cache, fazendo com que o programa perca desempenho; e é este desempenho que analisaremos neste trabalho.

2. Testes Neste trabalho utilizaremos um computador com o seguinte processador: Intel Core 2 Duo Processor T5750 (2M Cache, 2.00 GHz, 667 MHz FSB) SPECIFICATIONS Essentials Status Launched Launch Date Q1'08 Processor Number T5750 # of Cores 2 # of Threads 2 Clock Speed 2 Ghz L1 Cache 2 x 32 KB instruction caches 2 x 32 KB write-back data caches L2 Cache 2 MB Bus/Core Ratio 12 FSB Speed 667 MHz FSB Parity No Instruction Set 64-bit Embedded Options No Available Supplemental SKU No Lithography 65 nm Max TDP 35 W VID Voltage Range 1.075V-1.175V Package Specifications TJUNCTION 85 C Package Size 35mm x 35mm Processing Die Size 143 mm 2 # of Processing Die Transistors Halogen Free Options Available 291 million No

Advanced Technologies Intel Turbo Boost Technology Intel Hyper-Threading Technology No No Intel Virtualization Technology (VTx) No Intel Trusted Execution Technology No Intel 64 Enhanced Intel SpeedStep Technology Intel Demand Based Switching Execute Disable Bit Yes Yes No Yes Sistema Operacional: Ubuntu Linux 10.04 LTS (32 bits).

2.1. Percorrendo Matrizes Neste teste iremos percorrer uma matriz grande de duas maneiras: primeiro percorremos linha a linha e, em seguida, percorreremos coluna a coluna. Linha a Linha time./teste1 real 0m0.641s user 0m0.116s sys 0m0.448s /usr/bin/time -v./teste1 Command being timed: "./teste1" User time (seconds): 0.12 System time (seconds): 0.44 Percent of CPU this job got: 88% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.63 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 1565808 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 97923 Voluntary context switches: 1 Involuntary context switches: 141 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0

valgrind --tool=cachegrind./teste1 ==3714== Cachegrind, a cache and branch-prediction profiler ==3714== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et al. ==3714== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3714== Command:./teste1 ==3714== ==3714== ==3714== I refs: 406,062,661 ==3714== I1 misses: 841 ==3714== L2i misses: 831 ==3714== I1 miss rate: 0.00% ==3714== L2i miss rate: 0.00% ==3714== ==3714== D refs: 102,915,842 (1,986,944 rd + 100,928,898 wr) ==3714== D1 misses: 6,292,110 ( 28,821 rd + 6,263,289 wr) ==3714== L2d misses: 6,279,771 ( 16,610 rd + 6,263,161 wr) ==3714== D1 miss rate: 6.1% ( 1.4% + 6.2% ) ==3714== L2d miss rate: 6.1% ( 0.8% + 6.2% ) ==3714== ==3714== L2 refs: 6,292,951 ( 29,662 rd + 6,263,289 wr) ==3714== L2 misses: 6,280,602 ( 17,441 rd + 6,263,161 wr) ==3714== L2 miss rate: 1.2% ( 0.0% + 6.2% ) Com estes testes podemos perceber que houve poucas falhas relativas de cache, permitindo que o programa executasse em menos de um segundo. Percebemos que o processador realizou menos de 150 trocas de contexto, este número é pequeno se comparado ao número de operações. Coluna a Coluna time./teste1 a real 0m2.738s user 0m1.580s sys 0m0.612s

/usr/bin/time -v./teste1 a Command being timed: "./teste1 a" User time (seconds): 1.65 System time (seconds): 0.62 Percent of CPU this job got: 91% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.49 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 1565792 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 97921 Voluntary context switches: 1 Involuntary context switches: 619 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 valgrind --tool=cachegrind./teste1 ==3717== Cachegrind, a cache and branch-prediction profiler ==3717== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et ==3717== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for cop ==3717== Command:./teste1 a ==3717== ==3717== ==3717== I refs: 506,042,647 ==3717== I1 misses: 841 ==3717== L2i misses: 831 ==3717== I1 miss rate: 0.00% ==3717== L2i miss rate: 0.00% ==3717== ==3717== D refs: 202,905,840 (101,976,942 rd + 100,928,898 wr) ==3717== D1 misses: 106,290,566 ( 6,278,821 rd + 100,011,745 wr) ==3717== L2d misses: 98,498,405 ( 95,510 rd + 98,402,895 wr) ==3717== D1 miss rate: 52.3% ( 6.1% + 99.0% ) ==3717== L2d miss rate: 48.5% ( 0.0% + 97.4% ) ==3717== ==3717== L2 refs: 106,291,407 ( 6,279,662 rd + 100,011,745 wr) ==3717== L2 misses: 98,499,236 ( 96,341 rd + 98,402,895 wr) ==3717== L2 miss rate: 13.8% ( 0.0% + 97.4% ) Com estes testes podemos perceber que houveram muitas falhas relativas de cache, fazendo que o programa demorasse mais de quatro vezes para executar. Percebemos, também, que o processador realizou mais de 600 trocas de contexto, este número indica que o processador teve que muitas vezes esperar os dados virem da memória RAM.

Quando a constante SIZE é ajustada para um valor próximo a 350 o tempo para percorrer o vetor, tanto linha a linha como coluna a coluna se aproximam muito. time./teste1 ou time./teste2 real 0m0.002s user 0m0.000s sys 0m0.002s Este fato ocorre pois, todos os dados pertinentes as matrizes estão sendo armazenados no cache e, portanto, em nenhum dos dois métodos é necessário ficar buscando os dados na memória. Em uma matriz de inteiros cada célula ocupa um byte, portanto uma matiz de 350 linhas e 350 colunas ocupa um espaço de 122500 bytes que é aproximadamente igual a 120 kbytes. Como o tamanho do cache L1, ao todo é de 128 kbytes, logo a grande maioria dos dados procurados pelo programa tanto percorrendo em linha a linha como coluna a coluna estavam no cache L1. Se o programa time tivesse uma precisão melhor, talvez esse valor de 350 tivesse que ser menor, pois além da matriz precisamos guardar o programa em si no cache, além disso, poderia acontecer de entrar outro processo de sistema junto com o programa, compartilhando assim o cache. Com estes testes podemos concluir que percorrer uma matriz grande linha a linha é muito mais eficiente do que percorrê-la coluna a coluna. Isto ocorre, pois na linguagem c, as matrizes são armazenadas na memória como sendo um vetor de vetores, ou seja, existe um vetor principal A que para cada índice dele existe um outro vetor B. Portanto, o vetor A armazena as linhas em quanto que os vetores B, armazenam as colunas. Quando acessamos um índice do vetor linha, o sistema operacional leva para o cache todos os valores dos endereços próximos, logo, quando acessamos o segundo índice este já estará no cache, economizando. Em alguns momentos, o programa necessite acessar a memória novamente para recarregar o cache, mas estes acessos são pequenos se comparados aos acessos ao cache. Com isso, podemos perceber que este tipo de acesso nos permite um grande desempenho e a economia de vários ciclos para realizar as operações. Quando acessamos o índice da primeira linha e primeira coluna, o sistema operacional também leva para o cache todos os valores dos endereços próximos, porém, estes valores para uma matriz grande são os da linha, ou de poucas próximas, colunas, logo, conforme formos acessando as colunas, o processador terá que esperar os dados virem da memória, pois os dados que estarão no cache não serão os desejados. Com isso, o processador ficará mais tempo ocioso esperando os dados da memória do que realizando as instruções, e com isso fazendo que este programa demore mais para executar, piorando assim seu desempenho. Obs.: Em outras linguagens de programação, os resultados poderiam ser ao contrário, pois elas podem tratar as matrizes sendo um vetor de linha que armazena colunas. Com estes testes podemos comprovar a importância do cache para evitar o acesso a dados na memória RAM, fazendo com que o programa não fique esperando por vários ciclos, melhorando assim sua eficiência.

2.2. Somando Vetores /usr/bin/time -v./teste2 1 Command being timed: "./teste2 1" User time (seconds): 0.53 System time (seconds): 0.13 Percent of CPU this job got: 85% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.77 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 471616 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 29535 Voluntary context switches: 1 Involuntary context switches: 171 Swaps: 0 File system inputs: 0 File system outputs: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 /usr/bin/time -v./teste2 50 Command being timed: "./teste2 50" User time (seconds): 1.96 System time (seconds): 0.11 Percent of CPU this job got: 92% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.24 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 471632 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 29536 Voluntary context switches: 1 Involuntary context switches: 512 Swaps: 0 File system inputs: 0 File system outputs: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0

valgrind --tool=cachegrind./teste2 1 ==2751== Cachegrind, a cache and branch-prediction profiler ==2751== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et al. ==2751== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2751== Command:./teste2 1 ==2751== ==2751== ==2751== I refs: 2,101,455,128 ==2751== I1 misses: 821 ==2751== L2i misses: 811 ==2751== I1 miss rate: 0.00% ==2751== L2i miss rate: 0.00% ==2751== ==2751== D refs: 1,259,955,891 (789,801,744 rd + 470,154,147 wr) ==2751== D1 misses: 3,132,842 ( 1,257,466 rd + 1,875,376 wr) ==2751== L2d misses: 3,129,896 ( 1,254,619 rd + 1,875,277 wr) ==2751== D1 miss rate: 0.2% ( 0.1% + 0.3% ) ==2751== L2d miss rate: 0.2% ( 0.1% + 0.3% ) ==2751== ==2751== L2 refs: 3,133,663 ( 1,258,287 rd + 1,875,376 wr) ==2751== L2 misses: 3,130,707 ( 1,255,430 rd + 1,875,277 wr) ==2751== L2 miss rate: 0.0% ( 0.0% + 0.3% ) valgrind --tool=cachegrind./teste2 50 ==2759== Cachegrind, a cache and branch-prediction profiler ==2759== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et al. ==2759== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2759== Command:./teste2 50 ==2759== ==2759== ==2759== I refs: 2,101,455,689 ==2759== I1 misses: 821 ==2759== L2i misses: 811 ==2759== I1 miss rate: 0.00% ==2759== L2i miss rate: 0.00% ==2759== ==2759== D refs: 1,259,956,247 (789,802,099 rd + 470,154,148 wr) ==2759== D1 misses: 31,257,837 ( 20,007,462 rd + 11,250,375 wr) ==2759== L2d misses: 31,254,892 ( 20,004,616 rd + 11,250,276 wr) ==2759== D1 miss rate: 2.4% ( 2.5% + 2.3% ) ==2759== L2d miss rate: 2.4% ( 2.5% + 2.3% ) ==2759== ==2759== L2 refs: 31,258,658 ( 20,008,283 rd + 11,250,375 wr) ==2759== L2 misses: 31,255,703 ( 20,005,427 rd + 11,250,276 wr) ==2759== L2 miss rate: 0.9% ( 0.6% + 2.3% )

time./teste2 1 real 0m0.749s user 0m0.560s sys 0m0.140s time./teste2 3 real 0m0.903s user 0m0.708s sys 0m0.136s time./teste2 10 real 0m1.500s user 0m1.196s sys 0m0.152s time./teste2 30 real 0m2.445s user 0m2.156s sys 0m0.140s time./teste2 50 real 0m2.292s user 0m1.896s sys 0m0.180s 3 Gráfico de Tempo por Pulos 2,5 2 Tempo(s) 1,5 1 0,5 0 0 10 20 30 40 50 60 Com este gráfico podemos observar que quanto maior o deslocamento, mais tempo demora para executar o programa, até que por volta de 30 pulos o tempo estabiliza. Isso acontece devido ao fato de que quando pulamos espaços maiores, temos que acessar mais vezes a memória RAM, pois os próximos dados a serem acessados, devido a distância de armazenamento, podem não estarem no cache, com isso, o processador é obrigado a esperar alguns ciclos até que os dados venham da memória RAM. Pulo

Quando passa de um determinando valor de deslocamento a curva deve ficar continua, pois poucos dados estarão no cache, sendo assim, toda vez que for acessar o próximo índice do vetor, o processador precisará esperar os dados virem da memória RAM, ou do cache L2, caso o vetor caiba nele. Podemos perceber também que o tempo de execução cresce quase que linearmente até 30 pulos, e que após esse valor esse mantem constante, este tempo pode aumentar em mais de três vezes conforme o deslocamento vai aumentando. 3 Gráfico de Tempo por Pulos 2,5 2 Tempo(s) 1,5 1 0,5 0 0 10 20 30 40 50 60 Com este gráfico podemos observar que quanto maior o deslocamento, mais tempo demora para executar o programa, até que por volta de 30 pulos o tempo estabiliza. Isso acontece devido ao fato de que quando pulamos espaços maiores, temos que acessar mais vezes a memória RAM, pois os próximos dados a serem acessados, devido a distância de armazenamento, podem não estarem no cache, com isso, o processador é obrigado a esperar alguns ciclos até que os dados venham da memória RAM. Quando passa de um determinando valor de deslocamento a curva deve ficar continua, pois poucos dados estarão no cache, sendo assim, toda vez que for acessar o próximo índice do vetor, o processador precisará esperar os dados virem da memória RAM, ou do cache L2, caso o vetor caiba nele. Podemos perceber também que o tempo de execução cresce quase que linearmente até 30 pulos, e que após esse valor esse mantem constante, este tempo pode aumentar em mais de três vezes conforme o deslocamento vai aumentando. Pulo

2.3. Memória Compartilhada entre Cores time./teste3 real 0m2.160s user 0m0.636s sys 0m0.004s /usr/bin/time -v./teste3 Command being timed: "./teste3" User time (seconds): 0.62 System time (seconds): 0.00 Percent of CPU this job got: 23% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.60 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 1376 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 129 Voluntary context switches: 1 Involuntary context switches: 280 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 opreport -l./teste3 CPU: CPU with timer interrupt, speed 0 MHz (estimated) Profiling through timer interrupt samples % symbol name 151 50.3333 inc_second 149 49.6667 main Como estes testes podemos perceber que o processador fica boa parte do tempo ocioso, isso ocorre devido ao fato de os o programa rodar dois processos que utilizam o mesmo espaço de memória. O problema de usar o mesmo espaço de memória é que uma instrução só pode ser processada, quando a outra não estiver sendo processada, pois assim cada processo poderá usar o espaço de memória. O programa perde desempenho, pois o processador não pode usar o pipeline para processos, ou seja, os processos não podem entrar juntos no processador para não darem conflitos de memória. Como consequência dessa espera para poder usar o espaço de memória podemos perceber nos testes muitas trocas de contexto, chegando quase a 300 trocas. Estas trocas são feitas para que ambos os processos possam ser processados sem que um tenha que esperar o outro terminar.

Utilizando o OProfile foi possível identificar que o processador praticamente dividiu-se entre as duas funções do programa, explicando também a frequente troca de contexto. Uma solução para este problema seria linearizar a execução, pois com isso a memória seria utilizada em instantes diferentes. Outra forma seria não compartilhar o mesmo espaço de memória, assim os processos poderiam rodar simultaneamente, resultando em um excelente desempenho. Ainda outra forma, seria definir um tempo ou quantidade de instruções que poderiam ser executadas por cada processo antes que o contexto fosse alterado, assim não perderíamos muito tempo com trocas de contexto. Esquema de Compartilhamento de um espaço de memória por um processo e por um dado.

2.4. Acesso Aleatório a um Vetor Os testes abaixo se referem a execução do Anexo 1. time./teste4 real 0m0.294s user 0m0.236s sys 0m0.056s time./teste4 a real 0m0.986s user 0m0.900s sys 0m0.064s /usr/bin/time -v./teste4 Command being timed: "./teste4" User time (seconds): 0.24 System time (seconds): 0.05 Percent of CPU this job got: 97% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.30 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 159072 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 9997 Voluntary context switches: 1 Involuntary context switches: 42 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0

/usr/bin/time -v./teste4 a Command being timed: "./teste4 a" User time (seconds): 0.87 System time (seconds): 0.06 Percent of CPU this job got: 100% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.93 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 159088 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 9999 Voluntary context switches: 1 Involuntary context switches: 82 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 valgrind --tool=cachegrind./teste4 a ==3173== Cachegrind, a cache and branch-prediction profiler ==3173== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et al. ==3173== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3173== Command:./teste4 a ==3173== ==3173== ==3173== I refs: 10,301,472,422 ==3173== I1 misses: 812 ==3173== L2i misses: 802 ==3173== I1 miss rate: 0.00% ==3173== L2i miss rate: 0.00% ==3173== ==3173== D refs: 5,897,386,359 (3,497,227,708 rd + 2,400,158,651 wr) ==3173== D1 misses: 106,249,591 ( 7,479 rd + 106,242,112 wr) ==3173== L2d misses: 105,729,456 ( 4,675 rd + 105,724,781 wr) ==3173== D1 miss rate: 1.8% ( 0.0% + 4.4% ) ==3173== L2d miss rate: 1.7% ( 0.0% + 4.4% ) ==3173== ==3173== L2 refs: 106,250,403 ( 8,291 rd + 106,242,112 wr) ==3173== L2 misses: 105,730,258 ( 5,477 rd + 105,724,781 wr) ==3173== L2 miss rate: 0.6% ( 0.0% + 4.4% )

valgrind --tool=cachegrind./teste4 ==3266== Cachegrind, a cache and branch-prediction profiler ==3266== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et al. ==3266== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3266== Command:./teste4 ==3266== ==3266== ==3266== I refs: 951,472,452 ==3266== I1 misses: 812 ==3266== L2i misses: 802 ==3266== I1 miss rate: 0.00% ==3266== L2i miss rate: 0.00% ==3266== ==3266== D refs: 590,289,590 (350,130,939 rd + 240,158,651 wr) ==3266== D1 misses: 1,257,853 ( 7,475 rd + 1,250,378 wr) ==3266== L2d misses: 1,254,959 ( 4,673 rd + 1,250,286 wr) ==3266== D1 miss rate: 0.2% ( 0.0% + 0.5% ) ==3266== L2d miss rate: 0.2% ( 0.0% + 0.5% ) ==3266== ==3266== L2 refs: 1,258,665 ( 8,287 rd + 1,250,378 wr) ==3266== L2 misses: 1,255,761 ( 5,475 rd + 1,250,286 wr) ==3266== L2 miss rate: 0.0% ( 0.0% + 0.5% ) valgrind --tool=cachegrind./teste4 a ==3607== Cachegrind, a cache and branch-prediction profiler ==3607== Copyright (C) 2002-2009, and GNU GPL'd, by Nicholas Nethercote et al. ==3607== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3607== Command:./teste4 a ==3607== ==3607== ==3607== I refs: 1,031,472,422 ==3607== I1 misses: 812 ==3607== L2i misses: 802 ==3607== I1 miss rate: 0.00% ==3607== L2i miss rate: 0.00% ==3607== ==3607== D refs: 590,289,585 (350,130,934 rd + 240,158,651 wr) ==3607== D1 misses: 10,624,841 ( 7,479 rd + 10,617,362 wr) ==3607== L2d misses: 10,105,335 ( 4,675 rd + 10,100,660 wr) ==3607== D1 miss rate: 1.7% ( 0.0% + 4.4% ) ==3607== L2d miss rate: 1.7% ( 0.0% + 4.2% ) ==3607== ==3607== L2 refs: 10,625,653 ( 8,291 rd + 10,617,362 wr) ==3607== L2 misses: 10,106,137 ( 5,477 rd + 10,100,660 wr) ==3607== L2 miss rate: 0.6% ( 0.0% + 4.2% )

Este programa consiste em fazer acessos aleatórios a um vetor de 10000000 de elementos caso seja passado algum parâmetro, caso contrário percorre o vetor linearmente. Quando sorteamos um índice de um vetor, em questão de desempenho podemos, ter duas situações: o dado procurado esta no cache ou o dado procurado não esta no cache. Quando ocorre o primeiro caso, o desempenho é melhor, pois o processador não precisa ficar esperando vários ciclos até que os dados venham da memória, no segundo caso, a espera é inevitável. Podemos perceber que quando fazemos acessos aleatórios, quase dobra a troca de contextos, isso ocorre, pois para um vetor de 10000000 de elementos com o processador utilizado a chance de o dado procurado esta no cache é de aproximadamente 0,01%. As muitas trocas de contextos e o processador precisa ficar esperando mais tempo para conseguir acessar os dados do cache fazem com que o desempenho do acesso aleatório seja muito pior, chegando a triplicar o tempo de execução. time./teste5 a real 0m0.918s user 0m0.868s sys 0m0.048s time./teste5 a real 0m0.917s user 0m0.848s sys 0m0.056s time./teste5 a real 0m0.918s user 0m0.856s sys 0m0.052s time./teste5 a real 0m0.929s user 0m0.856s sys 0m0.056s time./teste5 a real 0m0.948s user 0m0.884s sys 0m0.056s

Tempo por Execução 0,96 0,95 0,94 Tempo (s) 0,93 0,92 0,91 0,9 1 2 3 4 5 É interessante observar que o tempo de execução quase não varia, este fato ocorre porque a probabilidade de o dado procurado estar no cache é muito pequena, portante é provável que o processador tenha que esperar todos os dados virem direto da memória RAM.

3. Conclusão Com este trabalho pudemos conhecer um pouco sobre o funcionamento da memória cache e entender a sua importância para a eficiência do processamento, pudemos perceber, também, que sem a memória cache, os computadores modernos iriam ser muito mais lentos, pois sempre que precisassem de dados para alimentar o processamento teriam que buscar na memória e isso poderia ser muito custoso em relação a tempo. Os testes nos permitiram ver que quando conseguimos fazer códigos que fazem menos acessos a memória RAM em comparação com a memória cache estes executam em tempos menores. Pudemos perceber que o cache começa a perder sua utilidade quando trabalhamos com grandes quantidade de dados que estão armazenados longe, distância de endereçamento, um dos outros. Outro problema com o cache é quando dois processos precisam utilizar o mesmo espaço de memória, não podendo permitir alterações nesses endereços, para alterar é necessário trocar o contexto, fazendo assim com que o tempo de processamento aumente.

4. Referências Bibliográficas http://ark.intel.com/product.aspx?id=33915 http://www.ibm.com/developerworks/systems/library/es-oprofile/ http://www.cpu-world.com/cpus/core_2/intel-core%202%20duo%20mobile %20T5750%20LF80537GF0412M.html http://www.guiadohardware.net/termos/memoria-cache http://pt.wikipedia.org/wiki/cache

5. Anexos Anexo 1: Acesso aleatório a um vetor #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 10000000 int main (int argc, char *argv[]) { int i, j, *array1; srand(time(null)); array1 = (int *) malloc (SIZE*sizeof(int)); for (i = 0; i < SIZE; i++) { array1[i] = 1; } if (argc == 1) { for (i = 0; i < SIZE; i++) { j = rand() % SIZE; array1[i] = 2; } } else { for (i = 0; i < SIZE; i++) { j = rand() % SIZE; array1[j] = 2; } } free(array1); return 0; }