From c6a579b2efd742a3f5493cbaff0f59f9f9c1132b Mon Sep 17 00:00:00 2001 From: edurossob <erb19@c3sl.ufpr.br> Date: Sun, 21 May 2023 22:40:24 -0300 Subject: [PATCH] initial commit --- Makefile | 8 + Planilha-prefixSum-Pthreads-v1-e-v0-WZ.ods | Bin 0 -> 49975 bytes README.md | 88 +--- chrono.c | 80 ++++ especificacao-do-trabalho1-v1.1.txt | 184 +++++++++ ...mo-paralelo-SomaDePrefixos-Pthreads-V1.txt | 123 ++++++ prefixSumPth-v1 | Bin 0 -> 18552 bytes prefixSumPth-v1.c | 390 ++++++++++++++++++ roda-v1.sh | 13 + umaVez-v1.sh | 13 + 10 files changed, 814 insertions(+), 85 deletions(-) create mode 100644 Makefile create mode 100644 Planilha-prefixSum-Pthreads-v1-e-v0-WZ.ods create mode 100644 chrono.c create mode 100644 especificacao-do-trabalho1-v1.1.txt create mode 100644 ideia-do-algoritmo-paralelo-SomaDePrefixos-Pthreads-V1.txt create mode 100755 prefixSumPth-v1 create mode 100644 prefixSumPth-v1.c create mode 100755 roda-v1.sh create mode 100755 umaVez-v1.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c5bd248 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +all: prefixSumPth-v1 + +prefixSumPth-v1: prefixSumPth-v1.c chrono.c +# g++ -mcmodel=large prefixSumPth.c -O3 -o prefixSumPth -lpthread + g++ prefixSumPth-v1.c -O3 -o prefixSumPth-v1 -lpthread + + + diff --git a/Planilha-prefixSum-Pthreads-v1-e-v0-WZ.ods b/Planilha-prefixSum-Pthreads-v1-e-v0-WZ.ods new file mode 100644 index 0000000000000000000000000000000000000000..215a3eac59313605cb56e99fa44226f291799e87 GIT binary patch literal 49975 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 Yx}q!!pa+70a3A<#2fnBFYv61D1J=lI)Bpeg literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 9467a3f..2d68553 100644 --- a/README.md +++ b/README.md @@ -2,91 +2,9 @@ -## Getting started +## Como rodar -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +Execute o comando make -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +execute com: ./prefixSumPth-v1 <nTotalElements> <nThreads> -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://gitlab.c3sl.ufpr.br/erb19/parallel-prefix-sum.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://gitlab.c3sl.ufpr.br/erb19/parallel-prefix-sum/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/chrono.c b/chrono.c new file mode 100644 index 0000000..3aa91d6 --- /dev/null +++ b/chrono.c @@ -0,0 +1,80 @@ +// chrono.h +// +// A small library to measure time in programs +// +// by W.Zola (2017) + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <stdio.h> + + + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include <sys/time.h> /* struct timeval definition */ +#include <unistd.h> /* declaration of gettimeofday() */ + +#include <time.h> + + + typedef struct { + + struct timespec xadd_time1, xadd_time2; + long long xtotal_ns; + long xn_events; + + } chronometer_t; + + + void chrono_reset( chronometer_t *chrono ) + { + chrono->xtotal_ns = 0; + chrono->xn_events = 0; + } + + inline void chrono_start( chronometer_t *chrono ) { + clock_gettime(CLOCK_MONOTONIC_RAW, &(chrono->xadd_time1) ); + } + + inline long long chrono_gettotal( chronometer_t *chrono ) { + return chrono->xtotal_ns; + } + + inline long long chrono_getcount( chronometer_t *chrono ) { + return chrono->xn_events; + } + + inline void chrono_stop( chronometer_t *chrono ) { + + clock_gettime(CLOCK_MONOTONIC_RAW, &(chrono->xadd_time2) ); + + long long ns1 = chrono->xadd_time1.tv_sec*1000*1000*1000 + + chrono->xadd_time1.tv_nsec; + long long ns2 = chrono->xadd_time2.tv_sec*1000*1000*1000 + + chrono->xadd_time2.tv_nsec; + long long deltat_ns = ns2 - ns1; + + chrono->xtotal_ns += deltat_ns; + chrono->xn_events++; + } + + void chrono_reportTime( chronometer_t *chrono, const char *s ) { + + printf("\n%s deltaT(ns): %lld ns for %ld ops \n" + " ==> each op takes %lld ns\n", + s, chrono->xtotal_ns, chrono->xn_events, + chrono->xtotal_ns/chrono->xn_events ); + } + + void chrono_report_TimeInLoop( chronometer_t *chrono, char *s, int loop_count ) { + + printf("\n%s deltaT(ns): %lld ns for %ld ops \n" + " ==> each op takes %lld ns\n", + s, chrono->xtotal_ns, chrono->xn_events*loop_count, + chrono->xtotal_ns/(chrono->xn_events*loop_count) ); + } diff --git a/especificacao-do-trabalho1-v1.1.txt b/especificacao-do-trabalho1-v1.1.txt new file mode 100644 index 0000000..2b8db2e --- /dev/null +++ b/especificacao-do-trabalho1-v1.1.txt @@ -0,0 +1,184 @@ +ci1316/ci316 - PROGRAMAÇÃO PARALELA - +1o semestre de 2023 +por W.Zola/UFPR + +Lab 1: SomaDePrefixos-com-PThreads (versão 1.1) +---------------------------------- +Histórico: +- v1.0 a versão inicial +- v1.1 trocamos para 5milhoes de elementos + +Data do enunciado: 10/maio/2023 +Data da entrega Lab1: 22/maio/2023 (será aproximadamente 2semanas aprox.!) + + +------- PASSOS PARA FAZER ESSE TRABALHO: ----------- +O professor vai fornecer: + - o programa main completo + - o script para rodar os experimentos + - um Makefile exemplo do professor + - uma planilha exemplo para voce rodar seus experimentos + e incluir seus dados na pagina dadosV1 + ao incluir os seus dados a planilha já tem os gráficos + como incluir? + * compile com comando make + * rode o script do professor chamado roda.sh na sua máquina + A MÃQUINA DEVE TER NO MÃNIMO 4 CORES E 8GB DE MEMÓRIA + (voce DEVE ter um mÃnimo de processos rodando para não atrapalhar suas medidas) + assim: + #rode com 5milhoes de elementos em máquina com 8GB assim + ./roda.sh 5000000 + + + esse script vai produzir a saida das experiencias para + 1 a 8 threads (10 vezes cada) + Voce deve copiar (contol-C) a saida do script + na página dadosV1 na primeira celula dessa pagina + fazer (control-V nessa célula) + MARCANDO PARA A PLANILHA USAR COMO SEPARADORES OS CARACTERES [] + + Ao fazer isso as tabelas estarão ok, e as cẽlulas também + certifique-se que está tudo certo + + Aà basta voce ajustar as escalas dos gráficos + - clicando com mouse os eixos y (esquerdo ou direito) para + e adequar as escalas, o prof. vai mostra na sala + + +Objetivos: +// Obter uma implementação paralela MAIS eficiente para +// esse algoritmo "Soma de Prefixos" V1 +// em CPUs multicore usando PThreads + +// será feita a soma de prefixos de +// um vetor de long (ou seja, INTEIROS DE 64BITS) +// usando nThreads (definida via linha de comando) + +// Para ESSE lab faremos: +// a soma de prefixos será com a operação SOMA (+) + +// Funcionamento: +// O programa deve funcionar para nTotalElements com nThreads +// Com: nTotalElements e nThreads obtidos da linha de comando +// (assim como a versão feita em aula para a redução) +// Utilização do programa: +// usage: ./prefixSumPth <nTotalElements> <nThreads> + +// ENTRADA para o algoritmo: +// A entrada para a função PthPrefixSum será: +// um vetor (GLOBAL) de nTotalElements números inteiros +// chamado InputVector +// (nTotalElements obtido da linha de comando) +// +// Para esse teste o vetor NÂO será lido, +// - o vetor será preenchido sequencialmente pela função main +// Assim como nossa implementação anteriror, +// a inicializaçao do vetor de entrada (em main) deve ser +// +// // initialize InputVector + for( long i = 0; i < nTotalElements; i++ ){ + int r = rand(); // Returns a pseudo-random integer + // between 0 and RAND_MAX. + InputVector[i] = (r % 1000) - 500; + } + +// SAIDA do o algoritmo: +// A saÃda da função PthPrefixSum será: +// um vetor (GLOBAL) de nTotalElements números inteiros +// chamado OutputVector +// (nTotalElements obtido da linha de comando) +// + +// o programa deve calcular e imprimir +// o tempo e a vazão de calculo da Soma de Prefixos usando +// a VERSÂO 1 do algoritmo usando as idéias descritas +// no arquivo: +// ideia-do-algoritmo-paralelo-SomaDePrefixos-Pthreads-V1.txt + + +// Verificaçao de correção do programa: +// ------------------------------------ +// (item incluido na versao 1) +// o programa main, antes de terminar, deve verificar +// se seu algoritmo paralelo gerando corretamente o vetor de saÃda +// essa verificação DEVE ser feita de forma sequencial +// (ao final do main), INCLUINDO-SE O CÓDIGO ABAIXO +// esse código roda apenas ao final, e NÃO deve influenciar +// na medida de tempo feita para o algoritmo paralelo e +// nem deve influenciar no cálculo e resultado de VAZAO reportada + +void verifyPrefixSum( const TYPE *InputVec, + const TYPE *OutputVec, + long nTotalElmts ) +{ + volatile TYPE last = InputVec[0]; + int ok = 1; + for( long i=1; i<nTotalElmts ; i++ ) { + if( OutputVec[i] != (InputVec[i] + last) ) { + fprintf( stderr, "In[%ld]= %ld\n" + "Out[%ld]= %ld (wrong result!)\n" + "Out[%ld]= %ld (ok)\n" + "last=%ld\n" , + i, InputVec[i], + i, OutputVec[i], + i-1, OutputVec[i-1], + last ); + ok = 0; + break; + } + last = OutputVec[i]; + } + if( ok ) + printf( "\nPrefix Sum verified correctly.\n" ); + else + printf( "\nPrefix Sum DID NOT compute correctly!!!\n" ); +} + + +// O código ACIMA DEVE SER chamado ao final de main + da seguinte maneira para verificar que seus algoritmos funcionam: + + verifyPrefixSum( InputVector, OutputVector, nTotalElements ); + + +// rodar o programa 10 vezes obtendo o tempo MÃNIMO e o MÉDIO +// colocar TODOS os resultados em planilha + +// Colocar em uma planilha (tabela) os tempos para +// 1, 2, 3, 4, 5, 6, 7, e 8 threads +// A última coluna da tabela (planilha) deve calcular +// a aceleração para 1, 2, 3, 4, 5, 6, 7, e 8 threads +// colocando em uma planilha (tabela) + +// Entregar: +// um tar.gz contendo: +// - o fonte da sua implementação em C, +// - os scripts para compilar e rodar as experiências +// - a planilha preenchida com dados conforme descrito acima +// - Um arquivo com seu relatório descrevendo +// +// a) como você implementou (descrever seu algoritmo) +// +// b) a descrição do processador que voce usou, +// seu modelo e caracteristicas importantes para o experimento +// COLOQUE EM APENDICE NO RELATORIO A SAIDA DO COMANDO lscpu + +// de preferência adicione também uma figura da +// topologia dos cores do processador obtida pelo programa lstopo + +// c) a descrição dos experimentos e como foram feitas as medidas +// +// d) a planilha de resultados sumarizando a vazao e aceleração +// e) um gráfico (obtido de sua planilha) mostrando: +// - no eixo X: o número de threads +// - no eixo Y da esquerda: a vazao para cada número de threads +// unir os pontos com linhas de uma certa cor C1 de sua preferência +// - no eixo Y da direita: a aceleração para cada número de threads +// unir os pontos com linhas de uma OUTRA cor C2 de sua preferência + +ENTREGA: +Em princÃpio o tar.gz deve ser entregue via upload na ufpr virtual +O professor deverá incluir (em breve) a opção para entrega do lab2 na ufpr virtual + +-------------------------------------- + diff --git a/ideia-do-algoritmo-paralelo-SomaDePrefixos-Pthreads-V1.txt b/ideia-do-algoritmo-paralelo-SomaDePrefixos-Pthreads-V1.txt new file mode 100644 index 0000000..9807253 --- /dev/null +++ b/ideia-do-algoritmo-paralelo-SomaDePrefixos-Pthreads-V1.txt @@ -0,0 +1,123 @@ +Trabalho 1: CI316 1sem22 UFPR + +Versão dessa Especificação: v1 + +Idéia: Algoritmo de soma de prefixos com pthreads +------------------------------------------------- + +Idéia geral: +Uma idéia "inicial/basica" para algoritmo ParallelPrefixSumPth, +cada thread pode fazer operaçoes de escrita em memória na +sua "faixa" de números. +No entanto, nesse trabalho DEVEMOS fazer uma verão mais otimizada que: + +a) opera em duas fases separadas por APENAS UMA BARREIRA +b) na fase ANTES da barreira: + - cada thread APENAS LÊ todos os + elementos da sua faixa produzindo sua partialSum de elementos. + (O vetor global partialSum, tem uma célula por thread, + cada célula irá produzir sua soma parcial ANTES da barreira) + +c) APÓS BARREIRA: + - Como o vetor de somasParciais só possui uma célula por thread + fica fácil (e muito rápido, pois são poucas posições) cada thread + fazer um "for" no vetor calculando o valor do seu prefixo na + sua variável local myPrefixSum. + Assim as threads NÃO precisam esperar a thread 0 calcular isso, + isso tamém evita a necessidade de uma barreira adicional, + pois não esperamos mais a thread 0 calcular myPrefixSum. + - Cada thread, após calcular seu valor myPrefixSum deve, então, + uma um "for" apenas, calcular e escrever corretamente os valores + na sua faixa, produzindo a soma de prefixos na sequencia final + correta (ou seja em uma passada de escritas na faixa) + +OBS: poderiamos usar apenas um vetor de entrada e saida (do algoritmo), + ou seja, seria uma implementação "in place", ou seja, + estarÃamos alterando o vetor de entrada. + + Essa NÂO será a especificação do trabalho 1. + + Nesse trabalho 1 devemos ter: + - um vetor de entrada chamado InputVector (de long ints). + - um vetor de saÃda chamado OutputVector (de long ints). + +Especificamente para esse trabalho 1, devemos produzir uma funçao +para esse algoritmo paralelo chamado ParallelPrefixSumPth. +DEVEMOS, então, DEFINIR e usar a funçao ParallelPrefixSumPth abaixo, +que faz tudo, e seja ENCAPSULADA implementando com thread pool: + - cria as threads necessárias, e inicializa qualquel estrutura necessária + (e.g. barreiras, criar pool de threads, etc) + - O número de threads deve ser recebido como parâmetro + - fazer o algoritmo paralelo descrito com APENAS uma barreira + (se usar mais vai fica pior, e perder pontos) + +O prototipo dessa função deve ser: + +#define TYPE long // tipo dos dados dos vetores DEVE ser long + +void ParallelPrefixSumPth( const TYPE *InputVec, + const TYPE *OutputVec, + long nTotalElmts, + int nThreads ); + +Obs: para ESSE trabalho + vamos alocar os vetores de entrada e saÃda via malloc, + permitindo alocar grandes vetores. + +-------------------------------- + +Exemplo para vetor de 8 números e fazendo com 3 threads: + +Exemplo de vetor InputVector (de entrada): +vetor InputVector: [3 1 7 . 0 4 1 . 6 3] + +Cada thread faz a soma de valores na sua faixa de números (chunk), +produzindo um valor na sua posição do vetor global partialSum +Ou seja, para esse exemplo fica: +A thread 0 faz: + - calcula myPartialSum = 3+1+7 + - armazena no vetor global assim: PartialSum[0] = myPartialSum; +A thread 1 faz: + - calcula myPartialSum = 0+4+1 + - armazena no vetor global assim: PartialSum[1] = myPartialSum; +A thread 2 faz: + - calcula myPartialSum = 6+3 + - armazena no vetor global assim: PartialSum[2] = myPartialSum; +O vetor global PartialSum fica: +PartialSum: [ 11 5 9 ] + +Após a barreira cada thread faz: +a) calcula a soma do seu prefixo lendo o vetor global PartialSum + e calculando seu "prefixo" na variável local myPrefixSum +b) produz a soma de prefixos correta na usa faixa, + lendo lendo a faixa do vetor de entrada e escrevendo no vetor + de saÃda, e usando o seu valor calculado em na soma myPrefixSum + +vetor InputVector: [3 1 7 . 0 4 1 . 6 3] +Assim temos: +A thread 0 faz: + a) myPrefixSum = 0 (sempre!) + b) produz sua faixa no vetor de saÃda com APENAS UM "for" + sua faixa fica [ 3 4 11 ... ] +A thread 1 faz: + a) myPrefixSum = 11 + b) produz sua faixa no vetor de saÃda com APENAS UM "for" + sua faixa fica [ ... 11 15 16 ... ] +A thread 2 faz: + a) myPrefixSum = 16 (porque? somou 11+5 do vetor PartialSum) + b) produz sua faixa no vetor de saÃda com APENAS UM "for" + sua faixa fica [ ... 22 25 ] + +Nesse caso obtemos o seguinte vetor de saÃda +OutputVector: [3 4 11 . 11 15 16 . 22 25] + + +--------------------------------- +Ou seja, vamos conferir: +InputVector: [3 1 7 . 0 4 1 . 6 3] +OutputVector: [3 4 11 . 11 15 16 . 22 25] + +Ou seja, FUNCIONA! + +---------------------------------- + diff --git a/prefixSumPth-v1 b/prefixSumPth-v1 new file mode 100755 index 0000000000000000000000000000000000000000..4b5add36068fc62ac4b18d9432a032e0ebe6e4e7 GIT binary patch literal 18552 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 JvMJbD_TM*Ww8#Je literal 0 HcmV?d00001 diff --git a/prefixSumPth-v1.c b/prefixSumPth-v1.c new file mode 100644 index 0000000..39f2fc1 --- /dev/null +++ b/prefixSumPth-v1.c @@ -0,0 +1,390 @@ +// TRABALHO1: CI1316 1o semestre 2023 +// Aluno: +// GRR: +// + + /////////////////////////////////////// + ///// ATENCAO: NAO MUDAR O MAIN ///// + /////////////////////////////////////// + +#include <iostream> // WILL USE SOME C++ features +#include <typeinfo> +// template <typename T> std::string type_name(); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + + + +#include "chrono.c" + +//#define DEBUG 2 +#define DEBUG 0 + +#define MAX_THREADS 64 + +#define MAX_TOTAL_ELEMENTS (500 * 1000 * 1000) // para maquina de 16GB de RAM +//#define MAX_TOTAL_ELEMENTS (200 * 1000 * 1000) // para maquina de 8GB de RAM + + +// ESCOLHA o tipo dos elementos usando o #define TYPE adequado abaixo +// a fazer a SOMA DE PREFIXOS: +#define TYPE long // OBS: o enunciado pedia ESSE (long) +//#define TYPE long long +//#define TYPE double +// OBS: para o algoritmo da soma de prefixos +// os tipos abaixo podem ser usados para medir tempo APENAS como referência +// pois nao conseguem precisao adequada ou estouram capacidade +// para quantidades razoaveis de elementos +//#define TYPE float +// #define TYPE int + + + +int nThreads; // numero efetivo de threads + // obtido da linha de comando +int nTotalElements; // numero total de elementos + // obtido da linha de comando + +//volatile +TYPE *InputVector; // will use malloc e free to allow large (>2GB) vectors +//volatile +TYPE *OutputVector; // will use malloc e free to allow large (>2GB) vectors + + +chronometer_t parallelPrefixSumTime; + +volatile TYPE partialSum[MAX_THREADS]; + +int min(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +void verifyPrefixSum( const TYPE *InputVec, + const TYPE *OutputVec, + long nTotalElmts ) +{ + volatile TYPE last = InputVec[0]; + int ok = 1; + for( long i=1; i<nTotalElmts ; i++ ) { + if( OutputVec[i] != (InputVec[i] + last) ) { + fprintf( stderr, "In[%ld]= %ld\n" + "Out[%ld]= %ld (wrong result!)\n" + "Out[%ld]= %ld (ok)\n" + "last=%ld\n" , + i, InputVec[i], + i, OutputVec[i], + i-1, OutputVec[i-1], + last ); + ok = 0; + break; + } + last = OutputVec[i]; + } + if( ok ) + printf( "\nPrefix Sum verified correctly.\n" ); + else + printf( "\nPrefix Sum DID NOT compute correctly!!!\n" ); +} + + +typedef struct { + int id; + long ini, fim; + const TYPE *inputVec; + TYPE *outputVec; +} args_t; + +pthread_barrier_t partialSum_barrier; + + + +void* prefixSum(int id, const TYPE *input, TYPE * output, long start, long end){ + + long currentSum = 0; + + for(int i = 1; i < id; i++){ + currentSum += partialSum[i]; + } + + // printf("(%ld, %ld] -> [", start, end); + for(int i = start; i < end; i++){ + output[i] = currentSum + input[i]; + currentSum = output[i]; + // printf("%ld ", output[i]); + } + + // printf("]\n"); + + + return NULL; + +} + +void* partialSumCalculator(void *args){ + + int id = ((args_t*)args)->id; + long ini = ((args_t*)args)->ini; + long end = ((args_t*)args)->fim; + const TYPE *InputVec = ((args_t*)args)->inputVec; + TYPE *OutputVec = ((args_t*)args)->outputVec; + + // printf("Starting id %i\n", id); + + long sum = 0; + + for (int i = ini; i < end; i++) { + sum += InputVec[i]; + } + + partialSum[id] = sum; + + pthread_barrier_wait(&partialSum_barrier); + + prefixSum(id, InputVec, OutputVec, ini, end); + + return NULL; +} + + +void ParallelPrefixSumPth( const TYPE *InputVec, + TYPE *OutputVec, + long nTotalElmts, + int nThreads ) +{ + pthread_t Thread[MAX_THREADS]; + int my_thread_id[MAX_THREADS]; + + + ///////////////// INCLUIR AQUI SEU CODIGO da V1 ///////// + + // criar o POOL de threads aqui! + + // voce pode criar outras funcoes para as suas threads + + ////////////////////////////////////////////////////////// + +/* +Criar vetor global partialSum de tamanho nThreads +Dividir vetor InputVec em partes iguais para cada thread + +Cada Thread recebe um id único sequencial entre 1 e nThreads e sua sequencia do vetor inputVec +Cada Thread lê seu vetor e salva o valor da soma de seus elementos em partialSum na posição adequada + +Barreira + +Cada Thread descobre o myPrefixSum com o vetor partialSum +Utilizando esse valor, faz o cálculo de soma de prefixos para seus elementos +Inserindo esses valores no local adequado no OutputVec + +*/ + + long chunk_size = nTotalElmts/nThreads; + long bonus = nTotalElmts - chunk_size * nThreads; + + pthread_barrier_init(&partialSum_barrier, NULL, nThreads+1); + + long start = 0; + long end = chunk_size; + + + my_thread_id[0] = 0; + for (int i = 1; i <= nThreads; i++) { + if (bonus){ + end++; + bonus--; + } + + my_thread_id[i] = i; + + args_t* arguments = (args_t*) malloc(sizeof(args_t)); + arguments->id = i; + arguments->ini = start; + arguments->fim = end; + arguments->inputVec = InputVec; + arguments->outputVec = OutputVec; + + pthread_create( &Thread[i], NULL, partialSumCalculator, (void*) arguments); + + start = end; + end = start + chunk_size; + } + + + pthread_barrier_wait(&partialSum_barrier); + + long totalSum = 0; + for (int i = 1; i <= nThreads; i++) { + + // printf("Id %d: %ld\n", i, partialSum[i]); + totalSum += partialSum[i]; + } + + // printf("Finnish! -> Total sum = %ld\n", totalSum); + + + for (int i=1; i < nThreads; i++) + pthread_join(Thread[i], NULL); // isso é necessário ? + + + return; +} + +int main(int argc, char *argv[]) +{ + long i; + + /////////////////////////////////////// + ///// ATENCAO: NAO MUDAR O MAIN ///// + /////////////////////////////////////// + + if (argc != 3) + { + printf("usage: %s <nTotalElements> <nThreads>\n", + argv[0]); + return 0; + } + else + { + nThreads = atoi(argv[2]); + if (nThreads == 0) + { + printf("usage: %s <nTotalElements> <nThreads>\n", + argv[0]); + printf("<nThreads> can't be 0\n"); + return 0; + } + if (nThreads > MAX_THREADS) + { + printf("usage: %s <nTotalElements> <nThreads>\n", + argv[0]); + printf("<nThreads> must be less than %d\n", MAX_THREADS); + return 0; + } + nTotalElements = atoi(argv[1]); + if (nTotalElements > MAX_TOTAL_ELEMENTS) + { + printf("usage: %s <nTotalElements> <nThreads>\n", + argv[0]); + printf("<nTotalElements> must be up to %d\n", MAX_TOTAL_ELEMENTS); + return 0; + } + } + + // allocate InputVector AND OutputVector + InputVector = (TYPE *) malloc( nTotalElements*sizeof(TYPE) ); + if( InputVector == NULL ) + printf("Error allocating InputVector of %d elements (size=%ld Bytes)\n", + nTotalElements, nTotalElements*sizeof(TYPE) ); + OutputVector = (TYPE *) malloc( nTotalElements*sizeof(TYPE) ); + if( OutputVector == NULL ) + printf("Error allocating OutputVector of %d elements (size=%ld Bytes)\n", + nTotalElements, nTotalElements*sizeof(TYPE) ); + + +// #if DEBUG >= 2 + // Print INFOS about the reduction + TYPE myType; + long l; + std::cout << "Using PREFIX SUM of TYPE "; + + if( typeid(myType) == typeid(int) ) + std::cout << "int" ; + else if( typeid(myType) == typeid(long) ) + std::cout << "long" ; + else if( typeid(myType) == typeid(float) ) + std::cout << "float" ; + else if( typeid(myType) == typeid(double) ) + std::cout << "double" ; + else if( typeid(myType) == typeid(long long) ) + std::cout << "long long" ; + else + std::cout << " ?? (UNKNOWN TYPE)" ; + + std::cout << " elements (" << sizeof(TYPE) + << " bytes each)\n" << std::endl; + + + /*printf("reading inputs...\n"); + for (int i = 0; i < nTotalElements; i++) + { + scanf("%d", &InputVector[i]); + }*/ + + // initialize InputVector + //srand(time(NULL)); // Initialization, should only be called once. + + int r; + for (long i = 0; i < nTotalElements; i++){ + r = rand(); // Returns a pseudo-random integer + // between 0 and RAND_MAX. + InputVector[i] = (r % 1000) - 500; + //InputVector[i] = 1; i + 1; + } + + printf("\n\nwill use %d threads to calculate prefix-sum of %d total elements\n", + nThreads, nTotalElements); + + chrono_reset(¶llelPrefixSumTime); + chrono_start(¶llelPrefixSumTime); + + //////////////////////////// + // call it N times + #define NTIMES 1000 + // #define NTIMES 1 + TYPE globalSum; + TYPE *InVec = InputVector; + for( int i=0; i<NTIMES ; i++ ) { + //globalSum = parallel_reduceSum( InputVector, + // nTotalElements, nThreads ); + + ParallelPrefixSumPth( InputVector, OutputVector, nTotalElements, nThreads ); + //InputVector += (nTotalElements % MAX_TOTAL_ELEMENTS); + + + // wait 50 us == 50000 ns + // nanosleep((const struct timespec[]){{0, 50000L}}, NULL); + } + + // Measuring time of the parallel algorithm + // including threads creation and joins... + chrono_stop(¶llelPrefixSumTime); + chrono_reportTime(¶llelPrefixSumTime, "parallelPrefixSumTime"); + + // calcular e imprimir a VAZAO (numero de operacoes/s) + double total_time_in_seconds = (double)chrono_gettotal(¶llelPrefixSumTime) / + ((double)1000 * 1000 * 1000); + printf("total_time_in_seconds: %lf s for %d somas de prefixos\n", total_time_in_seconds, NTIMES ); + + double OPS = ((long)nTotalElements * NTIMES) / total_time_in_seconds; + printf("Throughput: %lf OP/s\n", OPS); + + //////////// + verifyPrefixSum( InputVector, OutputVector, nTotalElements ); + ////////// + + //#if NEVER + #if DEBUG >= 2 + // Print InputVector + printf( "In: " ); + for (int i = 0; i < nTotalElements; i++){ + printf( "%d ", InputVector[i] ); + + } + printf( "\n" ); + + // Print OutputVector + printf( "Out: " ); + for (int i = 0; i < nTotalElements; i++){ + printf( "%d ", OutputVector[i] ); + } + printf( "\n" ); + #endif + return 0; +} diff --git a/roda-v1.sh b/roda-v1.sh new file mode 100755 index 0000000..45e488d --- /dev/null +++ b/roda-v1.sh @@ -0,0 +1,13 @@ +#!/bin/bash +echo "USAGE: ./run.sh <nElements>" +echo "------------- COPIAR (ctrl-c) somente a partir da linha abaixo: -----------" + +for nt in {1..8} +do + echo "Executando 10 vezes com [$1] elementos e [$nt] threads:" + #for vez in {1..1} # 1 vez + for vez in {1..10} # 10 vezes + do + ./prefixSumPth-v1 $1 $nt | grep -oP '(?<=total_time_in_seconds: )[^ ]*' + done +done diff --git a/umaVez-v1.sh b/umaVez-v1.sh new file mode 100755 index 0000000..b83eaab --- /dev/null +++ b/umaVez-v1.sh @@ -0,0 +1,13 @@ +#!/bin/bash +echo "USAGE: ./run.sh <nElements>" +echo "------------- COPIAR (ctrl-c) somente a partir da linha abaixo: -----------" + +for nt in {1..8} +do + echo "Executando 10 vezes com [$1] elementos e [$nt] threads:" + for vez in {1..1} # 1 vez + #for vez in {1..10} # 10 vezes + do + ./prefixSumPth-v1 $1 $nt | grep -oP '(?<=total_time_in_seconds: )[^ ]*' + done +done -- GitLab