[FAQFlow #4]: OpenMPI: notes

Note: this is just for future reference. Sometimes [1,2] I create posts of this nature to serve as summaries of broad topics I might need later on. Because, in a few {months,years}, the future Thiago won’t remember what the current one was thinking…and, of course, I take those opportunities to share knowledge as well.

OpenMPI (and some OpenMP)

  • OpenMPI is like OpenMP in the sense that they both are standards: sponsored, endorsed and maintained by a bunch of important corporations.
  • OpenMP has something to do with parallelization, and doesn’t necessarily involve multiple machines. In particular, MP stands for multiprocessing [3]. It is like an add-on to the compiler, to take advantage of multiple processing cores available on several machines nowadays. Keyword: shared memory.
  • OpenMPI, in the other hand, is more like an API (a particular case of MPI), and is something that has been created for the purpose of having a common interface to pass messages between different computers (computing cores, for that matter). Keyword: distributed memory. You write a single program, which is run by all nodes(computers); however, each computer is uniquely assigned a number, so you can orchestrate the processing logic{,power} through them all.
  • Both support C, C++ and FORTRAN.
  • OpenMPI uses mainly the “mpi.h” header, and it usually uses a wrapper compiler (e.g. mpic++) which calls an underlying compiler (for example: g++, clang++, or the Intel compiler) which does the actual compilation. Why this wrapper? To encapsulate libraries and directories — the developer has no need to worry about -L, -I, or -l flags. The CXX environment variable can be used to define the underlying compiler, but this is not usually necessary.
  • OpenMP is hooked to the compiler: you don’t have to care about anything besides adding a few compile directives (#pragma) to your code. Keyword: thread parallelism. Because of this, data racing is common.

Still confused? [4]  might help.

OpenMPI, innocuous example

/* hello.cpp */
#include 
int main() {
  std::cout << "Hello, world!" << std::endl;
  return 0;
}

Now, you would normally run this program with:

g++ hello.cpp -o hello && ./hello

What would you do with OpenMPI? Nothing fancy, really:

mpic++ hello.cpp -o hellompi && ./hellompi

This will call g++ under the hood (supposing there are no other recognized C++ compilers on the system), but adding a few command-line flags to make it understand the MPI world. Of course, this example doesn’t contain anything which actually uses MPI. But, in fact, you can inspect they produce different executables:

sdiff <(strings hello) <(strings hellompi)
# or, alternatively, use `diff -uN`
# you'll see a few additional strings in the hellompi executable specific to OpenMPI

OpenMPI, real example

Credits for the source: on [3].

/* example.cpp */
#include 
#include 
int main(int argc, char *argv[]) {
    int rank; // id
    int size; // number of processors

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    std::cout << "Hello from " << rank << std::endl;
    MPI_Finalize();
}

Let’s proceed…

mpic++ example.cpp -o example
# note: if you try to compile this example with g++...well, then you're screwed, as it won't work out-of-the-box -- you would have to add the required compile flags manually

Then run it:

# naive: this will do nothing special
$ ./example
Hello from 0

# this is where the magic happens
# no need to specify it as "./example"; just use the executable name
$ mpirun -np 4 example
Hello from 0
Hello from 1
Hello from 2
Hello from 3

I ran it a few times and the order in which the lines were printed occasionally became different. You can use “400” instead of “4”…no problem.  My load average became “100”…whoa!!! Okay, a more lightweight example is “10”:

$ mpirun -np 10 example
Hello from 5
Hello from 3
Hello from 4
Hello from 0
Hello from 8
Hello from 7
Hello from 9
Hello from 2
Hello from 6
Hello from 1

You don’t have to have the number of cores of the argument you pass to the -np flag. This just specifies the number of processes you’ll spawn. When I just run “mpirun example”, without the -np argument, only two lines are printed. My ultrabook has two physical cores and two threads per core (2 x 2 = 4 virtual processors), so I believe mpirun picked the number of physical cores from my computer.

Conclusion

OpenMPI distributes work across nodes (between different nodes) and OpenMP distributes work within nodes (within a given node). In OpenMPI, each process is isolated from each other and can’t see variables or the memory from each other; in OpenMP, the several threads spawned by a process do share memory and can see variables from the other ones. The End.

Bonus

  • How many virtual processors do you have? Run `nproc`.
  • More info about processors? Run `lscpu` or inspect /proc/cpuinfo.
  • Slides on [5]
  • Run `ompi_info`.
  • `man MPI_Send` and `man MPI_Recv` (they’re both blocking calls)
  • `mpic++ –showme` (want to see the the compile flags of your friendly wrapper compiler?)
  • Difference between mpiexec and mpirun? It looks like the former is more standardized; however, the last one seems to be more common — at least on the sources I accessed.

Footnotes

  • [1]: https://thiagoperrotta.wordpress.com/2015/03/11/faqflow-2-android-upgrading-rooting-notes/
  • [2]: https://thiagoperrotta.wordpress.com/2014/03/07/scilab-notes-howto/
  • [3]: http://pawangh.blogspot.com.br/2014/05/mpi-vs-openmp.html
  • [4]: http://darmawan-salihun.blogspot.com.br/2009/11/openmp-vs-openmpi.html
  • [5]: https://www.open-mpi.org/papers/sc-2009/jjhursey-iu-booth.pdf
Advertisements
[FAQFlow #4]: OpenMPI: notes

Scilab Notes (~HOWTO)

Resumo sobre como utilizar o Scilab. Objetivo: matrizes, equações lineares e, mais geralmente, álgebra linear (computacional, ou não). A motivação para essas notas são basicamente a) resumo pessoal e b) estudar para uma prova.

Essas notas não tendem a ser extensivas. Na verdade, estou colocando aqui apenas as coisas que parecem ser mais importantes / úteis, e que eu provavelmente possa precisar uma vez ou outra. O Scilab é um ambiente full-featured, bem mais rico e complexo do que esse resumo pareça demonstrar (por exemplo, não estou cobrindo nada de programação, nem de Cálculo, aqui). É realmente impressionante. Eu já o tinha usado uma vez mas, agora que passo novamente por ele, percebo o quão completo ele é.

Essas notas estão licenciadas sob a Creative Commons BY-NC-SA (assim como todo o conteúdo desse blog, até hoje), o que significa que você pode compartilhá-las à vontade, desde que não faça uso comercial das mesmas; além disso, você pode criar derivados a partir delas, desde que os distribuam sob a mesma licença. OK, agora vamos ao trabalho.

Se você quiser aprimorar esse trabalho (por exemplo, indicando algum erro, ou acrescentando mais alguma coisa), por favor deixe um comentário ou me mande uma mensagem para que eu inclua mais itens aqui. Obrigado. No entanto, lembre-se que essa lista não pretende ser exaustiva.

Geral

  • Definindo um vetor / matriz linha: a = [1, 2, 3] ou b = [1 2 3].
  • Soma, subtração: a + ba - b.
  • Obtendo ajuda (interativa) sobre uma função: help <func_name>, exemplo help help.
  • Atribuição: c = a + b
  • Definindo constantes: k = 5.23
  • Ele suporta as keybindings do Emacs (parcialmente…)! Que alegria ao descobrir isso:
    • C-a: início da linha
    • C-e: fim da linha
    • C-k: matar a linha
    • C-b: volta um caractere
    • C-f: ops, em vez de avançar um caractere ele entra no search…
    • Up/down: navegar no histórico de comandos
    • $ scilab -nw: modo console
  • Listas: declarar como l = list(1,2,3,4). Existem várias formas de manipulá-las. Não achei uma função super útil a princípio, manipular vetores parece mais útil na maior parte das aplicações.
  • clc(): clear no console
  • %eps, sim, isso mesmo.
  • %T%F: true, false.
  • %inf, infinito.
  • rand() gera número aleatório (sempre de 0 a 1), rand(5,4) gera uma matriz com elementos aleatórios.
  • Funções/variáveis matemáticas: exp (e^), loglog10ceilfloorabs(-10)maxminsumprodsqrt(10)%pi%e, trigonométricas cossintanseccsccotg, inversas atan,acosasin, hiperbólicas, sinhcosh, … tudo o que você imaginar.
  • Funções matemáticas: factorfactorial.
  • Operações típicas com números complexos:
    • z=complex(1,2): define a partir da parte real, e depois da parte imaginária
    • Alternativamente: 1 + 2 * %i
    • real(z)
    • imag(z)
    • Conjugado z' ou conj(z)
  • ans retorna a última resposta que você não atribuiu (útil!)
  • : forma vetores (implícitos). Python-like. Exemplo: 1:5 retorna [1,2,3,4,5] e 1:2:5: retorna [1,3,5].

Álgebra linear

  • Multiplicação por escalar: k * a5 * a.
  • Comprimento de um vetor/matriz (= número de elementos): length(l) ou size(l)
  • Declarando uma matriz: m = [ [1,2]; [3,4] ] ou m = [1,2; 3,4], isto é, o ponto-e-vírgula separa as linhas. Convém declarar todos os elementos dela!
  • Multiplicação de matrizes: m * n.
  • Multiplicação de uma matriz por ela mesmo == potenciação: m^2m^5m**5, etc.
  • Inversa de uma matriz: m^-1 ou inv(m). Lembrando que m * m^-1 = matriz identidade.* Falando em identidade, para declarar uma matriz identidade com n linhas e m colunas:eye(n, m). Note que eye(k) (onde k é uma constante) é igual a 1 (constante). A matriz não precisa ser quadrada, porém note que apenas a diagonal principal é preenchida. Você também pode fazer eye(m) (de uma matriz).
  • zeros(3,4)ones(5,2): matriz com apenas zeros, ou apenas uns, com a dimensão especificada.
  • diag(m) retorna a diagonal principal de m.
  • Matriz transposta: m'.
  • Norma (quadrada) da matriz: norm(a). Você pode calcular a norma p especificando um segundo argumento: norm(a,1) retorna a norma de Manhattan / Táxi.
  • triu(m) ou tril(m): retorna a matriz triangular superior e inferior, respectivamente. Basicamente, a mesma matriz de entrada, só que as posições adequadas são preenchidas com zero.
  • Essa achei super útil: clean(m). Arredonda para zero os elementos muito pequenos (algo como 1e-10, por exemplo).
  • Extrair elemento de matriz: m(1,2) (da linha 1, coluna 2) (1-based index).
  • Extrair uma linha ou uma coluna (respec.): m(1,:)m(:,1).
  • Slices de uma matriz: m(2,2:3).
  • A\B e A/B: divisão de matriz, pela esquerda e pela direita, respectivamente. Isso é super útil, especialmente a do backslash.
  • clear a, m, k, b: mata as variáveis/matrizes/etc especificadas. Como se nunca tivessem existido.
  • kernel(m) – nullspace
  • [L,U] = lu(m) – retorna a fatoração LU de m. Também existe uma versão alternativa, com uma matriz de permutação, [L,U,E] = lu(m).
  • det(m) –> determinante
  • rank(m)–> posto de m = número de vetores linearmente independentes
  • trace(m) –> traço da matriz (=soma dos elementos da diagonal). O mesmo que sum(diag(m)). Faz sentido, né?

EDIT:

  • [autovetores, autovalores] = spec(A) ==> só podem ser calculados todos de uma vez.

Acho que estou satisfeito. A única coisa que falta para completar essa lista é como definir funções. Isso inevitavelmente nos fará entrar na parte de programação do Scilab…if, for e amigos. Provavelmente não postarei aqui como definir funções. Existe uma pequena chance de eu incluir aqui uma função como exemplo, mas só.

Scilab Notes (~HOWTO)