diff --git a/.gitignore b/.gitignore
index 51fd70d..9103000 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
_site/
*.swp
*.swo
-*.o
+*.o
\ No newline at end of file
diff --git a/CNAME b/CNAME
index 1f74f64..89259f5 100644
--- a/CNAME
+++ b/CNAME
@@ -1 +1 @@
-mpitutorial.com
+mpitutorial.com
\ No newline at end of file
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index a11e5aa..57aa16b 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1 +1,2 @@
-Wes Kendall (wesleykendall@gmail.com)
\ No newline at end of file
+Wes Kendall (wesleykendall@gmail.com)
+Wesley Bland (wesley@wesbland.com)
diff --git a/Gemfile b/Gemfile
new file mode 100755
index 0000000..962b96e
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,8 @@
+source "https://rubygems.org"
+
+gem "jekyll"
+gem "jekyll-sitemap"
+gem "jekyll-gist"
+gem "jekyll-feed"
+gem "jekyll-paginate"
+gem "jekyll-redirect-from"
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..2778723
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,111 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
+ base64 (0.3.0)
+ bigdecimal (3.2.2)
+ colorator (1.1.0)
+ concurrent-ruby (1.3.5)
+ csv (3.3.5)
+ em-websocket (0.5.3)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0)
+ eventmachine (1.2.7)
+ faraday (2.14.1)
+ faraday-net_http (>= 2.0, < 3.5)
+ json
+ logger
+ faraday-net_http (3.4.2)
+ net-http (~> 0.5)
+ ffi (1.17.2)
+ forwardable-extended (2.6.0)
+ google-protobuf (4.31.1)
+ bigdecimal
+ rake (>= 13)
+ http_parser.rb (0.8.0)
+ i18n (1.14.7)
+ concurrent-ruby (~> 1.0)
+ jekyll (4.4.1)
+ addressable (~> 2.4)
+ base64 (~> 0.2)
+ colorator (~> 1.0)
+ csv (~> 3.0)
+ em-websocket (~> 0.5)
+ i18n (~> 1.0)
+ jekyll-sass-converter (>= 2.0, < 4.0)
+ jekyll-watch (~> 2.0)
+ json (~> 2.6)
+ kramdown (~> 2.3, >= 2.3.1)
+ kramdown-parser-gfm (~> 1.0)
+ liquid (~> 4.0)
+ mercenary (~> 0.3, >= 0.3.6)
+ pathutil (~> 0.9)
+ rouge (>= 3.0, < 5.0)
+ safe_yaml (~> 1.0)
+ terminal-table (>= 1.8, < 4.0)
+ webrick (~> 1.7)
+ jekyll-feed (0.17.0)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-gist (1.5.0)
+ octokit (~> 4.2)
+ jekyll-paginate (1.1.0)
+ jekyll-redirect-from (0.16.0)
+ jekyll (>= 3.3, < 5.0)
+ jekyll-sass-converter (3.1.0)
+ sass-embedded (~> 1.75)
+ jekyll-sitemap (1.4.0)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-watch (2.2.1)
+ listen (~> 3.0)
+ json (2.18.1)
+ kramdown (2.5.1)
+ rexml (>= 3.3.9)
+ kramdown-parser-gfm (1.1.0)
+ kramdown (~> 2.0)
+ liquid (4.0.4)
+ listen (3.9.0)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ logger (1.7.0)
+ mercenary (0.4.0)
+ net-http (0.9.1)
+ uri (>= 0.11.1)
+ octokit (4.25.1)
+ faraday (>= 1, < 3)
+ sawyer (~> 0.9)
+ pathutil (0.16.2)
+ forwardable-extended (~> 2.6)
+ public_suffix (6.0.2)
+ rake (13.3.0)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.11.1)
+ ffi (~> 1.0)
+ rexml (3.4.2)
+ rouge (4.5.2)
+ safe_yaml (1.0.5)
+ sass-embedded (1.89.2)
+ google-protobuf (~> 4.31)
+ rake (>= 13)
+ sawyer (0.9.2)
+ addressable (>= 2.3.5)
+ faraday (>= 0.17.3, < 3)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ unicode-display_width (2.6.0)
+ uri (1.1.1)
+ webrick (1.9.1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ jekyll
+ jekyll-feed
+ jekyll-gist
+ jekyll-paginate
+ jekyll-redirect-from
+ jekyll-sitemap
+
+BUNDLED WITH
+ 2.6.9
diff --git a/_config.yml b/_config.yml
index 7050a57..7af44e7 100644
--- a/_config.yml
+++ b/_config.yml
@@ -1,6 +1,6 @@
# Dependencies
-markdown: redcarpet
-highlighter: pygments
+markdown: kramdown
+highlighter: rouge
# Permalinks
permalink: pretty
@@ -10,22 +10,19 @@ title: MPI Tutorial
tagline:
url: http://mpitutorial.com
baseurl: ''
-paginate: 1
author:
name: Wes Kendall
url: https://github.com/wesleykendall/
email: wesleykendall@gmail.com
-gems:
+plugins:
- jekyll-redirect-from
-redcarpet:
- extensions: ['tables', 'fenced_code_blocks', 'no_intra_emphasis', 'with_toc_data', 'prettify']
# Custom vars
version: 1.0.0
github:
- repo: https://github.com/wesleykendall/mpitutorial
- code: https://github.com/wesleykendall/mpitutorial/tree/gh-pages
-pages_list:
- Beginner Tutorial: 'beginner-mpi-tutorial'
+ repo: https://github.com/mpitutorial/mpitutorial
+ code: https://github.com/mpitutorial/mpitutorial/tree/gh-pages
+pages_list:
+ Tutorials: 'tutorials'
Recommended Books: 'recommended-books'
About: 'about'
diff --git a/_layouts/post.html b/_layouts/post.html
index 30eb60d..7bc2ad6 100644
--- a/_layouts/post.html
+++ b/_layouts/post.html
@@ -2,8 +2,37 @@
layout: default
---
+
+
{{ page.title }}
+
Author: {{ page.author }}
+
{{ content }}
@@ -11,7 +40,7 @@
{{ page.title }}
Want to contribute?
-This site is hosted entirely on
Github. This site is no longer being actively contributed to by the original author (Wes Kendall), but it was placed on Github in the hopes that others would write high-quality MPI tutorials. Click
here for more information about how you can contribute.
+This site is hosted entirely on
GitHub. This site is no longer being actively contributed to by the original author (Wes Kendall), but it was placed on GitHub in the hopes that others would write high-quality MPI tutorials. Click
here for more information about how you can contribute.
diff --git a/about.md b/about.md
index 0454158..c749748 100644
--- a/about.md
+++ b/about.md
@@ -7,13 +7,13 @@ This site is a collaborative space for providing tutorials about MPI (the Messag
## Contributing
-This site is hosted as a static page on [Github]({{ site.github.code }}). It is no longer actively contributed to by the original author, and any potential authors are encouraged to fork the repository [here]({{ site.github.code }}) and start writing a lesson!
+This site is hosted as a static page on [GitHub]({{ site.github.code }}). It is no longer actively contributed to by the original author, and any potential authors are encouraged to fork the repository [here]({{ site.github.code }}) and start writing a lesson!
-Github uses Jekyll, a markdown-based blogging framework for producing static HTML pages. For an introduction on using Jekyll with Github, checkout [this article](https://help.github.com/articles/using-jekyll-with-pages/).
+GitHub uses Jekyll, a markdown-based blogging framework for producing static HTML pages. For an introduction on using Jekyll with GitHub, checkout [this article](https://help.github.com/articles/using-jekyll-with-pages/).
All lessons are self-contained in their own directories in the *tutorials* directory of the main repository. New tutorials should go under this directory, and any code for the tutorials should go in the *code* directory of the tutorial and provide a Makefile with executable examples. Similarly, the structure of the posts should match other tutorials.
-For those that have never used Github or may feel overwhelmed about contributing a tutorial, contact Wes Kendall first at wesleykendall AT gmail DOT com. If you wish to write a tutorial with images as a Microsoft Word document or PDF, I'm happy to translate the lesson into the proper format for the site.
+For those that have never used GitHub or may feel overwhelmed about contributing a tutorial, contact Wes Kendall first at wesleykendall AT gmail DOT com. If you wish to write a tutorial with images as a Microsoft Word document or PDF, I'm happy to translate the lesson into the proper format for the site.
> **Note** - The tutorials on this site need to remain as informative as possible and encompass useful topics related to MPI. Before writing a tutorial, collaborate with me through email (wesleykendall AT gmail DOT com) if you want to propose a lesson to the beginning MPI tutorial. Similarly, we can also start an advanced MPI tutorial page for more advanced topics.
@@ -23,3 +23,12 @@ For those that have never used Github or may feel overwhelmed about contributing
Wes Kendall is the original author of mpitutorial.com. As a graduate student at the University of Tennessee, Knoxville, Wes earned his PhD under Jian Huang. His research revolved around large-scale data analysis and visualization, and he worked with the biggest supercomputers in the world. As a graduate student, he interned at Google, Oak Ridge National Labs, and Argonne National Labs. His research also earned him the Supercomputing 2011 Best Student Paper Award. He is currently co-founder and CTO of [Ambition](http://ambition.com), a data-analytics startup funded by YCombinator, Google Ventures, and several other top investment firms.
Disappointed with the amount of freely-available content on parallel programming and MPI, Wes started releasing tutorials on the subject after graduate school. Once his startup consumed most of his time, he opened up mpitutorial.com to the public on github.com so that others could start contributing high-quality content.
+
+### Dwaraka Nath
+
+Dwaraka Nath is a masters graduate from Birla Institute of Technology and Science, Pilani, India. He loves blogging and occasionally does some code contributions as well.
+
+You can find more about him on his [personal website](https://www.dwarak.in) and follow him on GitHub at [@dtsdwarak](https://github.com/dtsdwarak).
+
+### Wesley Bland
+Wesley Bland is a researcher in High Performance Computing and a contributor to both MPICH and Open MPI. He graduated from the University of Tennessee, Knoxville with his PhD under Dr. Jack Dongarra. His research involved fault tolerance at scale using MPI. After leaving the university, he went to Argonne National Laboratory where he worked under Dr. Pavan Balaji as a postdoctoral appointee and continued his fault tolerance research while working on MPICH directly. He has also worked at Intel and Meta.
diff --git a/advanced-mpi-tutorial.md b/advanced-mpi-tutorial.md
new file mode 100644
index 0000000..225b4b3
--- /dev/null
+++ b/advanced-mpi-tutorial.md
@@ -0,0 +1,10 @@
+---
+layout: page
+title: Advanced MPI Tutorial
+---
+
+Welcome to the advanced MPI tutorial! In this tutorial, you will learn some of the more advanced concepts of MPI. Below are the available lessons, each of which contain example code.
+
+This tutorial assumes that the reader understands the topics in the basic tutorial.
+
+* [Introduction to Groups and Communicators]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/)
diff --git a/atom.xml b/atom.xml
index 2e355d8..229c82d 100644
--- a/atom.xml
+++ b/atom.xml
@@ -1,5 +1,5 @@
---
-layout: nil
+layout: null
---
diff --git a/beginner-mpi-tutorial.md b/beginner-mpi-tutorial.md
deleted file mode 100644
index 1d47370..0000000
--- a/beginner-mpi-tutorial.md
+++ /dev/null
@@ -1,27 +0,0 @@
----
-layout: page
-title: Beginner MPI Tutorial
----
-
-Welcome to the MPI tutorial for beginners! In this tutorial, you will learn the basic concepts of MPI. Below are the available lessons, each of which contain example code.
-
-This beginner tutorial assumes that the reader has a basic knowledge of C, some C++, and Linux.
-
-## Introduction and MPI installation
-* [MPI tutorial introduction]({{ site.baseurl }}/tutorials/mpi-introduction/)
-* [Installing MPICH2 on a single machine]({{ site.baseurl }}/tutorials/installing-mpich2/)
-* [Launching an Amazon EC2 MPI cluster]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/)
-* [Running an MPI hello world application]({{ site.baseurl }}/tutorials/mpi-hello-world/)
-
-## Blocking point-to-point communication
-* [Sending and receiving with MPI_Send and MPI_Recv]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)
-* [Dynamic receiving with MPI_Probe and MPI_Status]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/)
-* [Point-to-point communication application - Random walking]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/)
-
-## Basic collective communication
-* [Collective communication introduction with MPI_Bcast]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/)
-* [Common collectives - MPI_Scatter, MPI_Gather, and MPI_Allgather]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/)
-* [Application example - Performing parallel rank computation with basic collectives]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/)
-
-## Advanced collective communication
-* [Using MPI_Reduce and MPI_Allreduce for parallel number reduction]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/)
\ No newline at end of file
diff --git a/index.md b/index.md
index b298745..b3a666b 100644
--- a/index.md
+++ b/index.md
@@ -5,11 +5,11 @@ title: A Comprehensive MPI Tutorial Resource
Welcome to mpitutorial.com, a website dedicated to providing useful tutorials about the Message Passing Interface (MPI).
-## Beginner Tutorial
-Wanting to get started learning MPI? Head over to the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/).
+## Tutorials
+Wanting to get started learning MPI? Head over to the [MPI tutorials]({{ site.baseurl }}/tutorials/).
## Recommended Books
Recommended books for learning MPI are located [here]({{ site.baseurl }}/recommended-books/).
## About
-This site is no longer being actively contributed to by its original author (Wes Kendall). However, mpitutorial.com has been placed on [Github]({{ site.github.code}}) so that others can contribute high-quality content. Click [here]({{ site.baseurl }}/about/) for more details on how to contribute.
+This site is no longer being actively contributed to by its original author (Wes Kendall). However, mpitutorial.com has been placed on [GitHub]({{ site.github.code}}) so that others can contribute high-quality content. Click [here]({{ site.baseurl }}/about/) for more details on how to contribute.
diff --git a/recommended-books.md b/recommended-books.md
index 7bb6542..6dd1208 100644
--- a/recommended-books.md
+++ b/recommended-books.md
@@ -3,31 +3,34 @@ layout: page
title: Recommended Books
---
-Here are the books that helped me out the most when learning MPI. Most of these are written by the primary designers of the Message Passing Interface. Some also include tutorials on how to use OpenMP with MPI. The very first book is a compilation of the beginner tutorials of this site as well and helps support mpitutorial.com.
+Here are the books that helped me out the most when learning MPI. Most of these are written by the primary designers of the Message Passing Interface. Some also include tutorials on how to use OpenMP with MPI.
-## Beginning MPI (An Introduction in C)
-This book is a compilation of all of the beginner tutorials on this site. It goes over everything from installing MPI on an Amazon EC2 cluster to the basics of sending and receiving with MPI to performing collective operations and reductions. If you have enjoyed the tutorials on this site and wish to have a copy of it in book format (while also supporting mpitutorial.com), then [click here for more info](http://www.amazon.com/gp/product/B00HM7O0M8/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B00HM7O0M8&linkCode=as2&tag=softengiintet-20).
-
-[](http://www.amazon.com/gp/product/B00HM7O0M8/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B00HM7O0M8&linkCode=as2&tag=softengiintet-20)
+> **Disclaimer** - The last book is primarily a compilation of these tutorials by the original mpitutorial.com author (Wes Kendall).
## Parallel Programming with MPI
-My personal favorite MPI book. The book gives a good overview of parallel computing before delving into all the various topics of MPI programming. It goes into detail about almost every essential MPI routine, and then it provides examples of parallel programs such as matrix multiplication and sorting. The end of the book discusses libraries and debugging routines. [Click here for more info](http://www.amazon.com/gp/product/1558603395/ref=as_li_qf_sp_asin_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=1558603395).
+My personal favorite MPI book. The book gives a good overview of parallel computing before delving into all the various topics of MPI programming. It goes into detail about almost every essential MPI routine, and then it provides examples of parallel programs such as matrix multiplication and sorting. The end of the book discusses libraries and debugging routines. [Click here for more info](http://www.amazon.com/gp/product/1558603395/ref=as_li_qf_sp_asin_il?ie=UTF8&linkCode=as2&camp=217145&creative=399377&creativeASIN=1558603395).
-[](http://www.amazon.com/gp/product/1558603395/ref=as_li_qf_sp_asin_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=1558603395)
+[](http://www.amazon.com/gp/product/1558603395/ref=as_li_qf_sp_asin_il?ie=UTF8&linkCode=as2&camp=217145&creative=399377&creativeASIN=1558603395)
-## Using MPI - 2nd Edition
-This is a more up-to-date book than the previous, but it mostly focuses on the newer and more advanced MPI routines in the second MPI standard. These include parallel I/O, remote memory access, and dynamic process management. The book also discusses using MPI with threads. This is a must have for advanced MPI development. [Click here for more info](http://www.amazon.com/gp/product/0262571323/ref=as_li_tf_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262571323).
+## Using MPI - 3rd Edition and Using Advanced MPI - 1st Edition
+This is a more up-to-date book than the previous. The "regular" book covers the fundamentals of MPI and the "advnaced" book covers additional topics. The table of contents can be found on [this](https://www.mcs.anl.gov/research/projects/mpi/usingmpi/) website. This is a must have for advanced MPI development. [Click here for Using MPI](https://www.amazon.com/gp/product/0262527391/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0262527391&linkCode=as2&linkId=52cc270bc3a798b37c787740d00c19d1). The "Using Advanced MPI" book is currently out of print.
-[](http://www.amazon.com/gp/product/0262571323/ref=as_li_tf_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262571323)
+[](https://www.amazon.com/gp/product/0262527391/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0262527391&linkCode=as2&linkId=52cc270bc3a798b37c787740d00c19d1)
## Parallel Programming in C with MPI and OpenMP
-This book is a bit older than the others, but it is still a classic. One strong point of this book is the huge amount of parallel programming examples, along with its focus on MPI and OpenMP. Many parallel programs are discussed in great detail, including matrix multiplication, fast fourier transforms, sorting, and combinatorial searching. [Click here for more info](http://www.amazon.com/gp/product/0071232656/ref=as_li_tf_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0071232656).
+This book is a bit older than the others, but it is still a classic. One strong point of this book is the huge amount of parallel programming examples, along with its focus on MPI and OpenMP. Many parallel programs are discussed in great detail, including matrix multiplication, fast fourier transforms, sorting, and combinatorial searching. [Click here for more info](http://www.amazon.com/gp/product/0071232656/ref=as_li_tf_il?ie=UTF8&linkCode=as2&camp=217145&creative=399377&creativeASIN=0071232656).
-[](http://www.amazon.com/gp/product/0071232656/ref=as_li_tf_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0071232656)
+[](http://www.amazon.com/gp/product/0071232656/ref=as_li_tf_il?ie=UTF8&linkCode=as2&camp=217145&creative=399377&creativeASIN=0071232656)
## MPI: The Complete Reference
-A complete reference guide to MPI one and two. The book does not necessarily teach MPI, but it provides a great reference and complete descriptions of every single function. One advantage of this book is that it includes Fortran routines as well as C routines. [Click here for more info](http://www.amazon.com/gp/product/0262692163/ref=as_li_tf_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262692163).
+A complete reference guide to MPI one and two. The book does not necessarily teach MPI, but it provides a great reference and complete descriptions of every single function. One advantage of this book is that it includes Fortran routines as well as C routines. [Click here for more info](http://www.amazon.com/gp/product/0262692163/ref=as_li_tf_il?ie=UTF8&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262692163).
+
+[](http://www.amazon.com/gp/product/0262692163/ref=as_li_tf_il?ie=UTF8&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262692163)
+
+
+
+## Beginning MPI (An Introduction in C)
+This book is a compilation of all of the beginner tutorials on this site. It goes over everything from installing MPI on an Amazon EC2 cluster to the basics of sending and receiving with MPI to performing collective operations and reductions. If you have enjoyed the tutorials on this site and wish to have a copy of it in book format, then [click here for more info](http://www.amazon.com/gp/product/B00HM7O0M8/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B00HM7O0M8&linkCode=as2).
-[](http://www.amazon.com/gp/product/0262692163/ref=as_li_tf_il?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262692163)
+[](http://www.amazon.com/gp/product/B00HM7O0M8/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B00HM7O0M8&linkCode=as2)
-
\ No newline at end of file
diff --git a/tutorials.md b/tutorials.md
new file mode 100644
index 0000000..a80dd8d
--- /dev/null
+++ b/tutorials.md
@@ -0,0 +1,56 @@
+---
+layout: page
+title: Tutorials
+redirect_from: '/beginner-mpi-tutorial/'
+---
+
+Welcome to the MPI tutorials! In these tutorials, you will learn a wide array of concepts about MPI. Below are the available lessons, each of which contain example code.
+
+The tutorials assume that the reader has a basic knowledge of C, some C++, and Linux.
+
+## Introduction and MPI installation
+* [MPI tutorial introduction]({{ site.baseurl }}/tutorials/mpi-introduction/)
+([中文版]({{ site.baseurl }}/tutorials/mpi-introduction/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/mpi-introduction/ja_jp))
+* [Installing MPICH2 on a single machine]({{ site.baseurl }}/tutorials/installing-mpich2/)
+([中文版]({{ site.baseurl }}/tutorials/installing-mpich2/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/installing-mpich2/ja_jp))
+* [Launching an Amazon EC2 MPI cluster]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/)
+([日本語]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/ja_jp))
+* [Running an MPI cluster within a LAN]({{ site.baseurl }}/tutorials/running-an-mpi-cluster-within-a-lan/)
+([日本語]({{ site.baseurl }}/tutorials/running-an-mpi-cluster-within-a-lan/ja_jp))
+* [Running an MPI hello world application]({{ site.baseurl }}/tutorials/mpi-hello-world/)
+([中文版]({{ site.baseurl }}/tutorials/mpi-hello-world/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/mpi-hello-world/ja_jp))
+
+## Blocking point-to-point communication
+* [Sending and receiving with MPI_Send and MPI_Recv]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)
+([中文版]({{ site.baseurl }}/tutorials/mpi-send-and-receive/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/mpi-send-and-receive/ja_jp))
+* [Dynamic receiving with MPI_Probe and MPI_Status]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/)
+([中文版]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/ja_jp))
+* [Point-to-point communication application - Random walking]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/)
+([中文版]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/ja_jp))
+
+## Basic collective communication
+* [Collective communication introduction with MPI_Bcast]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/)
+([中文版]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/ja_jp))
+* [Common collectives - MPI_Scatter, MPI_Gather, and MPI_Allgather]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/)
+([中文版]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/ja_jp))
+* [Application example - Performing parallel rank computation with basic collectives]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/)
+([中文版]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/ja_jp))
+
+## Advanced collective communication
+* [Using MPI_Reduce and MPI_Allreduce for parallel number reduction]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/)
+([中文版]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/ja_jp))
+
+## Groups and communicators
+* [Introduction to groups and communicators]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/)
+([中文版]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/zh_cn))
+([日本語]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/ja_jp))
diff --git a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/check_status.c b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/check_status.c
index b051601..ebd157b 100644
--- a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/check_status.c
+++ b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/check_status.c
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Example of checking the MPI_Status object from an MPI_Recv call
//
@@ -27,7 +27,7 @@ int main(int argc, char** argv) {
int numbers[MAX_NUMBERS];
int number_amount;
if (world_rank == 0) {
- // Pick a random amont of integers to send to process one
+ // Pick a random amount of integers to send to process one
srand(time(NULL));
number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
// Send the amount of integers to process one
diff --git a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/probe.c b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/probe.c
index 7adb9ce..e994b77 100644
--- a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/probe.c
+++ b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/probe.c
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Example of using MPI_Probe to dynamically allocated received messages
//
diff --git a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/index.md b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/index.md
index 9c9c87b..82af8aa 100644
--- a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/index.md
+++ b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/index.md
@@ -4,12 +4,13 @@ title: Dynamic Receiving with MPI Probe (and MPI Status)
author: Wes Kendall
categories: Beginner MPI
tags: MPI_Get_count, MPI_Probe
+translations: zh_cn,ja_jp
redirect_from: '/dynamic-receiving-with-mpi-probe-and-mpi-status/'
---
In the [previous lesson]({{ site.baseurl }}/tutorials/mpi-send-and-receive/), I discussed how to use MPI_Send and MPI_Recv to perform standard point-to-point communication. I only covered how to send messages in which the length of the message was known beforehand. Although it is possible to send the length of the message as a separate send / recv operation, MPI natively supports dynamic messages with just a few additional function calls. I will be going over how to use these functions in this lesson.
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code).
## The MPI_Status structure
As covered in the [previous lesson]({{ site.baseurl }}/tutorials/mpi-send-and-receive/), the `MPI_Recv` operation takes the address of an `MPI_Status` structure as an argument (which can be ignored with `MPI_STATUS_IGNORE`). If we pass an `MPI_Status` structure to the `MPI_Recv` function, it will be populated with additional information about the receive operation after it completes. The three primary pieces of information include:
@@ -37,7 +38,7 @@ const int MAX_NUMBERS = 100;
int numbers[MAX_NUMBERS];
int number_amount;
if (world_rank == 0) {
- // Pick a random amont of integers to send to process one
+ // Pick a random amount of integers to send to process one
srand(time(NULL));
number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
@@ -133,9 +134,9 @@ mpirun -n 2 ./probe
1 dynamically received 93 numbers from 0
```
-Although this example is trivial, `MPI_Probe` forms the basis of many dynamic MPI applications. For example, master/slave programs will often make heavy use of `MPI_Probe` when exchanging variable-sized worker messages. As an exercise, make a wrapper around `MPI_Recv` that uses `MPI_Probe` for any dynamic applications you might write. It makes the code look much nicer :-)
+Although this example is trivial, `MPI_Probe` forms the basis of many dynamic MPI applications. For example, manager/worker programs will often make heavy use of `MPI_Probe` when exchanging variable-sized worker messages. As an exercise, make a wrapper around `MPI_Recv` that uses `MPI_Probe` for any dynamic applications you might write. It makes the code look much nicer :-)
## Up next
Do you feel comfortable using the standard blocking point-to-point communication routines? If so, then you already have the ability to write endless amounts of parallel applications! Let's look at a more advanced example of using the routines you have learned. Check out [the application example using MPI_Send, MPI_Recv, and MPI_Probe]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/).
-Having trouble? Confused? Feel free to leave a comment below and perhaps I or another reader can be of help.
\ No newline at end of file
+Having trouble? Confused? Feel free to leave a comment below and perhaps I or another reader can be of help.
diff --git a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/ja_jp.md b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/ja_jp.md
new file mode 100644
index 0000000..e551213
--- /dev/null
+++ b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/ja_jp.md
@@ -0,0 +1,141 @@
+---
+layout: post
+title: MPI Probeを用いた可変長メッセージの受信 - Dynamic Receiving with MPI Probe (and MPI Status)
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Get_count, MPI_Probe
+redirect_from: '/dynamic-receiving-with-mpi-probe-and-mpi-status/'
+---
+
+[前回のレッスン]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)ではMPI_SendとMPI_Recvを使用した基本的なポイントツーポイント通信を学びました。前回はメッセージ長が固定である場合のみを説明しました。可変長メッセージを送る方法として1つ目のメッセージ長をsend/recvするという方法もとれます。しかし、MPIには追加の関数呼び出しだけで可変長のメッセージをサポートすることが可能です。このレッスンではこの方法を学びます。
+
+> **Note** - このサイトのコードはすべて[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは[tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code)にあります。
+
+## MPI_Status構造体 - The MPI_Status structure
+[前回のレッスン]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)で説明したように、`MPI_Recv`は受信したメッセージに関する情報を`MPI_Status`として受け取ります(`MPI_STATUS_IGNORE`で無視することもできます)。`MPI_Recv`関数に`MPI_Status`を渡していると受信操作が完了した後に次の3つの追加情報を得ることができます。
+
+1. **送信者のランク**: 送信者のランクは`MPI_SOURCE`構造体に格納されます。`MPI_Status stat`と宣言すると、ランクには`stat.MPI_SOURCE`でアクセスできます。
+2. **メッセージタグ**: メッセージのタグは`MPI_TAG`に格納されます。`MPI_SOURCE`と同じようにアクセスできます。
+3. **メッセージ長**: メッセージ長は、ステータス構造内には含まれません。そこで`MPI_Get_count`を使用してメッセージの長さを調べる必要があります。
+
+```cpp
+MPI_Get_count(
+ MPI_Status* status,
+ MPI_Datatype datatype,
+ int* count)
+```
+
+`MPI_Get_count`関数に`MPI_Status`渡すとメッセージの`datatype`と`count`が返されます。`count`には受信した要素の合計数が入ります。
+
+なぜこの2つの情報が必要になるのかを説明します。`MPI_Recv`受信のために`MPI_ANY_SOURCE`を送信者のランクに、`MPI_ANY_TAG`をメッセージのタグとして受信動作を行えます。この場合には`MPI_Status`が送信者とメッセージタグを知る唯一の手がかりになります(訳注:rankとtagを指定してrecvしていない場合、という意味です。)。また、`MPI_Recv`関数は引数として指定した要素数を全て受信することが保証されていないことに注意します。受信した要素数を得ることができます。しかしながら、指定した受信できる数以上の要素が送信された場合はエラーになることに注意してください。`MPI_Get_count`は実際に受信する量を決定するのに使われます。
+
+## MPI_Status構造体をクエリする例 - An example of querying the MPI_Status structure
+
+`MPI_Status`構造体をクエリするプログラム[check_status.c]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/check_status.c)を見ていきます。プログラムは数字を適当な個数送信します。受信者は送信された数字の数を調べます。コードのメイン部分は次のようになります。
+
+```cpp
+const int MAX_NUMBERS = 100;
+int numbers[MAX_NUMBERS];
+int number_amount;
+if (world_rank == 0) {
+ // プロセス1に送る数字の数を決定する
+ srand(time(NULL));
+ number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
+
+ // その分の数をプロセス1に送る
+ MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
+ printf("0 sent %d numbers to 1\n", number_amount);
+} else if (world_rank == 1) {
+ MPI_Status status;
+ // 最大でMAX_NUMBERS個のMPI_INTをプロセス0から受け取る
+ MPI_Recv(numbers, MAX_NUMBERS, MPI_INT, 0, 0, MPI_COMM_WORLD,
+ &status);
+
+ // メッセージを受信した後、そのメッセージにいくつの整数が含まれていたかを
+ // 取得する
+ MPI_Get_count(&status, MPI_INT, &number_amount);
+
+ // 含まれていた数字の数、ランク、タグを出力する。
+ printf("1 received %d numbers from 0. Message source = %d, "
+ "tag = %d\n",
+ number_amount, status.MPI_SOURCE, status.MPI_TAG);
+}
+```
+
+プロセス0は最大で`MAX_NUMBERS`個の整数をランダムに決めてプロセス1に送信します。プロセス1は最大で`MAX_NUMBERS`個の整数を読み込む`MPI_Recv`を実行します。プロセス1は`MPI_Recv` の引数として `MAX_NUMBERS` を渡しています。繰り返しになりますが、プロセス1が受け取ることができるのは**最大**この個数であることに注意してください。プロセス1では`MPI_Get_count`を呼び出して、実際に受信した`MPI_INT`の数をを調べます。プロセス1は受信したメッセージのサイズを表示すると同時に、`MPI_SOURCE`と`MPI_TAG`、つまり送信元ランクとタグも表示します。
+
+ここで注意があります。`MPI_Get_count`で得られるのはデータ型の数でバイト数でないことです。もしユーザーが`MPI_CHAR`をデータ型として(recvを)使用したとすると、返されるデータ量は4倍になります(intを4バイト、charを1バイトと仮定した場合)。このプログラムを[レポジトリ]({{ site.github.code }})の*tutorials*ディレクトリから実行すると、出力はこのようになるでしょう。
+
+```
+>>> cd tutorials
+>>> ./run.py check_status
+mpirun -n 2 ./check_status
+0 sent 92 numbers to 1
+1 received 92 numbers from 0. Message source = 0, tag = 0
+```
+
+プロセス0はプロセス1にランダムな数の整数を送信して、プロセス1は受信したメッセージに関する情報を出力できました。
+
+## MPI_Probeを使用してメッセージサイズを調べる - Using MPI_Probe to find out the message size
+`MPI_Status`への理解が深まってきました。前回の例では受信前にすべてのサイズのメッセージを処理できるように大きなバッファを用意しました。実は、`MPI_Probe`を使用すると受信前にメッセージ長を調べることができます。
+
+```cpp
+MPI_Probe(
+ int source,
+ int tag,
+ MPI_Comm comm,
+ MPI_Status* status)
+```
+
+`MPI_Probe`は`MPI_Recv`と似た呼び出しです。`MPI_Probe`は`MPI_Recv`で実際に受信する以外の処理を行うと考えて良いです。`MPI_Recv`と同様に`MPI_Probe`は指定したタグかつ指定した送信者ランクであるメッセージが来るまでブロッキングします。メッセージを受信するとstatus構造体に情報が格納されます。その後にユーザは(そのデータを受信するのに十分なバッファを用意して)`MPI_Recv`で実際にメッセージを受信すれば良いのです。
+
+[レポジトリ]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/code)の[probe.c]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/probe.c)にこの例を示します。
+
+```cpp
+int number_amount;
+if (world_rank == 0) {
+ const int MAX_NUMBERS = 100;
+ int numbers[MAX_NUMBERS];
+ // プロセス1に送る数字の数を決定する
+ srand(time(NULL));
+ number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
+
+ // その分の数をプロセス1に送る
+ MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
+ printf("0 sent %d numbers to 1\n", number_amount);
+} else if (world_rank == 1) {
+ MPI_Status status;
+ // プロセス0からのメッセージを"probe"する
+ MPI_Probe(0, 0, MPI_COMM_WORLD, &status);
+
+ // probeが完了した時、statusにはメッセージ長などの情報が含まれている
+ // MPI_Get_countを使ってメッセージ長を得る
+ MPI_Get_count(&status, MPI_INT, &number_amount);
+
+ // probeで判明したメッセージ長文のメモリを確保する
+ int* number_buf = (int*)malloc(sizeof(int) * number_amount);
+
+ // そのバッファを使用してrecv処理を行う
+ MPI_Recv(number_buf, number_amount, MPI_INT, 0, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("1 dynamically received %d numbers from 0.\n",
+ number_amount);
+ free(number_buf);
+}
+```
+
+先程の例と同様、プロセス0はプロセス1に送信する変数の数をランダムに決定します。変更点は、プロセス1が `MPI_Probe`を呼び出してプロセス0が送信しようとしている要素の数を(`MPI_Get_count`を使用して)得ることです。この後、プロセス1が適切なサイズのバッファを確保してから数値を受信します。
+
+```
+>>> ./run.py probe
+mpirun -n 2 ./probe
+0 sent 93 numbers to 1
+1 dynamically received 93 numbers from 0
+```
+
+ここで示した例はシンプルなものです。`MPI_Probe`は多くのMPIアプリケーションで用いられる基本的な機能です。例えばマネージャ/ワーカプログラムは、可変サイズのメッセージを交換する際に`MPI_Probe`を多用します。練習として、`MPI_Probe`を使用した`MPI_Recv`のラッパーを作成し、動的なアプリケーションを書いてみてください。コードの見栄えが格段に良くなるでしょう :-)
+
+## Up next
+さて、標準的なブロック型ポイント・ツー・ポイント通信に抵抗はなくなってきましたか?そうなら、あなたはすでにいくらでも並列アプリケーションを書く能力を持っています!では、学んだルーチンを使ったより高度な例を見てみましょう。[the application example using MPI_Send, MPI_Recv, and MPI_Probe]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/) をチェックしてください。
+
+困っていますか?混乱していますか?お気軽に下記にコメントを残してください。私や他の読者がお役に立てるかもしれません。
diff --git a/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/zh_cn.md b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/zh_cn.md
new file mode 100644
index 0000000..f289d30
--- /dev/null
+++ b/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/zh_cn.md
@@ -0,0 +1,181 @@
+---
+layout: post
+title: Dynamic Receiving with MPI Probe (and MPI Status)
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Get_count, MPI_Probe
+redirect_from: '/dynamic-receiving-with-mpi-probe-and-mpi-status/'
+---
+
+在 [上一节]({{ site.baseurl }}/tutorials/mpi-send-and-receive/) 中,讨论了如何使用 `MPI_Send` 和 `MPI_Recv` 执行标准的点对点通信。
+但仅仅只介绍了如何发送事先知道消息长度的消息。
+尽管可以将消息的长度作为单独的发送/接收操作发送,但是 MPI 本身仅通过几个额外的函数调用即可支持动态消息。
+在本节中,将讨论如何使用这些功能。
+
+> **注意** - 教程所涉及的所有代码都在 [GitHub 上]({{ site.github.repo }})。本文的代码在 [tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code) 下。
+
+## MPI_Status 结构体
+
+如 [上节]({{ site.baseurl }}/tutorials/mpi-send-and-receive/) 所述,`MPI_Recv` 将 `MPI_Status` 结构体的地址作为参数(可以使用 `MPI_STATUS_IGNORE` 忽略)。
+如果我们将 `MPI_Status` 结构体传递给 `MPI_Recv` 函数,则操作完成后将在该结构体中填充有关接收操作的其他信息。
+三个主要的信息包括:
+
+1. **发送端秩**. 发送端的秩存储在结构体的 `MPI_SOURCE` 元素中。也就是说,如果我们声明一个 `MPI_Status stat` 变量,则可以通过 `stat.MPI_SOURCE` 访问秩。
+2. **消息的标签**. 消息的标签可以通过结构体的 `MPI_TAG` 元素访问(类似于 `MPI_SOURCE`)。
+3. **消息的长度**. 消息的长度在结构体中没有预定义的元素。相反,我们必须使用 `MPI_Get_count` 找出消息的长度。
+
+```cpp
+MPI_Get_count(
+ MPI_Status* status,
+ MPI_Datatype datatype,
+ int* count)
+```
+
+在 `MPI_Get_count` 函数中,使用者需要传递 `MPI_Status` 结构体,消息的 `datatype`(数据类型),并返回 `count`。
+变量 `count` 是已接收的 `datatype` 元素的数目。
+
+为什么需要这些信息?
+事实证明,`MPI_Recv` 可以将 `MPI_ANY_SOURCE` 用作发送端的秩,将 `MPI_ANY_TAG` 用作消息的标签。
+在这种情况下,`MPI_Status` 结构体是找出消息的实际发送端和标签的唯一方法。
+此外,并不能保证 `MPI_Recv` 能够接收函数调用参数的全部元素。
+相反,它只接收已发送给它的元素数量(如果发送的元素多于所需的接收数量,则返回错误。)
+`MPI_Get_count` 函数用于确定实际的接收量。
+
+## `MPI_Status` 结构体查询的示例
+
+查询 `MPI_Status` 结构体的程序在 [check_status.c]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code/check_status.c) 中。
+程序将随机数量的数字发送给接收端,然后接收端找出发送了多少个数字。
+代码的主要部分如下所示。
+
+```cpp
+const int MAX_NUMBERS = 100;
+int numbers[MAX_NUMBERS];
+int number_amount;
+if (world_rank == 0) {
+ // Pick a random amount of integers to send to process one
+ srand(time(NULL));
+ number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
+
+ // Send the amount of integers to process one
+ MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
+ printf("0 sent %d numbers to 1\n", number_amount);
+} else if (world_rank == 1) {
+ MPI_Status status;
+ // Receive at most MAX_NUMBERS from process zero
+ MPI_Recv(numbers, MAX_NUMBERS, MPI_INT, 0, 0, MPI_COMM_WORLD,
+ &status);
+
+ // After receiving the message, check the status to determine
+ // how many numbers were actually received
+ MPI_Get_count(&status, MPI_INT, &number_amount);
+
+ // Print off the amount of numbers, and also print additional
+ // information in the status object
+ printf("1 received %d numbers from 0. Message source = %d, "
+ "tag = %d\n",
+ number_amount, status.MPI_SOURCE, status.MPI_TAG);
+}
+```
+
+如我们所见,进程 0 将最多 `MAX_NUMBERS` 个整数以随机数量发送到进程 1。
+进程 1 然后调用 `MPI_Recv` 以获取总计 `MAX_NUMBERS` 个整数。
+尽管进程 1 以 `MAX_NUMBERS` 作为 `MPI_Recv` 函数参数,但进程 1 将最多接收到此数量的数字。
+在代码中,进程 1 使用 `MPI_INT` 作为数据类型的参数,调用 `MPI_Get_count`,以找出实际接收了多少个整数。
+除了打印出接收到的消息的大小外,进程 1 还通过访问 status 结构体的 `MPI_SOURCE` 和 `MPI_TAG` 元素来打印消息的来源和标签。
+
+为了澄清起见,`MPI_Get_count` 的返回值是相对于传递的数据类型而言的。
+如果用户使用 `MPI_CHAR` 作为数据类型,则返回的数量将是原来的四倍(假设整数是四个字节,而 char 是一个字节)。
+如果你从 [库]({{ site.github.code }}) 的 *tutorials* 目录中运行 check_status 程序,则输出应类似于:
+
+```
+>>> cd tutorials
+>>> ./run.py check_status
+mpirun -n 2 ./check_status
+0 sent 92 numbers to 1
+1 received 92 numbers from 0. Message source = 0, tag = 0
+```
+
+正如预期的那样,进程 0 将随机数目的整数发送给进程 1,进程 1 将打印出接收到的消息的有关信息。
+
+## 使用 `MPI_Probe` 找出消息大小
+
+现在您了解了 `MPI_Status` 的工作原理,现在我们可以使用它来发挥更高级的优势。
+除了传递接收消息并简易地配备一个很大的缓冲区来为所有可能的大小的消息提供处理(就像我们在上一个示例中所做的那样),您可以使用 `MPI_Probe` 在实际接收消息之前查询消息大小。
+函数原型看起来像这样:
+
+```cpp
+MPI_Probe(
+ int source,
+ int tag,
+ MPI_Comm comm,
+ MPI_Status* status)
+```
+
+`MPI_Probe` 看起来与 `MPI_Recv` 非常相似。
+实际上,您可以将 `MPI_Probe` 视为 `MPI_Recv`,除了不接收消息外,它们执行相同的功能。
+与 `MPI_Recv` 类似,`MPI_Probe` 将阻塞具有匹配标签和发送端的消息。
+当消息可用时,它将填充 status 结构体。
+然后,用户可以使用 `MPI_Recv` 接收实际的消息。
+
+[教程代码]({{ site.github.code }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/code) 在 probe.c 中有一个示例。
+以下是源代码的主要部分
+
+```cpp
+int number_amount;
+if (world_rank == 0) {
+ const int MAX_NUMBERS = 100;
+ int numbers[MAX_NUMBERS];
+ // Pick a random amount of integers to send to process one
+ srand(time(NULL));
+ number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
+
+ // Send the random amount of integers to process one
+ MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
+ printf("0 sent %d numbers to 1\n", number_amount);
+} else if (world_rank == 1) {
+ MPI_Status status;
+ // Probe for an incoming message from process zero
+ MPI_Probe(0, 0, MPI_COMM_WORLD, &status);
+
+ // When probe returns, the status object has the size and other
+ // attributes of the incoming message. Get the message size
+ MPI_Get_count(&status, MPI_INT, &number_amount);
+
+ // Allocate a buffer to hold the incoming numbers
+ int* number_buf = (int*)malloc(sizeof(int) * number_amount);
+
+ // Now receive the message with the allocated buffer
+ MPI_Recv(number_buf, number_amount, MPI_INT, 0, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("1 dynamically received %d numbers from 0.\n",
+ number_amount);
+ free(number_buf);
+}
+```
+
+与上一个示例类似,进程 0 选择随机数量的数字发送给进程 1。
+不同之处在于,进程 1 现在调用 `MPI_Probe`,以找出进程 0 试图发送多少个元素(利用 `MPI_Get_count`)。
+然后,进程 1 分配适当大小的缓冲区并接收数字。
+执行本示例代码,结果看起来类似于:
+
+```
+>>> ./run.py probe
+mpirun -n 2 ./probe
+0 sent 93 numbers to 1
+1 dynamically received 93 numbers from 0
+```
+
+尽管这个例子很简单,但是 `MPI_Probe` 构成了许多动态 MPI 应用程序的基础。
+例如,控制端/执行子程序在交换变量大小的消息时通常会大量使用 `MPI_Probe`。
+作为练习,对 `MPI_Recv` 进行包装,将 `MPI_Probe` 用于您可能编写的任何动态应用程序。
+它将使代码看起来更美好:-)
+
+## 接下来
+
+对于使用标准的阻塞点对点通信例程的理解是否清晰?
+如果是的,那么您已经有能力编写无数的并行应用程序!
+让我们来看一个使用所学例程的高级示例。
+查看 [使用 `MPI_Send`,`MPI_Recv` 和 `MPI_Probe` 的应用程序示例]({{ site.baseurl }}/tutorials/point-to-point-communication-application-random-walk/).
+
+遇到麻烦? 困惑?
+随时在下面发表评论,也许我或其他读者会有所帮助。
diff --git a/tutorials/installing-mpich2/index.md b/tutorials/installing-mpich2/index.md
index a7cd9f5..92e032e 100644
--- a/tutorials/installing-mpich2/index.md
+++ b/tutorials/installing-mpich2/index.md
@@ -4,53 +4,54 @@ title: Installing MPICH2 on a Single Machine
author: Wes Kendall
categories: Beginner MPI
tags:
+translations: zh_cn,ja_jp
redirect_from: '/installing-mpich2/'
---
-MPI is simply a standard which others follow in their implementation. Because of this, there are a wide variety of MPI implementations out there. One of the most popular implementations, MPICH2, will be used for all of the examples provided through this site. Users are free to use any implementation they wish, but only instructions for installing MPICH2 will be provided. Furthermore, the scripts and code provided for the lessons are only guaranteed to execute and run with the lastest version of MPICH2.
+MPI is simply a standard which others follow in their implementation. Because of this, there are a wide variety of MPI implementations out there. One of the most popular implementations, MPICH, will be used for all of the examples provided through this site. Users are free to use any implementation they wish, but only instructions for installing MPICH will be provided. Furthermore, the scripts and code provided for the lessons are only guaranteed to execute and run with the lastest version of MPICH.
-MPICH2 is a widely-used implementation of MPI that is developed primarily by Argonne National Laboratory in the United States. The main reason for choosing MPICH2 over other implementations is simply because of my familiarity with the interface and because of my close relationship with Argonne National Laboratory. I also encourage others to check out OpenMPI, which is also a widely-used implementation.
+MPICH is a widely-used implementation of MPI that is developed primarily by Argonne National Laboratory in the United States. The main reason for choosing MPICH over other implementations is simply because of my familiarity with the interface and because of my close relationship with Argonne National Laboratory. I also encourage others to check out [OpenMPI](https://www.open-mpi.org/), which is also a widely-used implementation.
-## Installing MPICH2
-The latest version of MPICH2 is available [here](http://www.mcs.anl.gov/research/projects/mpich2/). The version that I will be using for all of the examples on the site is 1.4, which was released June 16, 2011. Go ahead and download the source code, uncompress the folder, and change into the MPICH2 directory.
+## Installing MPICH
+The latest version of MPICH is available [here](https://www.mpich.org/). The version that I will be using for all of the examples on the site is 3.3-2, which was released 13 November 2019. Go ahead and download the source code, uncompress the folder, and change into the MPICH directory.
```
->>> tar -xzf mpich2-1.4.tar.gz
->>> cd mpich2-1.4
+>>> tar -xzf mpich-3-3.2.tar.gz
+>>> cd mpich-3-3.2
```
-Once doing this, you should be able to configure your installation by performing `./configure`. I added a couple of parameters to my configuration to avoid building the MPI Fortran library. If you need to install MPICH2 to a local directory (for example, if you don't have root access to your machine), type `./configure --prefix=/installation/directory/path` For more information about possible configuration parameters, type `./configure --help`
+Once doing this, you should be able to configure your installation by performing `./configure`. If you need to install MPICH to a local directory (for example, if you don't have root access to your machine), type `./configure --prefix=/installation/directory/path`. It is possible to avoid building the MPI Fortran library by using `./configure --disable-fortran` if you do not have Fortran compilers. For more information about possible configuration parameters, type `./configure --help`
```
->>> ./configure --disable-f77 --disable-fc
-Configuring MPICH2 version 1.4 with '--disable-f77' '--disable-fc'
-Running on system: Darwin Wes-Kendalls-Macbook-Pro.local 10.7.0 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu1504.9.37~1/RELEASE_I386 i386
-checking for gcc... gcc
+>>> ./configure
+Configuring MPICH version 3.3.2
+Running on system: Linux localhost.localdomain 5.8.18-100.fc31.x86_64 #1 SMP Mon Nov 2 20:32:55 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
+checking build system type... x86_64-unknown-linux-gnu
```
When configuration is done, it should say *"Configuration completed."* Once this is through, it is time to build and install MPICH2 with `make; sudo make install`.
```
>>> make; sudo make install
-Beginning make
-Using variables CC='gcc' CFLAGS=' -O2' LDFLAGS=' ' F77='' FFLAGS=' ' FC='' FCFLAGS=' ' CXX='c++' CXXFLAGS=' -O2' AR='ar' CPP='gcc-E' CPP
+make
+make all-recursive
+
```
-If your build was successful, you should be able to type `mpich2version` and see something similar to this.
+If your build was successful, you should be able to type `mpiexec --version` and see something similar to this.
```
->>> mpich2version
-MPICH2 Version: 1.4
-MPICH2 Release date: Thu Jun 16 16:41:08 CDT 2011
-MPICH2 Device: ch3:nemesis
-MPICH2 configure: --disable-f77 --disable-fc
-MPICH2 CC: gcc -O2
-MPICH2 CXX: c++ -O2
-MPICH2 F77:
-MPICH2 FC:
+>>> mpiexec --version
+HYDRA build details:
+ Version: 3.3.2
+ Release Date: Tue Nov 12 21:23:16 CST 2019
+ CC: gcc
+ CXX: g++
+ F77: gfortran
+ F90: gfortran
```
-Hopefully your build finished successfully. If not, you may have issues with missing dependencies. For any issue, I highly recommend copying and pasting the error message directly into Google.
+Hopefully your build finished successfully. If not, you may have issues with missing dependencies. For any issue, I highly recommend copying and pasting the error message directly into Google.
## Up next
-Now that you have built MPICH2 locally, you have a couple options of where you can proceed on this site. If you don't have access to a cluster or want to learn more about building a virtual MPI cluster, check out the lesson about [building and running your own cluster on Amazon EC2]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/). If you already have your own cluster or simply want to run the rest of the lessons from your machine, proceed to the [MPI hello world lesson]({{ site.baseurl }}/tutorials/mpi-hello-world/), which provides an overview of the basics of programming and running your first MPI program.
\ No newline at end of file
+Now that you have built MPICH locally, you have some options of where you can proceed on this site. If you already have the hardware and resources to setup a local cluster, I suggest you proceed to the tutorial about [running an MPI cluster in LAN]({{ site.baseurl }}/tutorials/running-an-mpi-cluster-within-a-lan). If you don't have access to a cluster or want to learn more about building a virtual MPI cluster, check out the lesson about [building and running your own cluster on Amazon EC2]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/). If you have built a cluster in either way or simply want to run the rest of the lessons from your machine, proceed to the [MPI hello world lesson]({{ site.baseurl }}/tutorials/mpi-hello-world/), which provides an overview of the basics of programming and running your first MPI program.
diff --git a/tutorials/installing-mpich2/ja_jp.md b/tutorials/installing-mpich2/ja_jp.md
new file mode 100644
index 0000000..e73efc5
--- /dev/null
+++ b/tutorials/installing-mpich2/ja_jp.md
@@ -0,0 +1,55 @@
+---
+layout: post
+title: MPIをシングルマシンにインストールする - Installing MPICH2 on a Single Machine
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/installing-mpich2/'
+---
+
+MPIとは標準仕様を指すものであるのでMPIの実装は複数存在します。このレッスンでは主な実装の1つであるMPICHを使用します。あなたが望むなら他の実装を使用しても良いですが、このレッスンではMPICHインストール手順を紹介します。また、チュートリアル全体で提供されるスクリプトとコードは、最新バージョンのMPICHでの実行だけを確認しています。
+
+MPICHは米国のアルゴンヌ国立研究所が主に開発したメジャーなMPI実装です。MPICHを選択した理由は、私がこのインターフェイスに精通しておりアルゴンヌ国立研究所と縁が深いためです。広く使用されている実装である[OpenMPI](https://www.open-mpi.org/)もぜひ調べてみてください。
+
+## MPICHのインストール - Installing MPICH
+[ここ](https://www.mpich.org/)からMPICHの最新バージョンを入手できます。このチュートリアルでは3.3-2(2019年11月13日リリース)を利用します。tar.gzファイルをダウンロードし以下のように伸長します。
+
+```
+>>> tar -xzf mpich-3-3.2.tar.gz
+>>> cd mpich-3-3.2
+```
+
+`./configure`でmakeの準備をします。この際にマシンの特権がなくユーザディレクトリにインストールしたい場合は`./configure --prefix=/installation/directory/path`としてインストールディレクトリを指定できます。Fortranへの対応が不要である場合は`./configure --disable-fortran`とします。利用可能なオプションを全て表示するには`./configure --help`とします。
+
+```
+>>> ./configure
+Configuring MPICH version 3.3.2
+Running on system: Linux localhost.localdomain 5.8.18-100.fc31.x86_64 #1 SMP Mon Nov 2 20:32:55 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
+checking build system type... x86_64-unknown-linux-gnu
+```
+
+configureが終了して*"Configuration completed."*と表示さたら`make; sudo make install`を使用して MPICH2 をビルド・インストールします。
+```
+>>> make; sudo make install
+make
+make all-recursive
+
+```
+
+成功したなら`mpiexec --version`でインストールした情報を出力できます。
+
+```
+>>> mpiexec --version
+HYDRA build details:
+ Version: 3.3.2
+ Release Date: Tue Nov 12 21:23:16 CST 2019
+ CC: gcc
+ CXX: g++
+ F77: gfortran
+ F90: gfortran
+```
+
+皆さんの環境でもビルドが無事に完了することを祈りますが、依存関係不足などで問題が起こる可能性があるでしょう。このような場合はエラーメッセージをGoogleで検索してみてください。
+
+## 次は?
+単一の環境にMPICHを構築できたのでこのサイトで次に進むための選択肢がいくつかあります。ローカルクラスタをセットアップするためのハードウェアとリソースがすでにある場合は、[running an MPI cluster in LAN]({{ site.baseurl }}/tutorials/running-an-mpi-cluster-within-a-lan)に進むことを推奨します。そのようなクラスタにアクセスできない場合や仮想 MPIクラスターの構築について詳しく知りたい場合は、[building and running your own cluster on Amazon EC2]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/)に進んでください。いずれかの方法でクラスターを構築し終わっていたり、残りのレッスンをスタンドアロンで実行する場合は[MPI hello world lesson]({{ site.baseurl }}/tutorials/mpi-hello-world/)に進んでください。Hello Worldではプログラミングの基礎と最初のMPIプログラムの実行の概要が説明されています。
\ No newline at end of file
diff --git a/tutorials/installing-mpich2/zh_cn.md b/tutorials/installing-mpich2/zh_cn.md
new file mode 100644
index 0000000..cb6abac
--- /dev/null
+++ b/tutorials/installing-mpich2/zh_cn.md
@@ -0,0 +1,73 @@
+---
+layout: post
+title: Installing MPICH2 on a Single Machine
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/installing-mpich2/'
+---
+
+MPI 只是众多实现中所遵循的一个标准。
+因此,这里有各种各样的 MPI 实现。
+本站点提供的所有示例都将使用最受欢迎的实现之一 MPICH。
+用户可以自由使用他们希望的任何实现,但是仅提供安装 MPICH 的说明。
+此外,仅保证为课程提供的脚本和代码可以在最新版本的 MPICH 上执行和运行。
+
+MPICH 是 MPI 的一种广泛使用的实现,主要由美国的 Argonne 国家实验室开发。
+选择 MPICH 而不是其他实现的主要原因是由于我对界面的熟悉以及与 Argonne 国家实验室的密切关系。
+我还鼓励其他人使用 OpenMPI,这也是一种广泛使用的实现。
+
+## Installing MPICH
+
+MPICH 的最新版本可在 [此处](https://www.mpich.org/) 获取。
+网站上所有示例的版本是 3.3-2,该版本于 2019 年 9 月 13 日发布。
+下载源代码,解压缩文件夹,然后切换到 MPICH 目录。
+
+```
+>>> tar -xzf mpich3-3.2.tar.gz
+>>> cd mpich3-3.2
+```
+
+完成之后,您应该能够通过执行 `./configure` 来配置安装。
+如果需要将 MPICH 安装到本地目录(例如,如果您没有对计算机的 root 访问权限),请使用 `./configure --prefix=/installation/directory/path`,如果您没有 Fortran 编译器,则可以通过使用 `./configure --disable-fortran` 来避免构建 MPI Fortran 库。输入 `./configure --help` 以获取有关可能的配置参数的更多信息。
+
+```
+>>> ./configure --disable-fortran
+Configuring MPICH version 3.3.2
+Running on system: Linux localhost.localdomain 5.8.18-100.fc31.x86_64 #1 SMP Mon Nov 2 20:32:55 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
+checking build system type... x86_64-unknown-linux-gnu
+```
+
+配置完成后,应显示 *"Configuration completed."*
+一旦完成,就该使用`make; sudo make install`命令来构建和安装 MPICH2 了。
+
+```
+>>> make; sudo make install
+make
+make all-recursive
+
+```
+
+如果构建成功,则应该可以输入`mpiexec --version`并看到以下类似的内容。
+
+```
+>>> mpiexec --version
+HYDRA build details:
+ Version: 3.3.2
+ Release Date: Tue Nov 12 21:23:16 CST 2019
+ CC: gcc
+ CXX: g++
+ F77: gfortran
+ F90: gfortran
+```
+
+希望您的构建成功完成。
+如果没有,您可能会遇到缺少依赖项的问题。
+对于任何问题,我强烈建议您将错误消息直接复制并粘贴到 Google 中。
+
+## 下一步
+
+现在,您已经在本地构建了 MPICH,您可以在该站点上进行一些选择。
+如果您已经具有用于设置本地集群的硬件和资源,建议您继续阅读有关 [在 LAN 中运行 MPI 集群]({{ site.baseurl }}/tutorials/running-an-mpi-cluster-within-a-lan) 的教程。
+如果您无权访问集群或想了解有关构建虚拟化 MPI 集群的更多信息,请阅读有关 [在 Amazon EC2 上构建和运行自己的集群]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/)。
+如果您以任何一种方式构建了集群,或者只是想从计算机上运行其余课程,请继续阅读 [MPI hello world 课程]({{ site.baseurl }}/tutorials/mpi-hello-world/),其中概述了编程和运行第一个 MPI 程序的基础知识。
diff --git a/tutorials/introduction-to-groups-and-communicators/code/comm_groups.c b/tutorials/introduction-to-groups-and-communicators/code/comm_groups.c
new file mode 100644
index 0000000..e29edf9
--- /dev/null
+++ b/tutorials/introduction-to-groups-and-communicators/code/comm_groups.c
@@ -0,0 +1,55 @@
+// Author: Wesley Bland
+// Copyright 2015 www.mpitutorial.com
+// This code is provided freely with the tutorials on mpitutorial.com. Feel
+// free to modify it for your own use. Any distribution of the code must
+// either provide a link to www.mpitutorial.com or keep this header intact.
+//
+// Example using MPI_Comm_split to divide a communicator into subcommunicators
+//
+#include
+#include
+#include
+
+int main(int argc, char **argv) {
+ MPI_Init(NULL, NULL);
+
+ // Get the rank and size in the original communicator
+ int world_rank, world_size;
+ MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+ MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+ // Get the group of processes in MPI_COMM_WORLD
+ MPI_Group world_group;
+ MPI_Comm_group(MPI_COMM_WORLD, &world_group);
+
+ int n = 7;
+ const int ranks[7] = {1, 2, 3, 5, 7, 11, 13};
+
+ // Construct a group containing all of the prime ranks in world_group
+ MPI_Group prime_group;
+ MPI_Group_incl(world_group, 7, ranks, &prime_group);
+
+ // Create a new communicator based on the group
+ MPI_Comm prime_comm;
+ MPI_Comm_create_group(MPI_COMM_WORLD, prime_group, 0, &prime_comm);
+
+ int prime_rank = -1, prime_size = -1;
+ // If this rank isn't in the new communicator, it will be MPI_COMM_NULL
+ // Using MPI_COMM_NULL for MPI_Comm_rank or MPI_Comm_size is erroneous
+ if (MPI_COMM_NULL != prime_comm) {
+ MPI_Comm_rank(prime_comm, &prime_rank);
+ MPI_Comm_size(prime_comm, &prime_size);
+ }
+
+ printf("WORLD RANK/SIZE: %d/%d --- PRIME RANK/SIZE: %d/%d\n",
+ world_rank, world_size, prime_rank, prime_size);
+
+ MPI_Group_free(&world_group);
+ MPI_Group_free(&prime_group);
+
+ if (MPI_COMM_NULL != prime_comm) {
+ MPI_Comm_free(&prime_comm);
+ }
+
+ MPI_Finalize();
+}
diff --git a/tutorials/introduction-to-groups-and-communicators/code/comm_split.c b/tutorials/introduction-to-groups-and-communicators/code/comm_split.c
new file mode 100644
index 0000000..3693e83
--- /dev/null
+++ b/tutorials/introduction-to-groups-and-communicators/code/comm_split.c
@@ -0,0 +1,38 @@
+// Author: Wesley Bland
+// Copyright 2015 www.mpitutorial.com
+// This code is provided freely with the tutorials on mpitutorial.com. Feel
+// free to modify it for your own use. Any distribution of the code must
+// either provide a link to www.mpitutorial.com or keep this header intact.
+//
+// Example using MPI_Comm_split to divide a communicator into subcommunicators
+//
+
+#include
+#include
+#include
+
+int main(int argc, char **argv) {
+ MPI_Init(NULL, NULL);
+
+ // Get the rank and size in the original communicator
+ int world_rank, world_size;
+ MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+ MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+ int color = world_rank / 4; // Determine color based on row
+
+ // Split the communicator based on the color and use the original rank for ordering
+ MPI_Comm row_comm;
+ MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &row_comm);
+
+ int row_rank, row_size;
+ MPI_Comm_rank(row_comm, &row_rank);
+ MPI_Comm_size(row_comm, &row_size);
+
+ printf("WORLD RANK/SIZE: %d/%d --- ROW RANK/SIZE: %d/%d\n",
+ world_rank, world_size, row_rank, row_size);
+
+ MPI_Comm_free(&row_comm);
+
+ MPI_Finalize();
+}
diff --git a/tutorials/introduction-to-groups-and-communicators/code/makefile b/tutorials/introduction-to-groups-and-communicators/code/makefile
new file mode 100644
index 0000000..6ecc3dc
--- /dev/null
+++ b/tutorials/introduction-to-groups-and-communicators/code/makefile
@@ -0,0 +1,13 @@
+EXECS=comm_groups comm_split
+MPICC?=mpicc
+
+all: ${EXECS}
+
+split: comm_split.c
+ ${MPICC} -o comm_split comm_split.c
+
+groups: comm_groups.c
+ ${MPICC} -o comm_groups comm_groups.c
+
+clean:
+ rm -f ${EXECS}
diff --git a/tutorials/introduction-to-groups-and-communicators/comm_split.png b/tutorials/introduction-to-groups-and-communicators/comm_split.png
new file mode 100644
index 0000000..ed55ef7
Binary files /dev/null and b/tutorials/introduction-to-groups-and-communicators/comm_split.png differ
diff --git a/tutorials/introduction-to-groups-and-communicators/comm_split.pptx b/tutorials/introduction-to-groups-and-communicators/comm_split.pptx
new file mode 100644
index 0000000..1516925
Binary files /dev/null and b/tutorials/introduction-to-groups-and-communicators/comm_split.pptx differ
diff --git a/tutorials/introduction-to-groups-and-communicators/groups.png b/tutorials/introduction-to-groups-and-communicators/groups.png
new file mode 100644
index 0000000..7710e81
Binary files /dev/null and b/tutorials/introduction-to-groups-and-communicators/groups.png differ
diff --git a/tutorials/introduction-to-groups-and-communicators/index.md b/tutorials/introduction-to-groups-and-communicators/index.md
new file mode 100644
index 0000000..5fd6456
--- /dev/null
+++ b/tutorials/introduction-to-groups-and-communicators/index.md
@@ -0,0 +1,206 @@
+---
+layout: post
+title: Introduction to Groups and Communicators
+author: Wesley Bland
+categories: Advanced MPI
+translations: zh_cn,ja_jp
+tags: MPI_Group, MPI_Comm
+redirect_from: '/introduction-to-groups-and-communicators/'
+---
+
+In all previous tutorials, we have used the communicator `MPI_COMM_WORLD`. For simple applications, this is sufficient as we have a relatively small number of processes and we usually either want to talk to one of them at a time or all of them at a time. When applications start to get bigger, this becomes less practical and we may only want to talk to a few processes at once. In this lesson, we show how to create new communicators to communicate with a subset of the original group of processes at once.
+
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/introduction-to-groups-and-communicators/code]({{ site.github.code }}/tutorials/introduction-to-groups-and-communicators/code).
+
+## Overview of communicators
+As we have seen when learning about collective routines, MPI allows you to talk to all processes in a communicator at once to do things like distribute data from one process to many processes using `MPI_Scatter` or perform a data reduction using `MPI_Reduce`. However, up to now, we have only used the default communicator, `MPI_COMM_WORLD`.
+
+For simple applications, it's not unusual to do everything using `MPI_COMM_WORLD`, but for more complex use cases, it might be helpful to have more communicators. An example might be if you wanted to perform calculations on a subset of the processes in a grid. For instance, all processes in each row might want to sum a value. This brings us to the first and most common function used to create new communicators:
+
+```cpp
+MPI_Comm_split(
+ MPI_Comm comm,
+ int color,
+ int key,
+ MPI_Comm* newcomm)
+```
+
+As the name implies, `MPI_Comm_split` creates new communicators by "splitting" a communicator into a group of sub-communicators based on the input values `color` and `key`. It's important to note here that the original communicator doesn't go away, but a new communicator is created on each process. The first argument, `comm`, is the communicator that will be used as the basis for the new communicators. This could be `MPI_COMM_WORLD`, but it could be any other communicator as well. The second argument, `color`, determines to which new communicator each processes will belong. All processes which pass in the same value for `color` are assigned to the same communicator. If the `color` is `MPI_UNDEFINED`, that process won't be included in any of the new communicators. The third argument, `key`, determines the ordering (rank) within each new communicator. The process which passes in the smallest value for `key` will be rank 0, the next smallest will be rank 1, and so on. If there is a tie, the process that had the lower rank in the original communicator will be first. The final argument, `newcomm` is how MPI returns the new communicator back to the user.
+
+## Example of using multiple communicators
+
+Now let's look at a simple example where we attempt to split a single global communicator into a set of smaller communicators. In this example, we'll imagine that we've logically laid out our original communicator into a 4x4 grid of 16 processes and we want to divide the grid by row. To do this, each row will get its own color. In the image below, you can see how each group of processes with the same color on the left ends up in its own communicator on the right.
+
+
+
+Let's look at the code for this.
+
+```cpp
+// Get the rank and size in the original communicator
+int world_rank, world_size;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+int color = world_rank / 4; // Determine color based on row
+
+// Split the communicator based on the color and use the
+// original rank for ordering
+MPI_Comm row_comm;
+MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &row_comm);
+
+int row_rank, row_size;
+MPI_Comm_rank(row_comm, &row_rank);
+MPI_Comm_size(row_comm, &row_size);
+
+printf("WORLD RANK/SIZE: %d/%d \t ROW RANK/SIZE: %d/%d\n",
+ world_rank, world_size, row_rank, row_size);
+
+MPI_Comm_free(&row_comm);
+```
+
+The first few lines get the rank and size for the original communicator, `MPI_COMM_WORLD`. The next line does the important operation of determining the "color" of the local process. Remember that color decides to which communicator the process will belong after the split. Next, we see the all important split operation. The new thing here is that we're using the original rank (`world_rank`) as the key for the split operation. Since we want all of the processes in the new communicator to be in the same order that they were in the original communicator, using the original rank value makes the most sense here as it will already be ordered correctly. After that, we print out the new rank and size just to make sure it works. Your output should look something like this:
+
+```
+WORLD RANK/SIZE: 0/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 1/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 2/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 3/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 4/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 5/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 6/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 7/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 8/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 9/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 10/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 11/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 12/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 13/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 14/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 15/16 ROW RANK/SIZE: 3/4
+```
+
+Don't be alarmed if yours isn't in the right order. When you print things out in an MPI program, each process has to send its output back to the place where you launched your MPI job before it can be printed to the screen. This tends to mean that the ordering gets jumbled so you can't ever assume that just because you print things in a specific rank order, that the output will actually end up in the same order you expect. The output was just rearranged here to look nice.
+
+Finally, we free the communicator with `MPI_Comm_free`. This seems like it's not an important step, but it's just as important as freeing your memory when you're done with it in any other program. When an MPI object will no longer be used, it should be freed so it can be reused later. MPI has a limited number of objects that it can create at a time and not freeing your objects could result in a runtime error if MPI runs out of allocatable objects.
+
+## Other communicator creation functions
+
+While `MPI_Comm_split` is the most common communicator creation function, there are many others. `MPI_Comm_dup` is the most basic and creates a duplicate of a communicator. It may seem odd that there would exist a function that only creates a copy, but this is very useful for applications which use libraries to perform specialized functions, such as mathematical libraries. In these kinds of applications, it's important that user codes and library codes do not interfere with each other. To avoid this, the first thing every application should do is to create a duplicate of `MPI_COMM_WORLD`, which will avoid the problem of other libraries also using `MPI_COMM_WORLD`. The libraries themselves should also make duplicates of `MPI_COMM_WORLD` to avoid the same problem.
+
+Another function is `MPI_Comm_create`. At first glance, this function looks very similar to `MPI_Comm_create_group`. Its signature is almost identical:
+
+```cpp
+MPI_Comm_create(
+ MPI_Comm comm,
+ MPI_Group group,
+ MPI_Comm* newcomm)
+```
+The key difference however (besides the lack of the `tag` argument), is that `MPI_Comm_create_group` is only collective over the group of processes contained in `group`, where `MPI_Comm_create` is collective over every process in `comm`. This is an important distinction as the size of communicators grows very large. If trying to create a subset of `MPI_COMM_WORLD` when running with 1,000,000 processes, it's important to perform the operation with as few processes as possible as the collective becomes very expensive at large sizes.
+
+There are other more advanced features of communicators that we do not cover here, such as the differences between inter-communicators and intra-communicators and other advanced communicator creation functions. These are only used in very specific kinds of applications which may be covered in a future tutorial.
+
+## Overview of groups
+
+While `MPI_Comm_split` is the simplest way to create a new communicator, it isn't the only way to do so. There are more flexible ways to create communicators, but they use a new kind of MPI object, `MPI_Group`. Before going into lots of detail about groups, let's look a little more at what a communicator actually is. Internally, MPI has to keep up with (among other things) two major parts of a communicator, the context (or ID) that differentiates one communicator from another and the group of processes contained by the communicator. The context is what prevents an operation on one communicator from matching with a similar operation on another communicator. MPI keeps an ID for each communicator internally to prevent the mixups. The group is a little simpler to understand since it is just the set of all processes in the communicator. For `MPI_COMM_WORLD`, this is all of the processes that were started by `mpiexec`. For other communicators, the group will be different. In the example code above, the group is all of the processes which passed in the same `color` to `MPI_Comm_split`.
+
+MPI uses these groups in the same way that set theory generally works. You don't have to be familiar with all of set theory to understand things, but it's helpful to know what two operations mean. Here, instead of referring to "sets", we'll use the term "groups" as it applies to MPI. First, the union operation creates a new, (potentially) bigger set from two other sets. The new set includes all of the members of the first two sets (without duplicates). Second, the intersection operation creates a new, (potentially) smaller set from two other sets. The new set includes all of the members that are present in both of the original sets. You can see examples of both of these operations graphically below.
+
+
+
+In the first example, the union of the two groups `{0, 1, 2, 3}` and `{2, 3, 4, 5}` is `{0, 1, 2, 3, 4, 5}` because each of those items appears in each group. In the second example, the intersection of the two groups `{0, 1, 2, 3}`, and `{2, 3, 4, 5}` is `{2, 3}` because only those items appear in each group.
+
+## Using MPI groups
+
+Now that we understand the fundamentals of how groups work, let's see how they can be applied to MPI operations. In MPI, it's easy to get the group of processes in a communicator with the API call, `MPI_Comm_group`.
+
+```cpp
+MPI_Comm_group(
+ MPI_Comm comm,
+ MPI_Group* group)
+```
+
+As mentioned above, a communicator contains a context, or ID, and a group. Calling `MPI_Comm_group` gets a reference to that group object. The group object works the same way as a communicator object except that you can't use it to communicate with other ranks (because it doesn't have that context attached). You can still get the rank and size for the group (`MPI_Group_rank` and `MPI_Group_size`, respectively). However, what you can do with groups that you can't do with communicators is use it to construct new groups locally. It's important to remember here the difference between a local operation and a remote one. A remote operation involves communication with other ranks where a local operation does not. Creating a new communicator is a remote operation because all processes need to decide on the same context and group, where creating a group is local because it isn't used for communication and therefore doesn't need to have the same context for each process. You can manipulate a group all you like without performing any communication at all.
+
+Once you have a group or two, performing operations on them is straightforward. Getting the union looks like this:
+
+```cpp
+MPI_Group_union(
+ MPI_Group group1,
+ MPI_Group group2,
+ MPI_Group* newgroup)
+```
+
+And you can probably guess that the intersection looks like this:
+
+```cpp
+MPI_Group_intersection(
+ MPI_Group group1,
+ MPI_Group group2,
+ MPI_Group* newgroup)
+```
+
+In both cases, the operation is performed on `group1` and `group2` and the result is stored in `newgroup`.
+
+There are many uses of groups in MPI. You can compare groups to see if they are the same, subtract one group from another, exclude specific ranks from a group, or use a group to translate the ranks of one group to another group. However, one of the recent additions to MPI that tends to be most useful is `MPI_Comm_create_group`. This is a function to create a new communicator, but instead of doing calculations on the fly to decide the makeup, like `MPI_Comm_split`, this function takes an `MPI_Group` object and creates a new communicator that has all of the same processes as the group.
+
+```cpp
+MPI_Comm_create_group(
+ MPI_Comm comm,
+ MPI_Group group,
+ int tag,
+ MPI_Comm* newcomm)
+```
+
+## Example of using groups
+
+Let's look at a quick example of what using groups looks like. Here, we'll use another new function which allows you to pick specific ranks in a group and construct a new group containing only those ranks, `MPI_Group_incl`.
+
+```cpp
+MPI_Group_incl(
+ MPI_Group group,
+ int n,
+ const int ranks[],
+ MPI_Group* newgroup)
+```
+
+With this function, `newgroup` contains the processes in `group` with ranks contained in `ranks`, which is of size `n`. Want to see how that works? Let's try creating a communicator which contains the prime ranks from `MPI_COMM_WORLD`.
+
+```cpp
+// Get the rank and size in the original communicator
+int world_rank, world_size;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+// Get the group of processes in MPI_COMM_WORLD
+MPI_Group world_group;
+MPI_Comm_group(MPI_COMM_WORLD, &world_group);
+
+int n = 7;
+const int ranks[7] = {1, 2, 3, 5, 7, 11, 13};
+
+// Construct a group containing all of the prime ranks in world_group
+MPI_Group prime_group;
+MPI_Group_incl(world_group, 7, ranks, &prime_group);
+
+// Create a new communicator based on the group
+MPI_Comm prime_comm;
+MPI_Comm_create_group(MPI_COMM_WORLD, prime_group, 0, &prime_comm);
+
+int prime_rank = -1, prime_size = -1;
+// If this rank isn't in the new communicator, it will be
+// MPI_COMM_NULL. Using MPI_COMM_NULL for MPI_Comm_rank or
+// MPI_Comm_size is erroneous
+if (MPI_COMM_NULL != prime_comm) {
+ MPI_Comm_rank(prime_comm, &prime_rank);
+ MPI_Comm_size(prime_comm, &prime_size);
+}
+
+printf("WORLD RANK/SIZE: %d/%d \t PRIME RANK/SIZE: %d/%d\n",
+ world_rank, world_size, prime_rank, prime_size);
+
+MPI_Group_free(&world_group);
+MPI_Group_free(&prime_group);
+MPI_Comm_free(&prime_comm);
+```
+
+In this example, we construct a communicator by selecting only the prime ranks in `MPI_COMM_WORLD`. This is done with `MPI_Group_incl` and results in `prime_group`. Next, we pass that group to `MPI_Comm_create_group` to create `prime_comm`. At the end, we have to be careful to not use `prime_comm` on processes which don't have it, therefore we check to ensure that the communicator is not `MPI_COMM_NULL`, which is returned from `MPI_Comm_create_group` on the ranks not included in `ranks`.
diff --git a/tutorials/introduction-to-groups-and-communicators/ja_jp.md b/tutorials/introduction-to-groups-and-communicators/ja_jp.md
new file mode 100644
index 0000000..e777b3b
--- /dev/null
+++ b/tutorials/introduction-to-groups-and-communicators/ja_jp.md
@@ -0,0 +1,217 @@
+---
+layout: post
+title: グループとコミュニケータ - Introduction to Groups and Communicators
+author: Wesley Bland
+categories: Advanced MPI
+tags: MPI_Group, MPI_Comm
+redirect_from: '/introduction-to-groups-and-communicators/'
+---
+
+これまでのレッスンでは`MPI_COMM_WORLD`を使用してきました。単純なプログラムの場合はプロセスの数は多くないので、一度に1つのプロセスと話すか、一度に全てのプロセスと話すかでしょうから問題はありませんでした。しかし、プログラムの規模が大きくなり始めると、限定的なプロセスとしか通信をしたくないケースが出てきます。このレッスンでは、元となるプロセスグループの集合のプロセスとだけ集団通信するために、新しいコミュニケータを作成する方法を紹介します。
+
+> **Note** - チュートリアルのコードは[GitHub]({{ site.github.repo }})にあります。. このレッスンのコードは[tutorials/introduction-to-groups-and-communicators/code]({{ site.github.code }}/tutorials/introduction-to-groups-and-communicators/code)を参照してください。
+
+## コミュニケータとは? - Overview of communicators
+集団通信のレッスンで見てきたように、MPIの集団通信はコミュニケータ内の全プロセスと一度に通信できます。`MPI_Scatter`は他のプロセスにデータを分配したり、`MPI_Reduce`ではreduceを実行できます。しかし、これまではデフォルトの `MPI_COMM_WORLD` しか使ってきませんでした。
+
+単純なアプリケーションでは`MPI_COMM_WORLD`を使うことも珍しくないのですが、複雑なユースケースでは多くのコミュニケータがあった方が便利でしょう。例えば、グリッド内だけのプロセスのサブセットに対して計算を行いたい場合です。例として、各行の全プロセスの合計値を求めるような場合です。新しいコミュニケータを作成するための関数宣言を見てみましょう。
+
+
+```cpp
+MPI_Comm_split(
+ MPI_Comm comm,
+ int color,
+ int key,
+ MPI_Comm* newcomm)
+```
+
+`MPI_Comm_split`の名のとおり`color`と`key`に基づいて、あるコミュニケータをサブコミュニケータ群に"分割"して新しいコミュニケータを生成します。元のコミュニケータはなくならず、各プロセスに新しいコミュニケータが作成されることに注意してください。最初の引数`comm`は分割元のコミュニケータです。`MPI_COMM_WORLD`でもよいですし、他のコミュニケータでもかまいません。2番目の引数`color`は各プロセスがどの新しいコミュニケータに属するかを決定します。`color`に同じ値を渡したプロセスはすべて同じコミュニケータに割り当てられます。もし`color`が`MPI_UNDEFINED`であれば、そのプロセスは新しいコミュニケータには含まれません。3番目の引数`key`は、新しいコミュニケータ内の順序(ランク)を決定します。`key`の値が最も小さいプロセスがランク0になり、次に小さいプロセスがランク1になります。同順位の場合は、元のコミュニケーター内の順位が低いプロセスが最初になります。最後の引数`newcomm`は新しいコミュニケータです。
+
+## 複数のコミュニケータを使用する例 - Example of using multiple communicators
+
+単純な例として、1つのグローバルコミュニケータを複数のコミュニケータに分割してみます。元のコミュニケータには16個のプロセス存在しますが、これを4x4のグリッドに論理的にレイアウトし、グリッドを行ごとに分割したいというシナリオを考えます。それぞれの行にcolorをつけます。下の画像では左側の同じ色のプロセスグループが右側のそれぞれのコミュニケータに入る様子を示します。
+
+
+
+これを実現するコードです。
+
+```cpp
+// 元のコミュニケータのランクとサイズを得る
+int world_rank, world_size;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+int color = world_rank / 4; // 行をcolorとして使います
+
+// colorと元のランクを利用して新しいコミュニケータを作成します
+MPI_Comm row_comm;
+MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &row_comm);
+
+int row_rank, row_size;
+MPI_Comm_rank(row_comm, &row_rank);
+MPI_Comm_size(row_comm, &row_size);
+
+printf("WORLD RANK/SIZE: %d/%d \t ROW RANK/SIZE: %d/%d\n",
+ world_rank, world_size, row_rank, row_size);
+
+MPI_Comm_free(&row_comm);
+```
+
+まず、オリジナルのコミュニケータ`MPI_COMM_WORLD`の中での自分のランクとこのコミュニケータのサイズを得ます。次はローカルプロセスの "色(color) "を決定する大切な操作です。色によって、分割後のプロセスがどのコミュニケータに属するかが決定します。そして分割を実施します。ここで注目して欲しいのは分割操作のキーとして元のランク(`world_rank`)を使っていることです。新しいコミュニケーター内のすべてのプロセスは元のコミュニケーターと同じ順序としたいので、元のランクの値を使用します。分割後に新しいコミュニケータのサイズと自分のランクを表示します。
+
+```
+WORLD RANK/SIZE: 0/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 1/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 2/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 3/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 4/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 5/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 6/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 7/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 8/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 9/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 10/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 11/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 12/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 13/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 14/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 15/16 ROW RANK/SIZE: 3/4
+```
+
+出力順序が違っても心配しないでください。MPIプログラムで出力する場合、各プロセスはMPIジョブを起動した場所に出力を送り返さないと画面に出力されないからです。この例では見栄え良くするために並び替えています。
+
+最後に`MPI_Comm_free`でコミュニケータを解放することを忘れないでください。これは重要なステップではないように思うかもしれませんが、メモリを使い終わったら解放するのと同じくらい重要です。MPIオブジェクトが使われなくなったら、後で再利用できるように解放しなければなりません。MPIが一度に生成できるオブジェクトの数には限りがあるので、オブジェクトを解放しておかないとMPIが割り当て可能なオブジェクトを使い果たしたときに実行時エラーになる可能性があります。
+
+## その他のコミュニケータ作成機能 - Other communicator creation functions
+
+`MPI_Comm_split`は最も基本的なコミュニケータ作成関数ですが、他にも多くの関数があります。`MPI_Comm_dup`はコミュニケータを複製します。複製だけを行う関数が必要か?と思われるかもしれませんがライブラリを実現するために非常に便利です。なぜなら、自分のコードとライブラリーのコードが互いに干渉しないようにしなければならないからです。ですから、アプリケーションが最初に行うべきことは`MPI_COMM_WORLD`の複製を作成することです。ライブラリ自身も`MPI_COMM_WORLD`を複製して使うべきです。
+
+もう一つの関数は`MPI_Comm_create`です。この関数は (後述する)`MPI_Comm_create_group` とよく似ています。
+
+```cpp
+MPI_Comm_create(
+ MPI_Comm comm,
+ MPI_Group group,
+ MPI_Comm* newcomm)
+```
+
+最も大きな違いは`MPI_Comm_create`が`comm`に含まれる全てのプロセスを集団として扱うのに対して、 (後述する)`MPI_Comm_create_group` は `group` に含まれるプロセス群だけを対象とします。これはコミュニケータのサイズが非常に大きい時に大切になります。`MPI_COMM_WORLD` のサブセットを1,000,000プロセスで実行する場合、サイズが大きくなると集団通信は非常に高価なコストとなるため、少ないプロセスで処理を実行することが重要になってくるのです。
+
+インターコミュニケータとイントラコミュニケータの違い、その他の高度なコミュニケータ作成関数などは今回のチュートリアルでは取り上げません。しかし、コミュニケータには他にも高度な機能があります。これらは特殊なアプリケーションでのみ使用されるものです。将来のチュートリアルで取り上げるかもしれません。
+
+## グループの概要 - Overview of groups
+
+`MPI_Comm_split` は新しいコミュニケータを作成する最も簡単な方法ですが他にもコミュニケータを作る方法はあります。それは`MPI_Group`という新しい種類のMPIオブジェクトを使う方法です。グループについて詳しく説明する前に、コミュニケータとは何かもう少し説明します。MPI内部的にはコミュニケータを構成する2つの主要な情報、コミュニケータと他のコミュニケータを区別するコンテキスト(context)またはIDと、コミュニケータに含まれるプロセスのグループを管理しています。コンテキストは、あるコミュニケータ上の操作が他のコミュニケータの操作に影響しないようにするためのものです。このためにMPIは内部で各コミュニケータのIDを保持しています。グループとはそのコミュニケータに含まれるすべてのプロセスの集合のことです。これまで使ってきた`MPI_COMM_WORLD`とは`mpiexec`で起動されたすべてのプロセスです。他のコミュニケータはグループが異なります。上のコード例では`MPI_Comm_split`に同じ`color`にしたすべてのプロセスは同じグループになっています。
+
+MPIは集合論(set theory)で扱われる操作をグループに適応することができます。集合論をすべて理解する必要はありませんが2つの操作の意味を知っておいてください。ここでは"集合(set)"と呼ぶ代わりに、MPIに適用される "グループ(group)"という用語を使用します。まず、和集合(union)演算は2つの集合から新しい(潜在的に)大きな集合を作ります。この新しい集合には2つの集合のすべてのメンバが含まれます(重複はありません)。次に、積集合(intersection)は、他の二つの集合から新しい(潜在的に)小さい集合を作ります。この新しい集合には、元の集合の両方に存在するメンバがすべて含まれます。これら両方の操作の例を以下に図解で示します。
+
+
+
+上段の例は`{0, 1, 2, 3}` と `{2, 3, 4, 5}` の和集合は `{0, 1, 2, 3, 4, 5}` となります。2つ目の例では、`{0, 1, 2, 3}` と `{2, 3, 4, 5}` の積集合は `{2, 3}` となります。
+
+## グループの使用 - Using MPI groups
+
+グループの仕組みの基本がわかったので実際のMPI操作でどう使うのかをみていきます。MPIでは`MPI_Comm_group`ルーチンでコミュニケータ内のプロセスのグループを簡単に取得することができます。
+
+```cpp
+MPI_Comm_group(
+ MPI_Comm comm,
+ MPI_Group* group)
+```
+
+コミュニケータにはコンテキスト(ID)とグループが含まれます。`MPI_Comm_group` はそのグループオブジェクトへの参照を得ます。グループオブジェクトはコミュニケータオブジェクトと同じように動作しますが、集団通信ルーチンの引数としで他のランクと通信するためには使用できません(コンテキストが付加されていないためです)。ただし、グループのランクとサイズを取得することはできます (`MPI_Group_rank` と `MPI_Group_size`)。コミュニケーターではできずにグループだけができることとは、ローカルで新しいグループを作成することです。ここで注目するのはローカル操作とリモート操作の違いに注意してください。リモート操作では他のランクと通信が発生しますが、ローカル操作では通信は発生しません。新しいコミュニケーターを作成する場合はそのアプリケーション内のすべてのプロセスで同じコンテキストとグループを決定する必要があるためリモート操作となります。しかし、グループを作成する場合は各プロセスで同じコンテキストを持つ必要がないため通信する必要はなくローカル操作となります。このため通信を気にする必要はなくなります。
+
+グループに対する操作はとても簡単です。
+
+```cpp
+MPI_Group_union(
+ MPI_Group group1,
+ MPI_Group group2,
+ MPI_Group* newgroup)
+```
+
+積集合もみてみましょう。
+
+```cpp
+MPI_Group_intersection(
+ MPI_Group group1,
+ MPI_Group group2,
+ MPI_Group* newgroup)
+```
+
+どちらの演算も、操作は`group1`と`group2`に対して行われて、結果は`newgroup`に格納されます。
+
+MPI におけるグループの使い方はたくさんあります。グループが同じかどうかを比較する、あるグループから別のグループを引く、あるグループから特定のランクを除外する、あるグループのランクを別のグループに変換する、といったようにグループを使用することができます。最近MPIに追加された関数の中で最も役に立つのは`MPI_Comm_create_group`でしょう。これは新しいコミュニケータを作成する関数ですが `MPI_Comm_split` のようにその場で計算をして構成を決めるのではなく、 `MPI_Group`を受け取り、グループと同じプロセスをすべて持つ新しいコミュニケータを作成します。
+
+```cpp
+MPI_Comm_create_group(
+ MPI_Comm comm,
+ MPI_Group group,
+ int tag,
+ MPI_Comm* newcomm)
+```
+
+## グループの使用例 - Example of using groups
+
+グループの使い方の簡単な例です。`MPI_Group_incl`という関数を使ってグループ内の特定のランクを選択し、そのランクのみを含む新しいグループを作成します。
+
+```cpp
+MPI_Group_incl(
+ MPI_Group group,
+ int n,
+ const int ranks[],
+ MPI_Group* newgroup)
+```
+
+この関数は`group`に含まれるプロセスのうち、`ranks`に含まれるランクを持つプロセスのだけが`newgroup`に入ります。どのように機能するかを確かめるため、`MPI_COMM_WORLD`の素数のランクを含むコミュニケータを作成してみます。(訳注:素数は静的に与えています)
+
+```cpp
+// 元のコミュニケータのサイズとその中でのランクを取得
+int world_rank, world_size;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+// このプロセスのMPI_COMM_WORLD内でのグループを得る
+MPI_Group world_group;
+MPI_Comm_group(MPI_COMM_WORLD, &world_group);
+
+int n = 7;
+const int ranks[7] = {1, 2, 3, 5, 7, 11, 13};
+
+// world_groupで素数ランクを持つプロセスだけのグループを作成する
+MPI_Group prime_group;
+MPI_Group_incl(world_group, 7, ranks, &prime_group);
+
+// このグループを元にしたコミュニケータを作成する
+MPI_Comm prime_comm;
+MPI_Comm_create_group(MPI_COMM_WORLD, prime_group, 0, &prime_comm);
+
+int prime_rank = -1, prime_size = -1;
+// このプロセスが新しいコミュニケータに所属しない場合、MPI_COMM_NULL になります。
+// MPI_Comm_rankまたはMPI_Comm_sizeを使う際に第一引数にMPI_COMM_NULL のコミュニケータを指定してはいけません
+if (MPI_COMM_NULL != prime_comm) {
+ MPI_Comm_rank(prime_comm, &prime_rank);
+ MPI_Comm_size(prime_comm, &prime_size);
+}
+
+printf("WORLD RANK/SIZE: %d/%d \t PRIME RANK/SIZE: %d/%d\n",
+ world_rank, world_size, prime_rank, prime_size);
+
+MPI_Group_free(&world_group);
+MPI_Group_free(&prime_group);
+MPI_Comm_free(&prime_comm);
+```
+
+この例では、`MPI_COMM_WORLD`の素数のランクのみを選択してコミュニケータを作成します。まずは `MPI_Group_incl`でグループ`prime_group`を生成します。次に、このグループを `MPI_Comm_create_group`に渡して コミュニケータ`prime_comm`を作成します。そして`ranks` に含まれていないランクの`MPI_Comm_create_group`から返されるコミュニケータが`MPI_COMM_NULL`でないことを確認してランクやグループを確認します。
+
+(以下は訳者の環境でn=8で実行した例です)
+```
+WORLD RANK/SIZE: 6/8 --- PRIME RANK/SIZE: -1/-1
+WORLD RANK/SIZE: 0/8 --- PRIME RANK/SIZE: -1/-1
+WORLD RANK/SIZE: 4/8 --- PRIME RANK/SIZE: -1/-1
+WORLD RANK/SIZE: 3/8 --- PRIME RANK/SIZE: 2/5
+WORLD RANK/SIZE: 1/8 --- PRIME RANK/SIZE: 0/5
+WORLD RANK/SIZE: 5/8 --- PRIME RANK/SIZE: 3/5
+WORLD RANK/SIZE: 2/8 --- PRIME RANK/SIZE: 1/5
+WORLD RANK/SIZE: 7/8 --- PRIME RANK/SIZE: 4/5
+```
\ No newline at end of file
diff --git a/tutorials/introduction-to-groups-and-communicators/zh_cn.md b/tutorials/introduction-to-groups-and-communicators/zh_cn.md
new file mode 100644
index 0000000..37155ee
--- /dev/null
+++ b/tutorials/introduction-to-groups-and-communicators/zh_cn.md
@@ -0,0 +1,287 @@
+---
+layout: post
+title: Introduction to Groups and Communicators
+author: Wesley Bland
+categories: Advanced MPI
+tags: MPI_Group, MPI_Comm
+redirect_from: '/introduction-to-groups-and-communicators/'
+---
+
+在以前的教程中,我们使用了通讯器 `MPI_COMM_WORLD`。
+对于简单的程序,这已经足够了,因为我们的进程数量相对较少,并且通常要么一次要与其中之一对话,要么一次要与所有对话。
+当程序规模开始变大时,这变得不那么实用了,我们可能只想一次与几个进程进行对话。
+在本次教程中,我们将展示如何创建新的通讯器,以便一次与原始线程组的子集进行沟通。
+
+> **注意**- 本站点的所有代码都在 [GitHub]({{ site.github.repo }}) 上。本教程的代码在 [tutorials/introduction-to-groups-and-communicators/code]({{ site.github.code }}/tutorials/introduction-to-groups-and-communicators/code) 目录下。
+
+## 通讯器概述
+
+正如我们在学习集体例程时所看到的那样,MPI 允许您立即与通讯器中的所有进程进行对话,以执行诸如使用 `MPI_Scatter` 将数据从一个进程分发到多个进程或使用 `MPI_Reduce` 执行数据归约的操作。
+但是,到目前为止,我们仅使用了默认的通讯器 `MPI_COMM_WORLD`。
+
+对于简单的应用程序,使用 `MPI_COMM_WORLD` 进行所有操作并不罕见,但是对于更复杂的用例,拥有更多的通讯器可能会有所帮助。
+例如,如果您想对网格中进程的子集执行计算。
+例如,每一行中的所有进程都可能希望对一个值求和。
+这将是第一个也是最常见的用于创建新的通讯器的函数:
+
+```cpp
+MPI_Comm_split(
+ MPI_Comm comm,
+ int color,
+ int key,
+ MPI_Comm* newcomm)
+```
+
+顾名思义,`MPI_Comm_split` 通过基于输入值 `color` 和 `key` 将通讯器“拆分”为一组子通讯器来创建新的通讯器。
+在这里需要注意的是,原始的通讯器并没有消失,但是在每个进程中都会创建一个新的通讯器。
+第一个参数 `comm` 是通讯器,它将用作新通讯器的基础。
+这可能是 `MPI_COMM_WORLD`,但也可能是其他任何通讯器。
+第二个参数 `color` 确定每个进程将属于哪个新的通讯器。
+为 `color` 传递相同值的所有进程都分配给同一通讯器。
+如果 `color` 为 `MPI_UNDEFINED`,则该进程将不包含在任何新的通讯器中。
+第三个参数 `key` 确定每个新通讯器中的顺序(秩)。
+传递 `key` 最小值的进程将为 0,下一个最小值将为 1,依此类推。
+如果存在平局,则在原始通讯器中秩较低的进程将是第一位。
+最后一个参数 `newcomm` 是 MPI 如何将新的通讯器返回给用户。
+
+## 使用多个通讯器的示例
+
+现在,让我们看一个简单的示例,在该示例中,我们尝试将单个全局通讯器拆分为一组较小的通讯器。
+在此示例中,我们将想象我们已经在逻辑上将原始通讯器布局为共 16 个进程的 4x4 网格,并且希望按行划分网格。
+为此,每一行将获得自己的颜色(参数 `color`)。
+在下图中,您可以看到左图具有相同颜色的每组进程如何最终变成右图的自己的通讯器。
+
+
+
+让我们看一下代码。
+
+```cpp
+// 获取原始通讯器的秩和大小
+int world_rank, world_size;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+int color = world_rank / 4; // 根据行确定颜色
+
+// 根据颜色拆分通讯器,然后调用
+// 利用原始秩
+MPI_Comm row_comm;
+MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &row_comm);
+
+int row_rank, row_size;
+MPI_Comm_rank(row_comm, &row_rank);
+MPI_Comm_size(row_comm, &row_size);
+
+printf("WORLD RANK/SIZE: %d/%d \t ROW RANK/SIZE: %d/%d\n",
+ world_rank, world_size, row_rank, row_size);
+
+MPI_Comm_free(&row_comm);
+```
+
+前几行获得原始通讯器 `MPI_COMM_WORLD` 的秩和大小。
+下一行执行确定局部进程 `color` 的重要操作。
+请记住,`color` 决定了拆分后该进程所属的通讯器。
+接下来,我们将看到所有重要的拆分操作。
+这里的新事物是,我们使用原始秩(`world_rank`)作为拆分操作的 `key`。
+由于我们希望新通讯器中的所有进程与原始通讯器中的所有进程处于相同的顺序,因此在这里使用原始等级值最有意义,因为它已经正确地排序了。
+之后,我们将打印出新的等级和大小以确保其有效。
+您的输出应如下所示:
+
+```
+WORLD RANK/SIZE: 0/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 1/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 2/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 3/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 4/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 5/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 6/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 7/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 8/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 9/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 10/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 11/16 ROW RANK/SIZE: 3/4
+WORLD RANK/SIZE: 12/16 ROW RANK/SIZE: 0/4
+WORLD RANK/SIZE: 13/16 ROW RANK/SIZE: 1/4
+WORLD RANK/SIZE: 14/16 ROW RANK/SIZE: 2/4
+WORLD RANK/SIZE: 15/16 ROW RANK/SIZE: 3/4
+```
+
+如果您的顺序不正确,请不要惊慌。
+当您在 MPI 程序中显示内容时,每个进程都必须将其输出发回启动 MPI 作业的位置,然后才能将其打印到屏幕上。
+这往往意味着排序变得混乱,因为您永远无法假设仅以特定的秩顺序打印内容,即输出结果实际上将按照您期望的顺序结束,但是显示不是。
+只是在这里重新排列输出内容,以至看起来不错。
+
+最后,我们使用 `MPI_Comm_free` 释放通讯器。
+这似乎不是一个重要的步骤,但与在其他任何程序中使用完内存后释放内存一样重要。
+当不再使用 MPI 对象时,应将其释放,以便以后重用。
+MPI 一次可以创建的对象数量有限,如果 MPI 用完了可分配对象,则不释放对象可能会导致运行时错误。
+
+## 其他通讯器创建函数
+
+尽管 `MPI_Comm_split` 是最常见的通讯器创建函数,但还有许多其他函数。
+`MPI_Comm_dup` 是最基本的,它创建了一个通讯器的副本。
+似乎存在一个仅创建副本的函数似乎很奇怪,但这对于使用库执行特殊函数的应用(例如数学库)非常有用。
+在这类应用中,重要的是用户代码和库代码不要互相干扰。
+为了避免这种情况,每个应用程序应该做的第一件事是创建 `MPI_COMM_WORLD` 的副本,这将避免其他使用 `MPI_COMM_WORLD` 的库的问题。
+库本身也应该复制 `MPI_COMM_WORLD` 以避免相同的问题。
+
+另一个功能是 `MPI_Comm_create`。
+乍一看,此功能与 `MPI_Comm_create_group` 非常相似。
+其原型几乎相同:
+
+```cpp
+MPI_Comm_create(
+ MPI_Comm comm,
+ MPI_Group group,
+ MPI_Comm* newcomm)
+```
+
+然而,主要区别(除了缺少 `tag` 参数之外)是,`MPI_Comm_create_group` 仅是 `group` 中包含的一组进程的集合,而 `MPI_Comm_create` 是 `comm` 中每个进程的集合。
+当通讯器的规模很大时,这是一个重要的区别。
+如果尝试在运行 1,000,000 个进程时创建 `MPI_COMM_WORLD` 的子集,则重要的是使用尽可能少的进程来执行该操作,因为大型集的开销会变得非常昂贵。
+
+通讯器还有其他一些更高级的功能,我们在这里不介绍,例如内部通讯器与内部通讯器之间的差异以及其他高级通讯器创建功能。
+这些仅用于非常特殊的应用,以后的教程中可能会介绍这些应用程序。
+
+## 组的概述
+
+尽管 `MPI_Comm_split` 是创建新通讯器的最简单的方法,但并非唯一的方法。
+创建通讯器有更灵活的方法,但是它们使用一种新的 MPI 对象 `MPI_Group`。
+在详细讨论组之前,让我们再回顾一下通讯器的实际含义。
+在内部,MPI 必须(除其他事项外)保持通讯器的两个主要部分,即区分一个通讯器与另一个通讯器的上下文(或 ID)以及该通讯器包含的一组进程。
+The context is what prevents an operation on one communicator from matching with a similar operation on another communicator.
+上下文阻止了与一个通讯器上的操作匹配的另一通讯器上的类似操作。
+MPI 在内部为每个通讯器保留一个 ID,以防止混淆。
+组更易于理解,因为它只是通讯器中所有进程的集合。
+对于 `MPI_COMM_WORLD`,这是由 `mpiexec` 启动的所有进程。
+对于其他通讯器,组将有所不同。
+在上面的示例代码中,组是所有以相同的 `color` 传参给 `MPI_Comm_split` 的进程。
+
+MPI 使用通常起作用集合理论的相同方式来使用这些组。
+您不必熟悉所有的集合理论即可理解 MPI,但是了解两个操作的含义会有所帮助。
+首先,**并**会从其他两个集合中创建一个新的(可能)更大的集合。
+新集合包括前两个集合的所有成员(无重复)。
+其次,**交**会从其他两个集合中创建一个新的(可能)更小的集合。
+新集合包括两个原始集合中都存在的所有成员。
+您可以在下面以图形方式查看这两个操作的示例。
+随后,我们将使用适用于 MPI 的术语“组”(`groups`),而非“集合“(`sets`)。
+
+
+
+在第一个示例中,两个组 `{0,1,2,3}` 和 `{2,3,4,5}` 的并集是 `{0,1,2,3,4,5}`,因为这些项中的每一个都出现在组中。
+在第二个示例中,两个组 `{0,1,2,3}` 和 `{2,3,4,5}` 的交集为 `{2,3}`,因为每个组中同时仅出现那些项。
+
+## Using MPI groups
+
+现在,我们了解了组工作原理的基础,让我们看看如何将其应用于 MPI 操作。
+在 MPI 中,很容易通过 API 调用 `MPI_Comm_group` 来获取通讯器中的进程组。
+
+```cpp
+MPI_Comm_group(
+ MPI_Comm comm,
+ MPI_Group* group)
+```
+
+如上所述,通讯器包含一个上下文或 ID,以及一个组。
+调用 `MPI_Comm_group` 会得到对该组对象的引用。
+组对象的工作方式与通讯器对象相同,不同之处在于您不能使用它与其他秩进行通信(因为它没有附加上下文)。
+您仍然可以获取组的秩和大小(分别为 `MPI_Group_rank` 和 `MPI_Group_size`)。
+但是,组特有的功能而通讯器无法完成的工作是可以使用组在本地构建新的组。
+在此记住本地操作和远程操作之间的区别很重要。
+远程操作涉及与其他秩的通信,而本地操作则没有。
+创建新的通讯器是一项远程操作,因为所有进程都需要决定相同的上下文和组,而在本地创建组是因为它不用于通信,因此每个进程不需要具有相同的上下文。
+您可以随意操作一个组,而无需执行任何通信。
+
+一旦有一个或两个组,对它们执行操作就很简单。
+**并**看起来像这样:
+
+```cpp
+MPI_Group_union(
+ MPI_Group group1,
+ MPI_Group group2,
+ MPI_Group* newgroup)
+```
+
+您可能会猜到**交**看起来像这样:
+
+```cpp
+MPI_Group_intersection(
+ MPI_Group group1,
+ MPI_Group group2,
+ MPI_Group* newgroup)
+```
+
+在这两种情况下,操作均在 `group1` 和 `group2` 上执行,结果存储在 `newgroup` 中。
+
+MPI 中有许多关于组的用法。
+您可以比较组以查看它们是否相同,从另一个组中减去一个组,从组中排除特定秩,或使用一个组将一个组的秩转换为另一组。
+但是,MPI 中可能是最有用的一个函数是 `MPI_Comm_create_group`。
+这是一个用于创建新通讯器的函数,但无需像 `MPI_Comm_split` 之类那样需要进行计算以决定组成,该函数将使用一个 `MPI_Group` 对象并创建一个与组具有相同进程的新通讯器。
+
+```cpp
+MPI_Comm_create_group(
+ MPI_Comm comm,
+ MPI_Group group,
+ int tag,
+ MPI_Comm* newcomm)
+```
+
+## Example of using groups
+
+让我们看一下使用组的简单示例。
+在这里,我们将使用另一个函数,该函数允许您选择组中的特定秩并构建为新组,即 `MPI_Group_incl`。
+
+```cpp
+MPI_Group_incl(
+ MPI_Group group,
+ int n,
+ const int ranks[],
+ MPI_Group* newgroup)
+```
+
+该函数中,`newgroup` 将包含 `group` 中的秩存在于 `ranks` 数组中的 `n` 个进程。
+想看看它是如何工作的?
+让我们尝试创建一个包含来自 `MPI_COMM_WORLD` 的主要秩的通讯器。
+
+```cpp
+// 获取原始通讯器的等级和大小
+int world_rank, world_size;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+// 获取 MPI_COMM_WORLD 中的进程组
+MPI_Group world_group;
+MPI_Comm_group(MPI_COMM_WORLD, &world_group);
+
+int n = 7;
+const int ranks[7] = {1, 2, 3, 5, 7, 11, 13};
+
+// 构造一个包含 world_group 中所有主要秩的组
+MPI_Group prime_group;
+MPI_Group_incl(world_group, 7, ranks, &prime_group);
+
+// 根据组创建一个新的通讯器
+MPI_Comm prime_comm;
+MPI_Comm_create_group(MPI_COMM_WORLD, prime_group, 0, &prime_comm);
+
+int prime_rank = -1, prime_size = -1;
+// 如果此秩不在新的通讯器中,则为
+// MPI_COMM_NULL。使用 MPI_COMM_NULL 作为 MPI_Comm_rank 或
+// MPI_Comm_size 的错误
+if (MPI_COMM_NULL != prime_comm) {
+ MPI_Comm_rank(prime_comm, &prime_rank);
+ MPI_Comm_size(prime_comm, &prime_size);
+}
+
+printf("WORLD RANK/SIZE: %d/%d \t PRIME RANK/SIZE: %d/%d\n",
+ world_rank, world_size, prime_rank, prime_size);
+
+MPI_Group_free(&world_group);
+MPI_Group_free(&prime_group);
+MPI_Comm_free(&prime_comm);
+```
+
+在此示例中,我们通过仅选择 `MPI_COMM_WORLD` 中的主要秩来构建通讯器。
+这是通过 `MPI_Group_incl` 完成的,并将结果存储在 `prime_group` 中。
+接下来,我们将该组传递给 `MPI_Comm_create_group` 以创建 `prime_comm`。
+最后,我们必须小心不要在没有 `prime_comm` 的进程上使用 `prime_comm`,因此我们要检查以确保通讯器不是 `MPI_COMM_NULL` 状态 —— 不在 `ranks` 中而从 `MPI_Comm_create_group` 返回的结果。
diff --git a/tutorials/launching-an-amazon-ec2-mpi-cluster/index.md b/tutorials/launching-an-amazon-ec2-mpi-cluster/index.md
index 512e1c6..db94d17 100644
--- a/tutorials/launching-an-amazon-ec2-mpi-cluster/index.md
+++ b/tutorials/launching-an-amazon-ec2-mpi-cluster/index.md
@@ -3,6 +3,7 @@ layout: post
title: Launching an Amazon EC2 MPI Cluster
author: Wes Kendall
categories: Beginner MPI
+translations: ja_jp
tags:
redirect_from: '/launching-an-amazon-ec2-mpi-cluster/'
---
@@ -82,7 +83,7 @@ This creates a "mykey" key on your machine under `~/.ssh/mykey.rsa` and also cre
```
>>> Successfully created keypair: mykey
>>> fingerprint: ...
->>> contents:
+>>> contents:
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
@@ -113,6 +114,9 @@ To determine the cost of running your cluster, multiply the number of nodes by t
> **Note** - Although t1.micro instances are the cheapest, they hang indefinitely for me when starting a cluster with StarCluster.
+### Enable mpich2 plugin for Starcluster
+Lastly, before launching Starcluster, enable the `mpich2` plugin for Starcluster by following [these steps](http://star.mit.edu/cluster/docs/0.93.3/plugins/mpich2.html).
+
## Starting, accessing, and stopping your cluster
After your StarCluster is configured, type the following to start a cluster called "mpicluster." The default config uses "smallcluster" as the default cluster type:
@@ -122,18 +126,18 @@ starcluster start mpicluster
The process to start a cluster can take a bit of time depending on your configuration. After the command is complete, StarCluster will print out the available commands for accessing, stopping, and restarting your cluster.
-SSH into the master node of your cluster by typing:
+SSH into the manager node of your cluster by typing:
```
-starcluster sshmaster mpicluster
+starcluster ssh manager mpicluster
```
Once you are logged into the cluster, your current working directory will be `/root`. Change into the `/home/ubuntu` or `/home/sgeadmin` areas to compile code. These directories are mounted on a network file system and are viewable by all nodes in your cluster.
-While you are in one of the mounted home directories, go ahead and check out the MPI tutorial code from its Github repository. The code is used by every lesson on this site:
+While you are in one of the mounted home directories, go ahead and check out the MPI tutorial code from its GitHub repository. The code is used by every lesson on this site:
```
-git clone git://github.com/wesleykendall/mpitutorial.git
+git clone git://github.com/mpitutorial/mpitutorial.git
```
After you feel comfortable with accessing your cluster, log out of it and stop your cluster:
@@ -157,4 +161,4 @@ starcluster terminate mpicluster
The difference between a "stopped" and "terminated" cluster is that stopped clusters still reside as images on Amazon's Elastic Block Store (EBS). If you will not be starting your cluster again in the foreseeable future, it is recommended to go ahead and terminate the cluster since Amazon EBS payment rates apply to stored instances. As always, educate yourself on [Amazon EC2 Pricing](http://aws.amazon.com/ec2/pricing/) before getting started.
## Ready to run MPI programs on your cluster?
-Now that you have your very own cluster, it's time to start running MPI programs. To get started, check out how to compile and run an [MPI hello world application]({{ site.baseurl }}/tutorials/mpi-hello-world/). For all beginner lessons, check out the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/). If you had any trouble with the lesson, please leave your comment below so that we can try to figure out what went wrong. Happy coding!
\ No newline at end of file
+Now that you have your very own cluster, it's time to start running MPI programs. To get started, check out how to compile and run an [MPI hello world application]({{ site.baseurl }}/tutorials/mpi-hello-world/). If you want to try the same by building a local cluster, go through [running an MPI cluster within a LAN]({{ site.baseurl }}/tutorials/running-an-mpi-cluster-within-a-lan) tutorial. For all lessons, check out the [MPI tutorials]({{ site.baseurl }}/tutorials/). If you had any trouble with the lesson, please leave your comment below so that we can try to figure out what went wrong. Happy coding!
diff --git a/tutorials/launching-an-amazon-ec2-mpi-cluster/ja_jp.md b/tutorials/launching-an-amazon-ec2-mpi-cluster/ja_jp.md
new file mode 100644
index 0000000..e4b621e
--- /dev/null
+++ b/tutorials/launching-an-amazon-ec2-mpi-cluster/ja_jp.md
@@ -0,0 +1,162 @@
+---
+layout: post
+title: Amazon EC2 MPIクラスタを起動する - Launching an Amazon EC2 MPI Cluster
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/launching-an-amazon-ec2-mpi-cluster/'
+---
+
+[前回のレッスン]({{ site.baseurl }}/tutorials/installing-mpich2/)ではMPICH2を1台のマシンにインストールしする方法を説明しました。ただし、MPIを学習しプログラムを実行するために十分なリソースが1台のマシンで提供できるとは限りません。クラスタに簡単にアクセスできる初心者はそういないでしょう。最高のMPIチュートリアルのためには、このサイトのチュートリアルコードはもちろん、自分の並列コードを実行できる環境が必要なので、仮想MPIクラスタをセットアップする方法を説明します。
+
+## Amazon EC2で始めよう
+このレッスンではAmazonのElastic Compute Cloud (EC2)を使ったクラスタの説明をします。Amazon EC2を始めるには、[Amazon Web Services (AWS)](http://aws.amazon.com/)にアクセスし、"Sign Up "ボタンを押します。サービスを利用するには支払い情報を入力する必要があり、利用したサービスに応じて課金されます。
+
+> **Note** - AWSにサインアップする前に、必ず[EC2の価格設定](http://aws.amazon.com/ec2/pricing/)を読んでなにをしようとしているのかを理解してください。この記事を書いている時点では、AWSは一部のマシンサイズに無料時間枠を提供しており、1時間あたり2アメリカセントという低価格のマシンも提供されています。
+
+AWSにサインアップしたら、[EC2スタートガイド](http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html?r=1874)を読んでください。さまざまなインスタンスの起動、アクセス方法、終了の方法などを知っておかなければなりません。
+
+一方で、MPIクラスタを作成しアクセスするためにAmazonのEC2インフラストラクチャを完全に理解する必要はありません。EC2の基本がわかったら次のステップに進んでください。
+
+## StarCluster のインストール
+仮想クラスタを作成するために使うツールは[MITのStarCluster toolkit](http://star.mit.edu/cluster/)です。StarClusterは、EC2上にクラスタを構築してアクセス可能とする一連のプロセスを自動化したツールセットです。StarClusterはクラスタの作成・起動だけでなく、OpenMPIや並列アプリケーションのためのソフトウェアも一括でインストールしてくれます。
+
+StarClusterツールキットをローカルマシンにインストールするには(Linux/MacOSXでは)、次のように入力します:
+
+```
+$ sudo easy_install StarCluster
+```
+
+Windowsを使っている場合は[Windows installation instructions](http://star.mit.edu/cluster/docs/latest/installation.html#installing-on-windows)を参照してください。
+
+## StarClusterの設定 - Configuring StarCluster
+インストールが終わったら次のように実行してみてください。
+
+```
+$ starcluster help
+```
+
+この時点ではStarClusterは設定されていないため、以下のように出力されるでしょう(ディレクトリが私のとは異なることに注意してください)。
+
+```
+StarCluster - (http://web.mit.edu/starcluster) (v. 0.93.3)
+Software Tools for Academics and Researchers (STAR)
+Please submit bug reports to starcluster@mit.edu
+
+!!! ERROR - config file /Users/wesleykendall/.starcluster/config does not exist
+
+Options:
+--------
+[1] Show the StarCluster config template
+[2] Write config template to /Users/wesleykendall/.starcluster/config
+[q] Quit
+
+Please enter your selection:
+```
+
+2を入力しましょう。StarClusterはホームディレクトリ内の `~/.starcluster/config`にデフォルトの構成ファイルを生成します。
+
+次はAWSアカウントからAWSアクセスキー、シークレットアクセスキー、12桁のユーザーIDを取得しましょう。この情報は、[Amazon Web Services](http://aws.amazon.com/)にアクセスし、右上にある "My Account/Console "をクリックし、"My Security Credentials "をクリックすることで確認できます。
+
+
+
+"Access Credentials"セクションの中に"Access Key ID "フィールドと"Secret Access Key"フィールドがあります。ページの下部には"Account Identifiers"セクションがあり、"AWS Account ID"フィールがあります。
+
+デフォルトの設定ファイル(`~/.starcluster/config`)をテキストエディタで開き`[aws info]`の行を探し、適切なフィールドにAWSの情報を入力してください。
+
+```
+[aws info]
+AWS_ACCESS_KEY_ID = # Your Access Key ID here
+AWS_SECRET_ACCESS_KEY = # Your Secret Access Key here
+AWS_USER_ID = # Your 12-digit AWS Account ID here (no hyphens)
+```
+
+これらの情報を入力したら設定ファイルを保存してsshの公開/秘密鍵ペアを作成します。この鍵はAmazonにアップロードされ、クラスタにログインする際の認証に使用します。StarClusterで公開/秘密鍵ペアを生成しましょう。
+
+```
+$ starcluster createkey mykey -o ~/.ssh/mykey.rsa
+```
+これにより、`~/.ssh/mykey.rsa`に「mykey」キーが作成され、AWSアカウントにもキーが作成されます。Amazonの認証情報を正しく入力した場合は、次のような出力になるでしょう。
+
+```
+>>> Successfully created keypair: mykey
+>>> fingerprint: ...
+>>> contents:
+-----BEGIN RSA PRIVATE KEY-----
+...
+-----END RSA PRIVATE KEY-----
+```
+
+設定ファイルを再度開き、`[key mykey]`エントリがあることを確認してください。このエントリがない場合は、設定に次の内容を追加します。
+
+```
+[key mykey]
+KEY_LOCATION = ~/.ssh/mykey.rsa
+```
+
+そしてクラスタパラメータを設定します。デフォルトの設定では、`[cluster smallcluster]`に "smallcluster "というクラスタの設定が書かれています。デフォルトで以下のパラメータが設定されているはずでしょう。
+
+```
+[cluster smallcluster]
+KEYNAME = mykey
+CLUSTER_SIZE = 2
+CLUSTER_USER = sgeadmin
+CLUSTER_SHELL = bash
+NODE_IMAGE_ID = ami-899d49e0
+NODE_INSTANCE_TYPE = m1.small
+```
+
+このフィールドをみていきます。2つ以外のノードでクラスタを開始したい場合は、`CLUSTER_SIZE`オプションを変更してください。別のキー(例の"mykey"以外) を定義している場合は、`KEYNAME` フィールドに適切なキーを追加します。クラスタを実行すると、ネットワークファイルシステム (NFS) にマウントされたホームディレクトリを持つ `CLUSTER_USER` ユーザ名が自動的に生成されます。`NODE_IMAGE_ID` はクラスタソフトウェアのイメージIDです。最後のパラメータ `NODE_INSTANCE_TYPE` は各ノードのサイズを決定します。利用可能なインスタンスタイプとその属性のリストについては、[ここ](http://aws.amazon.com/ec2/instance-types/)を参照してください。
+
+クラスタの実行コストを決定するには、ノード数にインスタンス・タイプの時間単価を掛ければよいです。この記事を書いている時点ではm1.smallインスタンスは1時間あたり6.5アメリカセントです。費用は時間に対して課金されます。クラスタを30分間稼働させた場合、1時間分の料金が請求されます。
+
+> **Note** - t1.microインスタンスは最も安価ですが、私の場合StarClusterでクラスタを起動すると、うまく起動できません。
+
+### Starclusterのmpich2プラグインを有効にする。
+最後に、Starclusterを起動する前に、[以下の手順](http://star.mit.edu/cluster/docs/0.93.3/plugins/mpich2.html)に従って、Starcluster用の`mpich2`プラグインを有効にしてください。
+
+## クラスタの起動、アクセス、停止
+設定が終わったら次のように入力して"mpicluster"クラスタを起動します。デフォルトの構成では、デフォルトのクラスタタイプとして"smallcluster"が使用されます:
+
+```
+starcluster start mpicluster
+```
+
+このプロセスは構成によっては少し時間がかかります。コマンドの完了後StarClusterはクラスタへのアクセス、停止、および再起動に使用できるコマンドを出力します。
+
+次のコマンドでクラスタのマネージャノードにSSHでログインできます。
+
+```
+starcluster ssh manager mpicluster
+```
+
+クラスタにログインすると、カレントディレクトリは`/root`となります。コードのコンパイルは`/home/ubuntu`または`/home/sgeadmin`に移動してから行ってください。このディレクトリはNFSマウントされており、クラスタ内のすべてのノードから共有されています。
+
+次にGitHubレポジトリからこのMPIチュートリアルのコードをチェックアウトしてください。このサイトのすべてのレッスンで使用されているコードにアクセスできます。
+
+```
+git clone git://github.com/mpitutorial/mpitutorial.git
+```
+
+クラスタへのアクセスに慣れてきたらログアウトしてクラスタを停止ましょう。
+
+```
+starcluster stop mpicluster
+```
+
+クラスターは次のように入力して再度起動できます。
+
+```
+starcluster start -x mpicluster
+```
+
+クラスターを完全に終了するには、次のように入力します。
+
+```
+starcluster terminate mpicluster
+```
+
+"stop"と"terminate"の違いはなんでしょう?stopしたクラスタはまだAmazonのElastic Block Store(EBS)上にイメージが残っています。Amazon EBSの料金は保存されている量に対して課金されるため、当面クラスタを使用しない場合はクラスタのterminateをお勧めします。しっかりと[Amazon EC2 Pricing](http://aws.amazon.com/ec2/pricing/)について理解してください。
+
+## MPI クラスタの準備はできましたか? - Ready to run MPI programs on your cluster?
+ついに自分のMPIクラスタを手に入れたので、プログラムを実行しましょう。最初に[MPI hello world アプリケーション]({{ site.baseurl }}/tutorialss/mpi-hello-world/)のコンパイルと実行方法をチェックしてください。ローカルクラスタを構築して同じことを試したい場合は、[running an MPI cluster within a LAN]({{ site.baseurl }}/tutorialss/running-an-mpi cluster-within-a-lan)チュートリアルを参照してください。全てのレッスンは、[MPIチュートリアル]({{ site.baseurl }}/tutorials/)をチェックしてください。もしレッスンで何か問題があれば、以下にコメントを残してください。
\ No newline at end of file
diff --git a/tutorials/mpi-alltoall-and-v-routines/code/bin.c b/tutorials/mpi-alltoall-and-v-routines/code/bin.c
index 448ba00..cf166a5 100644
--- a/tutorials/mpi-alltoall-and-v-routines/code/bin.c
+++ b/tutorials/mpi-alltoall-and-v-routines/code/bin.c
@@ -2,7 +2,7 @@
// Copyright 2014 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// A program that bins random numbers using MPI_Alltoallv.
//
@@ -58,7 +58,7 @@ int *get_send_amounts_per_proc(float *rand_nums, int numbers_per_proc,
for (i = 0; i < numbers_per_proc; i++) {
int owning_rank = which_process_owns_this_number(rand_nums[i], world_size);
send_amounts_per_proc[owning_rank]++;
- }
+ }
return send_amounts_per_proc;
}
@@ -157,7 +157,7 @@ int main(int argc, char** argv) {
// when they are binned from this process.
int *send_amounts_per_proc = get_send_amounts_per_proc(rand_nums,
numbers_per_proc,
- world_size);
+ world_size);
// Determine how many numbers you will receive from each process. This
// information is needed to set up the binning call.
@@ -178,7 +178,7 @@ int main(int argc, char** argv) {
// are ordered by bin. For simplicity, we are simply going to sort the random
// numbers, however, this could be optimized since the numbers don't need to be
// fully sorted.
- qsort(rand_nums, numbers_per_proc, sizeof(float), &compare_float);
+ qsort(rand_nums, numbers_per_proc, sizeof(float), &compare_float);
// Perform the binning step with MPI_Alltoallv. This will send all of the numbers in
// the rand_nums array to their proper bin. Each process will only contain numbers
diff --git a/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c b/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c
index c53e405..b506e5c 100644
--- a/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c
+++ b/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Comparison of MPI_Bcast with the my_bcast function
//
@@ -78,5 +78,6 @@ int main(int argc, char** argv) {
printf("Avg MPI_Bcast time = %lf\n", total_mpi_bcast_time / num_trials);
}
+ free(data);
MPI_Finalize();
}
diff --git a/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c b/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c
index 7140c5b..b632a07 100644
--- a/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c
+++ b/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// An example of a function that implements MPI_Bcast using MPI_Send and
// MPI_Recv
diff --git a/tutorials/mpi-broadcast-and-collective-communication/index.md b/tutorials/mpi-broadcast-and-collective-communication/index.md
index cea903b..0223a56 100644
--- a/tutorials/mpi-broadcast-and-collective-communication/index.md
+++ b/tutorials/mpi-broadcast-and-collective-communication/index.md
@@ -3,13 +3,14 @@ layout: post
title: MPI Broadcast and Collective Communication
author: Wes Kendall
categories: Beginner MPI
+translations: zh_cn,ja_jp
tags: MPI_Barrier, MPI_Bcast, MPI_Wtime
redirect_from: '/mpi-broadcast-and-collective-communication/'
---
-So far in the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/), we have examined point-to-point communication, which is communication between two processes. This lesson is the start of the *collective communication* section. Collective communication is a method of communication which involves participation of **all** processes in a communicator. In this lesson, we will discuss the implications of collective communication and go over a standard collective routine - broadcasting.
+So far in the [MPI tutorials]({{ site.baseurl }}/tutorials/), we have examined point-to-point communication, which is communication between two processes. This lesson is the start of the *collective communication* section. Collective communication is a method of communication which involves participation of **all** processes in a communicator. In this lesson, we will discuss the implications of collective communication and go over a standard collective routine - broadcasting.
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-broadcast-and-collective-communication/code]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-broadcast-and-collective-communication/code]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code).
## Collective communication and synchronization points
One of the things to remember about collective communication is that it implies a *synchronization point* among processes. This means that all processes must reach a point in their code before they can all begin executing again.
@@ -55,7 +56,7 @@ MPI_Bcast(
Although the root process and receiver processes do different jobs, they all call the same `MPI_Bcast` function. When the root process (in our example, it was process zero) calls `MPI_Bcast`, the `data` variable will be sent to all other processes. When all of the receiver processes call `MPI_Bcast`, the `data` variable will be filled in with the data from the root process.
## Broadcasting with MPI_Send and MPI_Recv
-At first, it might seem that `MPI_Bcast` is just a simple wrapper around `MPI_Send` and `MPI_Recv`. In fact, we can make this wrapper function right now. Our function, called `my_bcast` is located in [bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/bcast.c). It takes the same arguments as `MPI_Bcast` and looks like this:
+At first, it might seem that `MPI_Bcast` is just a simple wrapper around `MPI_Send` and `MPI_Recv`. In fact, we can make this wrapper function right now. Our function, called `my_bcast` is located in [my_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c). It takes the same arguments as `MPI_Bcast` and looks like this:
```cpp
void my_bcast(void* data, int count, MPI_Datatype datatype, int root,
@@ -81,7 +82,7 @@ void my_bcast(void* data, int count, MPI_Datatype datatype, int root,
}
```
-The root process sends the data to everyone else while the others receive from the root process. Easy, right? If you run the my_bcast program from the *tutorials* directory of the [repo]({{ site.github.code }}), the output should look similar to this.
+The root process sends the data to everyone else while the others receive from the root process. Easy, right? If you run the my_bcast program from the *tutorials* directory of the [repo]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/), the output should look similar to this.
```
@@ -105,7 +106,7 @@ Do you think you can code this? Writing this code is a bit outside of the purpos
## Comparison of MPI_Bcast with MPI_Send and MPI_Recv
The `MPI_Bcast` implementation utilizes a similar tree broadcast algorithm for good network utilization. How does our broadcast function compare to `MPI_Bcast`? We can run `compare_bcast`, an example program included in the lesson code ([compare_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c)). Before looking at the code, let's first go over one of MPI's timing functions - `MPI_Wtime`. `MPI_Wtime` takes no arguments, and it simply returns a floating-point number of seconds since a set time in the past. Similar to C's `time` function, you can call multiple `MPI_Wtime` functions throughout your program and subtract their differences to obtain timing of code segments.
-Let's take a look of our code that compares my_bcast to MPI_Bcast.
+Let's take a look at our code that compares my_bcast to MPI_Bcast.
```cpp
for (i = 0; i < num_trials; i++) {
@@ -154,6 +155,6 @@ As you can see, there is no difference between the two implementations at two pr
Try running the code yourself and experiment at larger scales!
## Conclusions / up next
-Feel a little better about collective routines? In the [next MPI tutorial]({{ site.baseurl }}/mpi-scatter-gather-and-allgather/), I go over other essential collective communication routines - [gathering and scattering]({{ site.baseurl }}/mpi-scatter-gather-and-allgather/).
+Feel a little better about collective routines? In the [next MPI tutorial]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/), I go over other essential collective communication routines - [gathering and scattering]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/).
-For all beginner lessons, go the the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/).
\ No newline at end of file
+For all lessons, go the the [MPI tutorials]({{ site.baseurl }}/tutorials/) page.
diff --git a/tutorials/mpi-broadcast-and-collective-communication/ja_jp.md b/tutorials/mpi-broadcast-and-collective-communication/ja_jp.md
new file mode 100644
index 0000000..ab3222e
--- /dev/null
+++ b/tutorials/mpi-broadcast-and-collective-communication/ja_jp.md
@@ -0,0 +1,158 @@
+---
+layout: post
+title: MPIブロードキャストと集団通信 - MPI Broadcast and Collective Communication
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Barrier, MPI_Bcast, MPI_Wtime
+redirect_from: '/mpi-broadcast-and-collective-communication/'
+---
+
+ここまでの[チュートリアル]({{ site.baseurl }}/tutorials/)では、2プロセス間のポイントツーポイント通信を説明してきました。このレッスンから集団通信(Collective Communication)について学びます。集団通信はコミュニケータ内の*すべての*プロセスが関係する通信です。このレッスンでは、最初に集団通信の意味を確認し、標準的な集団通信のルーチンであるブロードキャスト(Broadcast)について説明します。
+
+> **Note** - このサイトのコードはすべて[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは[tutorials/mpi-broadcast-and-collective-communication/code]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code)にあります。
+
+## 集団通信と同期 - Collective communication and synchronization points
+集団通信を学んでいくために、最初に覚えておくべきことの1つは、プロセス間の同期が必要になるということです。つまり、すべての関連するプロセスがコード内の特定の集団通信を完了しなければ、すべてのプロセスが再び実行を開始できません。
+
+同期についてさらに詳しく説明します。MPIにはプロセスの同期専用の特別な関数があります。
+
+
+```cpp
+MPI_Barrier(MPI_Comm communicator)
+```
+
+バリアという非常にわかりやすい名前がついています。この関数は、コミュニケータ内のすべてのプロセスがこの関数を呼ぶまで全てのプロセスはこの関数でブロック(訳注:つまり、あるプロセスだけが先に進むことをバリア)します。下図の横軸がプログラム実行の時間を示し、各円はプロセスを示します。
+
+
+それぞれの図を見ていきましょう。T1では、プロセス0が`MPI_Barrier`に達しました。T2ではプロセス0はバリア関数でブロックされ、この間にプロセス1と3がバリア関数に到達しました。T3でプロセス2がようやくバリアに到達します。この結果、T4のようにすべてのプロセスが再び実行を進められます。
+
+`MPI_Barrier`の利用用途はいくつかあります。最も主な使途は並列プログラムで正確な時間測定(timed accurately)をするためです。
+
+`MPI_Barrier` はどのように実装されているのでしょうか?[sending and receiving tutorial]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)のレッスンで学んだリングプログラムを思い出してください。トークンをリングのようにすべてのプロセスに渡すプログラムでした。この実装は全てのプロセスが処理を終えなければこの処理は終わらないのでバリアを実装するの1つの方式です。
+
+繰り返しになりますが、すべての集団通信は同期されている。言い換えると、そのルーチンを`MPI_Barrier`とした時にバリアが完了しないような状態ができてしまうと集団通信は正常に完了できません。`MPI_Barrier`や集団通信の関数をコミュニケータ内の全てのプロセスが呼び出すことを保証せずに呼び出そうとするとプログラムはブロック状態のまま先に進めません。これは初学者にとって非常に分かりにくいので覚えておいてください!
+
+## MPI_Bcast によるブロードキャスト - Broadcasting with MPI_Bcast
+ブロードキャスト(broadcast)は最も基本的な集団通信の1つです。ブロードキャストはある1つのプロセスがコミュニケータ内のすべてのプロセスに同じデータを送信します。ユーザ入力や構成パラメータをすべてのプロセスに送信するために使うことができます。
+
+ブロードキャストの例を次に示します。
+
+
+
+この例はプロセス0がルートプロセスでとなり、オリジナルのデータを保持しています。他のすべてのプロセスはデータのコピーを受け取ります。
+
+MPIにはブロードキャストを実現する`MPI_Bcast`関数があります。
+
+```cpp
+MPI_Bcast(
+ void* data,
+ int count,
+ MPI_Datatype datatype,
+ int root,
+ MPI_Comm communicator)
+```
+
+ルートプロセスは送信をし、他のプロセスは受信を行うのですが共通して`MPI_Bcast`関数を呼びます。ルートプロセス(この例ではプロセス0)が`MPI_Bcast`を呼ぶと`data`が他のすべてのプロセスに送信されます。すべての受信プロセスは`MPI_Bcast`でルート・プロセスからの`data`を受け取ります。
+
+## MPI_Send と MPI_Recv によるブロードキャスト - Broadcasting with MPI_Send and MPI_Recv
+`MPI_Bcast`は`MPI_Send`と`MPI_Recv`のラッパーなのでしょうか?実際、このラッパーはsendとrecvを使って簡単に実装することもできます。[my_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c)に示す`my_bcast`という関数は、`MPI_Bcast`と同じ引数をとる自作のブロードキャスト関数です。
+
+```cpp
+void my_bcast(void* data, int count, MPI_Datatype datatype, int root,
+ MPI_Comm communicator) {
+ int world_rank;
+ MPI_Comm_rank(communicator, &world_rank);
+ int world_size;
+ MPI_Comm_size(communicator, &world_size);
+
+ if (world_rank == root) {
+ // ルートプロセスはforで各プロセスにデータを送る
+ int i;
+ for (i = 0; i < world_size; i++) {
+ if (i != world_rank) {
+ MPI_Send(data, count, datatype, i, 0, communicator);
+ }
+ }
+ } else {
+ // ルートでないプロセスはルートプロセスからのデータを受け取る
+ MPI_Recv(data, count, datatype, root, 0, communicator,
+ MPI_STATUS_IGNORE);
+ }
+}
+```
+
+コメントの通りで、ルートプロセスが他のプロセスにデータを送り、他のプロセスはルートプロセスからデータを受け取る。とても簡単ですね。[レポジトリ]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/)のチュートリアルディレクトリからmy_bcastプログラムを実行してみましょう。
+
+```
+>>> cd tutorials
+>>> ./run.py my_bcast
+mpirun -n 4 ./my_bcast
+Process 0 broadcasting data 100
+Process 2 received data 100 from root process
+Process 3 received data 100 from root process
+Process 1 received data 100 from root process
+```
+
+動作はしますが、この自作関数は非常に非効率的です。各プロセスには送信/受信ネットワークのリンクが1つしかないのです。つまりプロセス0から常に1つのネットワーク リンクのみを使用することを繰り返してすべてのデータを送信します。ネットワークリンクを一度に多く使用できる賢い方法を考えましょう。ツリー(木)ベースの通信アルゴリズムです。
+
+
+
+この図を説明します。最初のステップでプロセス0はデータをプロセス1に送信します。次のステップでプロセス0もデータをプロセス2に送信します。さらにプロセス1はプロセス3にデータを送信します。他のプロセスがルートプロセスを助けて2つのネットワーク接続がブロードキャストに使用されています。このようにすべてのプロセスがデータを受信するまで各ステップごとにネットワークの使用率は2倍になります。
+
+この実装コードを書くことはレッスンの目的から少し外れます。詳細が気になる場合は[Parallel Programming with MPI](http://www.amazon.com/gp/product/1558603395/ref=as_li_qf_sp_asin_tl?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=1558603395)を参照してください。コードの問題の完全な例が掲載されている優れた本です。
+
+## MPI_Bcast と MPI_Send および MPI_Recv との比較 - Comparison of MPI_Bcast with MPI_Send and MPI_Recv
+`MPI_Bcast`はネットワーク利用効率の改善のために、今紹介したようなツリー型のブロードキャストアルゴリズムを利用しています。自作ブロードキャストを`MPI_Bcast`と比較してみましょう。このための`compare_bcast`を実行します。`compare_bcast`は、レッスンコードに含まれているサンプルプログラムです([compare_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c))。コードの説明の前にまずはMPIのタイミング(timing)関数の1つである`MPI_Wtime`の説明をします。`MPI_Wtime`は引数を取らず、過去の実行の秒数を浮動小数点数で返します。うまく使用することでCの`time`関数と同じようにプログラム中で複数の`MPI_Wtime`関数を呼び出して、差分を引くことでセグメントごとの時間を取得することができる関数です。
+
+`my_bcast` と `MPI_Bcast` を比較するコードを書きます。
+
+```cpp
+for (i = 0; i < num_trials; i++) {
+ // Time my_bcast
+ // バリアをして事前時間を同期する
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_my_bcast_time -= MPI_Wtime();
+ my_bcast(data, num_elements, MPI_INT, 0, MPI_COMM_WORLD);
+ // Synchronize again before obtaining final time
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_my_bcast_time += MPI_Wtime();
+
+ // Time MPI_Bcast
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_mpi_bcast_time -= MPI_Wtime();
+ MPI_Bcast(data, num_elements, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_mpi_bcast_time += MPI_Wtime();
+}
+```
+
+`num_trials`は実行する回数を示す変数です。 2つの関数ごとに実行時間を計算します。そして平均時間をプログラムの最後で出力します。 コード全体は、[compare_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c)を見てください。[レッスンコード]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code)で確認できます。
+
+[レポジトリ]({{ site.github.code }})の compare_bcast プログラムを実行すると出力は次のようになります。
+
+```
+>>> cd tutorials
+>>> ./run.py compare_bcast
+/home/kendall/bin/mpirun -n 16 -machinefile hosts ./compare_bcast 100000 10
+Data size = 400000, Trials = 10
+Avg my_bcast time = 0.510873
+Avg MPI_Bcast time = 0.126835
+```
+
+スクリプトは、16個のプロセッサにおいて1ブロードキャスト呼び出しあたり100,000 個の整数を送ることを10回試行します。Ethernet経由で接続された16プロセッサを使用した私の環境ではmy_bcastとMPI実装の間に大きな実行時間の差が生まれました。以下は、さまざまなプロセッサ数での結果です。
+
+| Processors | my_bcast | MPI_Bcast |
+| --- | --- | --- |
+| 2 | 0.0344 | 0.0344 |
+| 4 | 0.1025 | 0.0817 |
+| 8 | 0.2385 | 0.1084 |
+| 16 | 0.5109 | 0.1296 |
+
+2つのプロセッサでは2つの実装の時間差はありません。これは`MPI_Bcast`がツリー実装を使ったとしても、2つのプロセッサの場合はネットワーク使用率には違いがないためです。ただし16プロセッサまで増やすと違いがはっきりとわかります。
+
+試してみてください!
+
+## Conclusions / up next
+集団通信に慣れてきましたか?次は[ScatterとGather]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/)を学びましょう!
+
+その他のレッスンは[MPIチュートリアル]({{ site.baseurl }}/tutorials/)をみてください。
diff --git a/tutorials/mpi-broadcast-and-collective-communication/zh_cn.md b/tutorials/mpi-broadcast-and-collective-communication/zh_cn.md
new file mode 100644
index 0000000..25758c9
--- /dev/null
+++ b/tutorials/mpi-broadcast-and-collective-communication/zh_cn.md
@@ -0,0 +1,157 @@
+---
+layout: post
+title: MPI 广播以及集体(collective)通信
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Barrier, MPI_Bcast, MPI_Wtime
+redirect_from: '/mpi-broadcast-and-collective-communication/zh_cn'
+---
+
+[MPI 教程]({{ site.baseurl }}/tutorials/) 到目前为止,我们讲解了点对点的通信,这种通信只会同时涉及两个不同的进程。这节课是我们 MPI *集体通信*(collective communication)的第一节课。集体通信指的是一个涉及 communicator 里面所有进程的一个方法。这节课我们会解释集体通信以及一个标准的方法 - broadcasting (广播)。
+
+> **注意** - 这个网站的提到的所有代码都在 [GitHub]({{ site.github.repo }}) 上面。这篇教程的代码在 [tutorials/mpi-broadcast-and-collective-communication/code]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code)。
+
+## 集体通信以及同步点
+关于集体通信需要记住的一点是它在进程间引入了同步点的概念。这意味着所有的进程在执行代码的时候必须首先*都*到达一个同步点才能继续执行后面的代码。
+
+在看具体的集体通信方法之前,让我们更仔细地看一下同步这个概念。事实上,MPI 有一个特殊的函数来做同步进程的这个操作。
+
+```cpp
+MPI_Barrier(MPI_Comm communicator)
+```
+
+这个函数的名字十分贴切(Barrier,屏障)- 这个方法会构建一个屏障,任何进程都没法跨越屏障,直到所有的进程都到达屏障。这边有一个示意图。假设水平的轴代表的是程序的执行,小圆圈代表不同的进程。
+
+
+
+进程0在时间点 (T 1) 首先调用 `MPI_Barrier`。然后进程0就一直等在屏障之前,之后进程1和进程3在 (T 2) 时间点到达屏障。当进程2最终在时间点 (T 3) 到达屏障的时候,其他的进程就可以在 (T 4) 时间点再次开始运行。
+
+`MPI_Barrier` 在很多时候很有用。其中一个用途是用来同步一个程序,使得分布式代码中的某一部分可以被精确的计时。
+
+想知道 `MPI_Barrier` 是怎么实现的么?我知道你当然想 :-) 还记得我们之前的在[发送和接收教程]({{ site.baseurl }}/tutorials/mpi-send-and-receive/zh_cn) 里的环程序么?帮你回忆一下,我们当时写了一个在所有进程里以环的形式传递一个令牌(token)的程序。这种形式的程序是最简单的一种实现屏障的方式,因为令牌只有在所有程序都完成之后才能被传递回第一个进程。
+
+关于同步最后一个要注意的地方是:始终记得每一个你调用的集体通信方法都是同步的。也就是说,如果你没法让所有进程都完成 `MPI_Barrier`,那么你也没法完成任何集体调用。如果你在没有确保所有进程都调用 `MPI_Barrier` 的情况下调用了它,那么程序会空闲下来。这对初学者来说会很迷惑,所以小心这类问题。
+
+## 使用 MPI_Bcast 来进行广播
+*广播* (broadcast) 是标准的集体通信技术之一。一个广播发生的时候,一个进程会把同样一份数据传递给一个 communicator 里的所有其他进程。广播的主要用途之一是把用户输入传递给一个分布式程序,或者把一些配置参数传递给所有的进程。
+
+广播的通信模式看起来像这样:
+
+
+
+在这个例子里,进程0是我们的*根*进程,它持有一开始的数据。其他所有的进程都会从它这里接受到一份数据的副本。
+
+在 MPI 里面,广播可以使用 `MPI_Bcast` 来做到。函数签名看起来像这样:
+
+```cpp
+MPI_Bcast(
+ void* data,
+ int count,
+ MPI_Datatype datatype,
+ int root,
+ MPI_Comm communicator)
+```
+
+尽管根节点和接收节点做不同的事情,它们都是调用同样的这个 `MPI_Bcast` 函数来实现广播。当根节点(在我们的例子是节点0)调用 `MPI_Bcast` 函数的时候,`data` 变量里的值会被发送到其他的节点上。当其他的节点调用 `MPI_Bcast` 的时候,`data` 变量会被赋值成从根节点接受到的数据。
+
+## 使用 MPI_Send 和 MPI_Recv 来做广播
+粗略看的话,似乎 `MPI_Bcast` 仅仅是在 `MPI_Send` 和 `MPI_Recv` 基础上进行了一层包装。事实上,我们现在就可以自己来做这层封装。我们的函数叫做 `my_bcast`,在这里可以看到: [my_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/my_bcast.c)。它跟 `MPI_Bcast` 接受一样的参数,看起来像这样:
+
+```cpp
+void my_bcast(void* data, int count, MPI_Datatype datatype, int root,
+ MPI_Comm communicator) {
+ int world_rank;
+ MPI_Comm_rank(communicator, &world_rank);
+ int world_size;
+ MPI_Comm_size(communicator, &world_size);
+
+ if (world_rank == root) {
+ // If we are the root process, send our data to everyone
+ int i;
+ for (i = 0; i < world_size; i++) {
+ if (i != world_rank) {
+ MPI_Send(data, count, datatype, i, 0, communicator);
+ }
+ }
+ } else {
+ // If we are a receiver process, receive the data from the root
+ MPI_Recv(data, count, datatype, root, 0, communicator,
+ MPI_STATUS_IGNORE);
+ }
+}
+```
+
+根节点把数据传递给所有其他的节点,其他的节点接收根节点的数据。很简单对吧?如果你从这个 [repo]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/) *tutorials* 目录下面运行这个程序的话,输出看起来应该像这样:
+
+
+```
+>>> cd tutorials
+>>> ./run.py my_bcast
+mpirun -n 4 ./my_bcast
+Process 0 broadcasting data 100
+Process 2 received data 100 from root process
+Process 3 received data 100 from root process
+Process 1 received data 100 from root process
+```
+
+不管你信不信,其实我们的函数效率特别低!假设每个进程都只有一个「输出/输入」网络连接。我们的方法只是使用了进程0的一个输出连接来传递数据。比较聪明的方法是使用一个基于树的沟通算法对网络进行更好的利用。比如这样:
+
+
+
+在示意图里,进程0一开始传递数据给进程1。跟我们之前的例子类似,第二个阶段的时候进程0依旧会把数据传递给进程2。这个例子中不同的是进程1在第二阶段也会传递数据给进程3。在第二阶段,两个网络连接在同时发生了。在这个树形算法里,能够利用的网络连接每个阶段都会比前一阶段翻番,直到所有的进程接受到数据为止。
+
+你觉得你能用代码把这个算法实现么?实现这个算法有点超出我们这个课的主要目的了,如果你觉得你足够勇敢的话,可以去看这本超酷的书:[Parallel Programming with MPI](http://www.amazon.com/gp/product/1558603395/ref=as_li_qf_sp_asin_tl?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=1558603395) 这本书里面有完整的代码。
+
+## MPI_Bcast 和 MPI_Send 以及 MPI_Recv 的比较
+`MPI_Bcast` 的实现使用了一个类似的树形广播算法来获得比较好的网络利用率。我们的实现跟 `MPI_Bcast` 比起来怎么样呢?我们可以运行 `compare_bcast`,在课程代码里我们提供了这个程序 ([compare_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c))。在看代码之前,先让我们看一个 MPI 跟时间相关的函数 - `MPI_Wtime`。`MPI_Wtime` 不接收参数,它仅仅返回以浮点数形式展示的从1970-01-01到现在为止进过的秒数,跟 C 语言的 `time` 函数类似。我们可以多次调用 `MPI_Wtime` 函数,并去差值,来计算我们的代码运行的时间。
+
+让我们看一下我们的比较代码:
+
+```cpp
+for (i = 0; i < num_trials; i++) {
+ // Time my_bcast
+ // Synchronize before starting timing
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_my_bcast_time -= MPI_Wtime();
+ my_bcast(data, num_elements, MPI_INT, 0, MPI_COMM_WORLD);
+ // Synchronize again before obtaining final time
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_my_bcast_time += MPI_Wtime();
+
+ // Time MPI_Bcast
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_mpi_bcast_time -= MPI_Wtime();
+ MPI_Bcast(data, num_elements, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Barrier(MPI_COMM_WORLD);
+ total_mpi_bcast_time += MPI_Wtime();
+}
+```
+代码里的 `num_trials` 是一个指明一共要运行多少次实验的变量。我们分别记录两个函数运行所需的累加时间,平均的时间会在程序结束的时候打印出来。完整的代码在 [compare_bcast.c]({{ site.github.code }}/tutorials/mpi-broadcast-and-collective-communication/code/compare_bcast.c)
+
+如果你从这个 [repo]({{ site.github.code }}) *tutorials* 目录下面运行这个程序的话,输出看起来应该像这样:
+
+```
+>>> cd tutorials
+>>> ./run.py compare_bcast
+/home/kendall/bin/mpirun -n 16 -machinefile hosts ./compare_bcast 100000 10
+Data size = 400000, Trials = 10
+Avg my_bcast time = 0.510873
+Avg MPI_Bcast time = 0.126835
+```
+
+我们指定了16个进程来运行代码,每次广播发送 100,000 个整数,然后每次运行跑10个循环。如你所见,我的实验使用了通过网络连接起来的16个进程,结果显示运行我们的实现和 MPI 官方的实现体现了明显的时间差异。这里是一些不同进程数目运行时候的时间差异:
+
+| Processors | my_bcast | MPI_Bcast |
+| --- | --- | --- |
+| 2 | 0.0344 | 0.0344 |
+| 4 | 0.1025 | 0.0817 |
+| 8 | 0.2385 | 0.1084 |
+| 16 | 0.5109 | 0.1296 |
+
+可以看到,2个进程运行的时候是没有时间差异的。这是因为 `MPI_Bcast` 的树算法在使用两个进程的时候并没有提供额外的网络利用率。然而,进程数量稍微增加到即使只有16个的时候我们也可以看到明显的差异。
+
+试着自己运行一下代码,用更多的进程试试!
+
+## 结论 / 接下来
+现在对集体通信接口有了更好的理解么?在[接下来的教程]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/zh_cn)里,我会介绍另外的几个常用集体通信接口 - [gathering and scattering]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/zh_cn).
+需要看所有教程的话,可以去 [MPI 教程]({{ site.baseurl }}/tutorials/) 页面。
diff --git a/tutorials/mpi-hello-world/code/mpi_hello_world.c b/tutorials/mpi-hello-world/code/mpi_hello_world.c
index fe131d4..ea1ce89 100644
--- a/tutorials/mpi-hello-world/code/mpi_hello_world.c
+++ b/tutorials/mpi-hello-world/code/mpi_hello_world.c
@@ -2,14 +2,13 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// An intro MPI hello world program that uses MPI_Init, MPI_Comm_size,
// MPI_Comm_rank, MPI_Finalize, and MPI_Get_processor_name.
//
#include
#include
-#include
int main(int argc, char** argv) {
// Initialize the MPI environment. The two arguments to MPI Init are not
diff --git a/tutorials/mpi-hello-world/index.md b/tutorials/mpi-hello-world/index.md
index 6799630..b854bee 100644
--- a/tutorials/mpi-hello-world/index.md
+++ b/tutorials/mpi-hello-world/index.md
@@ -3,19 +3,21 @@ layout: post
title: MPI Hello World
author: Wes Kendall
categories: Beginner MPI
+translations: zh_cn,ja_jp
tags: MPI_Comm_rank, MPI_Comm_size, MPI_Finalize, MPI_Get_processor_name, MPI_Init
redirect_from: '/mpi-hello-world/'
---
In this lesson, I will show you a basic MPI hello world application and also discuss how to run an MPI program. The lesson will cover the basics of initializing MPI and running an MPI job across several processes. This lesson is intended to work with installations of MPICH2 (specifically 1.4). If you have not installed MPICH2, please refer back to the [installing MPICH2 lesson]({{ site.baseurl }}/tutorials/installing-mpich2/).
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-hello-world/code]({{ site.github.code }}/tutorials/mpi-hello-world/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-hello-world/code]({{ site.github.code }}/tutorials/mpi-hello-world/code).
## Hello world code examples
Let's dive right into the code from this lesson located in [mpi_hello_world.c]({{ site.github.code }}/tutorials/mpi-hello-world/code/mpi_hello_world.c). Below are some excerpts from the code.
```cpp
-#include ;
+#include
+#include
int main(int argc, char** argv) {
// Initialize the MPI environment
@@ -35,8 +37,7 @@ int main(int argc, char** argv) {
MPI_Get_processor_name(processor_name, &name_len);
// Print off a hello world message
- printf("Hello world from processor %s, rank %d"
- " out of %d processors\n",
+ printf("Hello world from processor %s, rank %d out of %d processors\n",
processor_name, world_rank, world_size);
// Finalize the MPI environment.
@@ -44,7 +45,7 @@ int main(int argc, char** argv) {
}
```
-You will notice that the first step to building an MPI program is including the MPI header files with `#include ;`. After this, the MPI environment must be initialized with:
+You will notice that the first step to building an MPI program is including the MPI header files with `#include `. After this, the MPI environment must be initialized with:
```cpp
MPI_Init(
@@ -68,7 +69,7 @@ MPI_Comm_size(
MPI_Comm_rank(
MPI_Comm communicator,
int* rank)
-```
+```
`MPI_Comm_rank` returns the rank of a process in a communicator. Each process inside of a communicator is assigned an incremental rank starting from zero. The ranks of the processes are primarily used for identification purposes when sending and receiving messages.
@@ -107,7 +108,7 @@ clean:
rm ${EXECS}
```
-My makefile looks for the MPICC environment variable. If you installed MPICH2 to a local directory, set your MPICC environment variable to point to your mpicc binary. The mpicc program in your installation is really just a wrapper around gcc, and it makes compiling and linking all of the necessary MPI routines much easier.
+My makefile looks for the MPICC environment variable. If you installed MPICH2 to a local directory, set your MPICC environment variable to point to your mpicc binary. The mpicc program in your installation is really just a wrapper around gcc, and it makes compiling and linking all of the necessary MPI routines much easier.
```
>>> export MPICC=/home/kendall/bin/mpicc
@@ -145,7 +146,7 @@ Hello world from processor cetus3, rank 2 out of 4 processors
As expected, the MPI program was launched across all of the hosts in my host file. Each process was assigned a unique rank, which was printed off along with the process name. As one can see from my example output, the output of the processes is in an arbitrary order since there is no synchronization involved before printing.
-Notice how the script called mpirun. This is program that the MPI implementation uses to launch the job. Processes are spawned across all the hosts in the host file and the MPI program executes across each process. My script automatically supplies the *-n* flag to set the number of MPI processes to four. Try changing the run script and launching more processes! Don't accidentally crash your system though. :-)
+Notice how the script called mpirun. This is the program that the MPI implementation uses to launch the job. Processes are spawned across all the hosts in the host file and the MPI program executes across each process. My script automatically supplies the *-n* flag to set the number of MPI processes to four. Try changing the run script and launching more processes! Don't accidentally crash your system though. :-)
Now you might be asking, *"My hosts are actually dual-core machines. How can I get MPI to spawn processes across the individual cores first before individual machines?"* The solution is pretty simple. Just modify your hosts file and place a colon and the number of cores per processor after the host name. For example, I specified that each of my hosts has two cores.
@@ -157,7 +158,7 @@ cetus3:2
cetus4:2
```
-When I execute the run script again, *voila!*, the MPI job spawns two processes on only two of my hosts.
+When I execute the run script again, *voila!*, the MPI job spawns four processes on only two of my hosts.
```
>>> ./run.py mpi_hello_world
@@ -169,6 +170,6 @@ Hello world from processor cetus1, rank 1 out of 4 processors
```
## Up next
-Now that you have a basic understanding of how an MPI program is executed, it is now time to learn fundamental point-to-point communication routines. In the next lesson, I cover [basic sending and receiving routines in MPI]({{ site.baseurl }}/tutorials/mpi-send-and-receive/). Feel free to also examine the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/) for a complete reference of all of the beginning MPI lessons.
+Now that you have a basic understanding of how an MPI program is executed, it is now time to learn fundamental point-to-point communication routines. In the next lesson, I cover [basic sending and receiving routines in MPI]({{ site.baseurl }}/tutorials/mpi-send-and-receive/). Feel free to also examine the [MPI tutorials]({{ site.baseurl }}/tutorials/) for a complete reference of all of the MPI lessons.
-Having trouble? Confused? Feel free to leave a comment below and perhaps I or another reader can be of help.
\ No newline at end of file
+Having trouble? Confused? Feel free to leave a comment below and perhaps I or another reader can be of help.
diff --git a/tutorials/mpi-hello-world/ja_jp.md b/tutorials/mpi-hello-world/ja_jp.md
new file mode 100644
index 0000000..f8643a2
--- /dev/null
+++ b/tutorials/mpi-hello-world/ja_jp.md
@@ -0,0 +1,186 @@
+---
+layout: post
+title: 初めてのMPIプログラム - MPI Hello World
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/mpi-hello-world/'
+---
+
+このレッスンでは基本的なMPI Hello Worldアプリケーションの作成方法とMPIプログラムの実行方法を説明します。MPIの初期化と複数のプロセスにわたるMPIジョブの実行の基本について説明します。このレッスンは、MPICH2(1.4)のインストールで動作することを確認しています。MPICH2をインストールしていない場合は[installing MPICH2 lesson]({{ site.baseurl }}/tutorials/installing-mpich2/)を参照してください。
+
+
+> **Note** : このサイトのコードはすべて [GitHub]({{ site.github.repo }})にあります。このチュートリアルのコードは[tutorials/mpi-hello-world/code]({{ site.github.code }}/tutorials/mpi-hello-world/code)にあります。
+
+## Hello, World!: Hello world code examples
+それでは[mpi_hello_world.c]({{ site.github.code }}/tutorials/mpi-hello-world/code/mpi_hello_world.c)にあるコードを読んでいきます。
+
+
+```cpp
+#include
+#include
+
+int main(int argc, char** argv) {
+ // Initialize the MPI environment
+ // MPI環境の初期化
+ MPI_Init(NULL, NULL);
+
+ // Get the number of processes
+ // プロセスの数を得る
+ int world_size;
+ MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+ // Get the rank of the process
+ // このコミュニケータ内の自分のランクを得る
+ int world_rank;
+ MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+
+ // Get the name of the processor
+ // このプロセッサーの名前を得る
+ char processor_name[MPI_MAX_PROCESSOR_NAME];
+ int name_len;
+ MPI_Get_processor_name(processor_name, &name_len);
+
+ // Print off a hello world message
+ // Hello, World!を表示する。
+ printf("Hello world from processor %s, rank %d out of %d processors\n",
+ processor_name, world_rank, world_size);
+
+ // Finalize the MPI environment.
+ // MPI環境をクローズする
+ MPI_Finalize();
+}
+```
+
+MPIプログラムではMPIヘッダのインクルードが必要です(`#include `)。次にMPI環境を初期化する必要があります。
+
+
+
+```cpp
+MPI_Init(
+ int* argc,
+ char*** argv)
+```
+
+`MPI_Init`: MPIのグローバル変数と内部変数が初期化・設定されます。このプログラムでは、全てのプロセスのそれぞれにランクを割り当て、それら全てを含むコミュニケータを作成します。`MPI_Init`は特に設定する引数がありません。追加のパラメータは将来の実装で必要になった場合に備えて予約されています。
+
+`MPI_Init`の後には2つの関数を呼び出します。これらはほとんど全てのMPIプログラムで呼び出されるコードです。
+
+```cpp
+MPI_Comm_size(
+ MPI_Comm communicator,
+ int* size)
+```
+
+`MPI_Comm_size`はコミュニケータのサイズを返します。サンプルで引数に与えている`MPI_COMM_WORLD`(MPIにより初期化される変数)はジョブ内のすべてのプロセスを含むのでこの呼び出しはジョブに要求したプロセスの数を返します。
+
+```cpp
+MPI_Comm_rank(
+ MPI_Comm communicator,
+ int* rank)
+```
+
+`MPI_Comm_rank`:コミュニケータ内におけるそのプロセスのランクを返します。コミュニケータ内の各プロセスには0から順にランクが割り当てられます。ランクは主にメッセージの送受信のために使用されます。
+
+次の関数は実際のコードではあまり使われることはないでしょう。
+
+```cpp
+MPI_Get_processor_name(
+ char* name,
+ int* name_length)
+```
+
+`MPI_Get_processor_name`はプロセッサの名前を取得します。最後に呼ばれる関数も見ていきましょう。
+
+```cpp
+MPI_Finalize()
+```
+
+`MPI_Finalize`はMPI環境のクリーンアップです。この関数の後はMPI関数を使うことはできません。
+
+## アプリケーションの実行: Running the MPI hello world application
+gitからコードをcloneしてコードのフォルダを見てみましょう。その中にはMakefileがあります。
+
+```
+>>> git clone {{ site.github.repo }}
+>>> cd mpitutorial/tutorials/mpi-hello-world/code
+>>> cat makefile
+EXECS=mpi_hello_world
+MPICC?=mpicc
+
+all: ${EXECS}
+
+mpi_hello_world: mpi_hello_world.c
+ ${MPICC} -o mpi_hello_world mpi_hello_world.c
+
+clean:
+ rm ${EXECS}
+```
+
+このmakefileはMPICC環境変数が設定されているならばそれを用います。MPICH2をローカルディレクトリにインストールした場合は、MPICC環境変数を適切なmpiccバイナリパスを指すように設定してください。mpiccは必要なライブラリやインクルードを行ってくれるgccのラッパーです。
+
+```
+>>> export MPICC=/home/kendall/bin/mpicc
+>>> make
+/home/kendall/bin/mpicc -o mpi_hello_world mpi_hello_world.c
+```
+
+プログラムのコンパイルが終わり実行の準備が整いました!複数のノードのクラスターでMPIプログラムを実行する場合はホストファイルをセットアップする必要があることに注意してください。単一のマシンでMPIを実行するだけの場合は次の情報は無視してください。
+
+host_fileには、MPIジョブが実行されるすべてのコンピューターのhostnameが含まれています。実行を容易にするために、これらのホストにSSHアクセスできることを確認する必要があります。また、SSHのパスワードプロンプトを回避するために[setup an authorized keys file](http://www.eng.cam.ac.uk/help/jpmg/ssh/authorized_keys_howto.html)を設定する必要があります。たとえばhost_fileの例を示します。
+
+```
+>>> cat host_file
+cetus1
+cetus2
+cetus3
+cetus4
+```
+gitレポジトリに含まれるrun.pyで複数のホストを用いるのにはMPI_HOSTSという環境変数を設定する必要があります。これを設定してMPIジョブが起動されると、実行コマンドラインにホストファイルが自動的に含められます。hostファイルが必要ない場合は環境変数は設定不要です。また、MPIのローカルインストールで特定のmpirunバイナリを指したい場合はMPIRUN環境変数を設定する必要があります。
+
+これが完了したら、レポジトリに含まれているrun.pyを使用できます。このスクリプトは*tutorials*ディレクトリに保存されており、すべてのチュートリアルの任意のプログラムを実行できます(実行前に実行可能ファイルのビルドも試行します)。mpitutorialフォルダから次の操作を試してください。
+
+
+```
+>>> export MPIRUN=/home/kendall/bin/mpirun
+>>> export MPI_HOSTS=host_file
+>>> cd tutorials
+>>> ./run.py mpi_hello_world
+/home/kendall/bin/mpirun -n 4 -f host_file ./mpi_hello_world
+Hello world from processor cetus2, rank 1 out of 4 processors
+Hello world from processor cetus1, rank 0 out of 4 processors
+Hello world from processor cetus4, rank 3 out of 4 processors
+Hello world from processor cetus3, rank 2 out of 4 processors
+```
+
+想定通りMPIプログラムはホストファイル内のすべてのホストで動作しました。各プロセスは一意の(ユニークな)ランクを持ち、プロセス名とともに出力されました。出力例からわかるように、実行順序は同期がしていないためプロセスの出力はランダムになっています。
+
+スクリプトはmpirunを呼び出していることに注目してください。これは、MPIがジョブを起動するために使用するプログラムです。このプログラムはプロセスをホストファイル内のホストで生成し、実際のプログラムは各プロセスで実行されます。このスクリプトではMPIプロセスの数を4に設定するために`-n`フラグを自動的に提供します。実行スクリプトを変更して、より多くのプロセスを起動してみてください。ただし、誤ってシステムをクラッシュさせないようにしてくださいね。:-)
+
+さて「私のコンピュータはマルチコアなので他のノードよりも先にあるノードのコアを使いたいのだが」と思っていますか?これは簡単に制御できます。ホストファイルを変更し、ホスト名の後にコロンとプロセッサあたりのコア数を入力すれば良いです。たとえば、各ホストの2つのコアを使いたいとしましょう。
+
+```
+>>> cat host_file
+cetus1:2
+cetus2:2
+cetus3:2
+cetus4:2
+```
+
+実行スクリプトを再度実行するとMPIジョブによって2つのホストのみで4つのプロセスが生成されました!(訳注:hostsは8個のプロセスを処理できることを示しますが、要求されたのは4個のプロセスのみなので2つのホストだけが使われています)
+
+```
+>>> ./run.py mpi_hello_world
+/home/kendall/bin/mpirun -n 4 -f host_file ./mpi_hello_world
+Hello world from processor cetus1, rank 0 out of 4 processors
+Hello world from processor cetus2, rank 2 out of 4 processors
+Hello world from processor cetus2, rank 3 out of 4 processors
+Hello world from processor cetus1, rank 1 out of 4 processors
+```
+
+## 次に: Up next
+MPIプログラムの実行方法について基本的な理解ができたので、次は基本的なポイントツーポイント通信ルーチンを学習します。次のレッスンは[MPI の基本的な送信ルーチンと受信ルーチン: basic sending and receiving routines in MPI]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)です。
+[MPI tutorials]({{ site.baseurl }}/tutorials/)をMPIレッスンの完全なリファレンスとして活用してください。
+
+困っていますか?混乱していますか?お気軽に下記にコメントを残してください。私や他の読者がお役に立てるかもしれません。
+
diff --git a/tutorials/mpi-hello-world/zh_cn.md b/tutorials/mpi-hello-world/zh_cn.md
new file mode 100644
index 0000000..58c15cb
--- /dev/null
+++ b/tutorials/mpi-hello-world/zh_cn.md
@@ -0,0 +1,177 @@
+---
+layout: post
+title: MPI Hello World
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Comm_rank, MPI_Comm_size, MPI_Finalize, MPI_Get_processor_name, MPI_Init
+redirect_from: '/mpi-hello-world/zh_cn'
+---
+
+在这个课程里,在展示一个基础的 MPI Hello World 程序的同时我会介绍一下该如何运行 MPI 程序。这节课会涵盖如何初始化 MPI 的基础内容以及让 MPI 任务跑在几个不同的进程上。这节课程的代码是在 MPICH2(当时是1.4版本)上面运行通过的。(译者在 MPCH-3.2.1 上运行程序也没有问题)。如果你还没装 MPICH2,你参考[MPICH2 安装指南]({{ site.baseurl }}/tutorials/installing-mpich2/)
+
+> **注意** - 这个网站的提到的所有代码都在 [GitHub]({{ site.github.repo }}) 上面。这篇教程的代码在 [tutorials/mpi-hello-world/code]({{ site.github.code }}/tutorials/mpi-hello-world/code)。
+
+
+## Hello world 代码案例
+让我们来看一下这节课的代码吧,完整的代码在 [mpi_hello_world.c]({{ site.github.code }}/tutorials/mpi-hello-world/code/mpi_hello_world.c)。
+下面是一些重点内容的摘录。
+```cpp
+#include
+#include
+
+int main(int argc, char** argv) {
+ // 初始化 MPI 环境
+ MPI_Init(NULL, NULL);
+
+ // 通过调用以下方法来得到所有可以工作的进程数量
+ int world_size;
+ MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+ // 得到当前进程的秩
+ int world_rank;
+ MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+
+ // 得到当前进程的名字
+ char processor_name[MPI_MAX_PROCESSOR_NAME];
+ int name_len;
+ MPI_Get_processor_name(processor_name, &name_len);
+
+ // 打印一条带有当前进程名字,秩以及
+ // 整个 communicator 的大小的 hello world 消息。
+ printf("Hello world from processor %s, rank %d out of %d processors\n",
+ processor_name, world_rank, world_size);
+
+ // 释放 MPI 的一些资源
+ MPI_Finalize();
+}
+```
+
+你应该已经注意到搭建一个 MPI 程序的第一步是引入 `#include ` 这个头文件。然后 MPI 环境必须以以下代码来初始化:
+
+```cpp
+MPI_Init(
+ int* argc,
+ char*** argv)
+```
+
+在 `MPI_Init` 的过程中,所有 MPI 的全局变量或者内部变量都会被创建。举例来说,一个通讯器 communicator 会根据所有可用的进程被创建出来(进程是我们通过 mpi 运行时的参数指定的),然后每个进程会被分配独一无二的秩 rank。当前来说,`MPI_Init` 接受的两个参数是没有用处的,不过参数的位置保留着,可能以后的实现会需要用到。
+
+在 `MPI_Init` 之后,有两个主要的函数被调用到了。这两个函数是几乎所有 MPI 程序都会用到的。
+
+```cpp
+MPI_Comm_size(
+ MPI_Comm communicator,
+ int* size)
+```
+
+`MPI_Comm_size` 会返回 communicator 的大小,也就是 communicator 中可用的进程数量。在我们的例子中,`MPI_COMM_WORLD`(这个 communicator 是 MPI 帮我们生成的)这个变量包含了当前 MPI 任务中所有的进程,因此在我们的代码里的这个调用会返回所有的可用的进程数目。
+
+```cpp
+MPI_Comm_rank(
+ MPI_Comm communicator,
+ int* rank)
+```
+
+`MPI_Comm_rank` 这个函数会返回 communicator 中当前进程的 rank。 communicator 中每个进程会以此得到一个从0开始递增的数字作为 rank 值。rank 值主要是用来指定发送或者接受信息时对应的进程。
+
+我们代码中使用到的一个不太常见的方法是:
+
+```cpp
+MPI_Get_processor_name(
+ char* name,
+ int* name_length)
+```
+
+`MPI_Get_processor_name` 会得到当前进程实际跑的时候所在的处理器名字。
+代码中最后一个调用是:
+
+```cpp
+MPI_Finalize()
+```
+`MPI_Finalize` 是用来清理 MPI 环境的。这个调用之后就没有 MPI 函数可以被调用了。
+
+## 运行 MPI hello world 程序
+现在查看以下代码文件以及代码所在的文件夹,你会看到一个 makefile。
+
+```
+>>> git clone {{ site.github.repo }}
+>>> cd mpitutorial/tutorials/mpi-hello-world/code
+>>> cat makefile
+EXECS=mpi_hello_world
+MPICC?=mpicc
+
+all: ${EXECS}
+
+mpi_hello_world: mpi_hello_world.c
+ ${MPICC} -o mpi_hello_world mpi_hello_world.c
+
+clean:
+ rm ${EXECS}
+```
+
+我的 makefile 会去找 MPICC 这个环境变量。如果你把 MPICH2 装在了本地文件夹里面而不是全局 PATH 下面, 手动设置一下 MPICC 这个环境变量,把它指向你的 mpicc 二进制程序。mpicc 二进制程序其实只是对 gcc 做了一层封装,使得编译和链接所有的 MPI 程序更方便。
+
+```
+>>> export MPICC=/home/kendall/bin/mpicc
+>>> make
+/home/kendall/bin/mpicc -o mpi_hello_world mpi_hello_world.c
+```
+当你的程序编译好之后,它就可以被执行了。不过执行之前你也许会需要一些额外配置。比如如果你想要在好几个节点的集群上面跑这个 MPI 程序的话,你需要配置一个 host 文件(不是 /etc/hosts)。如果你在笔记本或者单机上运行的话,可以跳过下面这一段。
+
+需要配置的 host 文件会包含你想要运行的所有节点的名称。为了运行方便,你需要确认一下所有这些节点之间能通过 SSH 通信,并且需要根据[设置认证文件这个教程](http://www.eng.cam.ac.uk/help/jpmg/ssh/authorized_keys_howto.html)配置不需要密码的 SSH 访问。
+我的 host 文件看起来像这样:
+
+```
+>>> cat host_file
+cetus1
+cetus2
+cetus3
+cetus4
+```
+
+为了用我提供的脚本来运行这个程序,你应该设置一个叫 MPI_HOSTS 的环境变量,把它指向 host 文件所在的位置。我的脚本会自动把这个 host 文件的配置项加到 MPI 启动命令里。如果单机跑的话就不用设置这个环境变量。另外如果你的 MPI 没有装到全局环境的话,你还需要指定 MPIRUN 这个环境变量指向你的 mpirun 二进制程序。
+
+准备就绪之后你就可以使用这个项目的我提供的 python 脚本来执行程序。脚本在 *tutorials* 目录下面,这个脚本可以用来跑我们这个教程里面提到的所有程序(而且它会帮你先编译一下程序)。你可以在 mpitutorial 这个文件夹的根目录下执行以下命令:
+
+```
+>>> export MPIRUN=/home/kendall/bin/mpirun
+>>> export MPI_HOSTS=host_file
+>>> cd tutorials
+>>> ./run.py mpi_hello_world
+/home/kendall/bin/mpirun -n 4 -f host_file ./mpi_hello_world
+Hello world from processor cetus2, rank 1 out of 4 processors
+Hello world from processor cetus1, rank 0 out of 4 processors
+Hello world from processor cetus4, rank 3 out of 4 processors
+Hello world from processor cetus3, rank 2 out of 4 processors
+```
+
+跟预想的一样,这个 MPI 程序运行在了我提供的所有节点上面。每个进程都被分配了一个单独的 rank,跟进程的名字一起打印出来了。你可以看到,在我们的输出的结果里,进程之间的打印顺序是任意的,因为我们的代码里并没有涉及到同步的操作。
+
+我们可以在打印的内容上面那条看到脚本是如何调用 mpirun 这个程序的。mpirun 是 MPI 的实现用来启动任务的一个程序。进程会在 host 文件里指定的所有机器上面生成,MPI 程序就会在所有进程上面运行。我的脚本自定地提供了一个 *-n* 参数告诉 MPI 程序我要运行 4 个进程。你可以试着修改脚本来使用更多进程运行 MPI 程序。当心别把你的操作系统玩崩了。:-)
+
+你可能会问,*“我的节点都都是双核的机器,我怎么样可以让 MPI 先在每个节点上的每个核上生成进程,再去其他的机器?”* 其实方案很简单。修改一下你的 host 文件,在每个节点名字的后面加一个冒号和每个处理器有的核数就行了。比如,我在 host 文件里指定我的每个节点有2个核。
+
+```
+>>> cat host_file
+cetus1:2
+cetus2:2
+cetus3:2
+cetus4:2
+```
+
+当我再次运行我的脚本,*哇!*,MPI 任务只在我的两个节点上生成了4个进程。
+
+
+```
+>>> ./run.py mpi_hello_world
+/home/kendall/bin/mpirun -n 4 -f host_file ./mpi_hello_world
+Hello world from processor cetus1, rank 0 out of 4 processors
+Hello world from processor cetus2, rank 2 out of 4 processors
+Hello world from processor cetus2, rank 3 out of 4 processors
+Hello world from processor cetus1, rank 1 out of 4 processors
+```
+
+## 接下来
+现在你对 MPI 程序有了基本的了解。接下来可以学习基础的 *点对点* (point-to-point)通信方法了。在下节课里,我讲解了 [MPI 里基础的发送和接收函数]({{ site.baseurl }}/tutorials/mpi-send-and-receive/zh_cn)。你也可以再去 [MPI tutorials]({{ site.baseurl }}/tutorials/) 首页查看所有其他的教程。
+
+有问题或者感到疑惑?欢迎在下面留言,也许我或者其他的读者可以帮到你。
diff --git a/tutorials/mpi-introduction/index.md b/tutorials/mpi-introduction/index.md
index 4580190..3424219 100644
--- a/tutorials/mpi-introduction/index.md
+++ b/tutorials/mpi-introduction/index.md
@@ -4,6 +4,7 @@ title: MPI Tutorial Introduction
author: Wes Kendall
categories: Beginner MPI
tags:
+translations: zh_cn,ja_jp
redirect_from: '/mpi-introduction/'
---
@@ -13,12 +14,12 @@ Before I dive into MPI, I want to explain why I made this resource. When I was i
Learning MPI was difficult for me because of three main reasons. First of all, the online resources for learning MPI were mostly outdated or not that thorough. Second, it was hard to find any resources that detailed how I could easily build or access my own cluster. And finally, the cheapest MPI book at the time of my graduate studies was a whopping 60 dollars - a hefty price for a graduate student to pay. Given how important parallel programming is in our day and time, I feel it is equally important for people to have access to better information about one of the fundamental interfaces for writing parallel applications.
-Although I am by no means an MPI expert, I decided that it would be useful for me to expel all of the information I learned about MPI during graduate school in the form of easy tutorials with example code that can be executed on your *very own* cluster! I hope this resource will be a valuable tool for your career, studies, or life - because parallel programming is not only the present, it *is* the future.
+Although I am by no means an MPI expert, I decided that it would be useful for me to disseminate all of the information I learned about MPI during graduate school in the form of easy tutorials with example code that can be executed on your *very own* cluster! I hope this resource will be a valuable tool for your career, studies, or life - because parallel programming is not only the present, it *is* the future.
## A brief history of MPI
Before the 1990's, programmers weren't as lucky as us. Writing parallel applications for different computing architectures was a difficult and tedious task. At that time, many libraries could facilitate building parallel applications, but there was not a standard accepted way of doing it.
-During this time, most parallel applications were in the science and research domains. The model most commonly adopted by the libraries was the message passing model. What is the message passing model? All it means is that an application passes messages among processes in order to perform a task. This model works out quite well in practice for parallel applications. For example, a master process might assign work to slave processes by passing them a message that describes the work. Another example is a parallel merge sorting application that sorts data locally on processes and passes results to neighboring processes to merge sorted lists. Almost any parallel application can be expressed with the message passing model.
+During this time, most parallel applications were in the science and research domains. The model most commonly adopted by the libraries was the message passing model. What is the message passing model? All it means is that an application passes messages among processes in order to perform a task. This model works out quite well in practice for parallel applications. For example, a manager process might assign work to worker processes by passing them a message that describes the work. Another example is a parallel merge sorting application that sorts data locally on processes and passes results to neighboring processes to merge sorted lists. Almost any parallel application can be expressed with the message passing model.
Since most libraries at this time used the same message passing model with only minor feature differences among them, the authors of the libraries and others came together at the Supercomputing 1992 conference to define a standard interface for performing message passing - the Message Passing Interface. This standard interface would allow programmers to write parallel applications that were portable to all major parallel architectures. It would also allow them to use the features and models they were already used to using in the current popular libraries.
@@ -32,6 +33,6 @@ Before starting the tutorial, I will cover a couple of the classic concepts behi
The foundation of communication is built upon send and receive operations among processes. A process may send a message to another process by providing the rank of the process and a unique *tag* to identify the message. The receiver can then post a receive for a message with a given tag (or it may not even care about the tag), and then handle the data accordingly. Communications such as this which involve one sender and receiver are known as *point-to-point* communications.
-There are many cases where processes may need to communicate with everyone else. For example, when a master process needs to broadcast information to all of its worker processes. In this case, it would be cumbersome to write code that does all of the sends and receives. In fact, it would often not use the network in an optimal manner. MPI can handle a wide variety of these types of *collective* communications that involve all processes.
+There are many cases where processes may need to communicate with everyone else. For example, when a manager process needs to broadcast information to all of its worker processes. In this case, it would be cumbersome to write code that does all of the sends and receives. In fact, it would often not use the network in an optimal manner. MPI can handle a wide variety of these types of *collective* communications that involve all processes.
-Mixtures of point-to-point and collective communications can be used to create highly complex parallel programs. In fact, this functionality is so powerful that it is not even necessary to start describing the advanced mechanisms of MPI. We will save that until a later lesson. For now, you should work on [installing MPI on a single machine]({{ site.baseurl }}/tutorials/installing-mpich2/) or [launching an Amazon EC2 MPI cluster]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/). If you already have MPI installed, great! You can head over to the [MPI Hello World lesson]({{ site.baseurl }}/tutorials/mpi-hello-world).
\ No newline at end of file
+Mixtures of point-to-point and collective communications can be used to create highly complex parallel programs. In fact, this functionality is so powerful that it is not even necessary to start describing the advanced mechanisms of MPI. We will save that until a later lesson. For now, you should work on [installing MPI on a single machine]({{ site.baseurl }}/tutorials/installing-mpich2/) or [launching an Amazon EC2 MPI cluster]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/). If you already have MPI installed, great! You can head over to the [MPI Hello World lesson]({{ site.baseurl }}/tutorials/mpi-hello-world).
diff --git a/tutorials/mpi-introduction/ja_jp.md b/tutorials/mpi-introduction/ja_jp.md
new file mode 100644
index 0000000..e6f4e8e
--- /dev/null
+++ b/tutorials/mpi-introduction/ja_jp.md
@@ -0,0 +1,37 @@
+---
+layout: post
+title: MPI チュートリアル イントロダクション
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/mpi-introduction/'
+---
+
+もはや並列計算はパソコン、スマートフォン、その他の技術と同じように人々の生活の一部となっています。これはこのWebサイトにアクセスするような方はご存じのことでしょう。並列プログラミングを学ぶというのは、学校でで学習している方・仕事のために学びたい方・趣味として学びたい方、どんな方にも今後にも活用できる非常に価値のある選択といえます。私の意見ですが、メッセージ パッシング インターフェイス(MPI)の学習は、並列プログラミングの知識を広げる正しい道です。MPI は他の多くの並列プログラミングライブラリ(Hadoopなど)よりも低レベルですが、並列プログラミングに関する知識を構築するためのとてもよい基礎となります。
+
+どうして私が、このMPIに関するリソースを作成したかを説明します。私は、大学院時代に MPIをよく使用していました。幸いにもアルゴンヌ国立研究所([Argonne National Laboratory](http://www.anl.gov))でのインターンシップ中にMPIコミュニティの重要人物と出会うことができ、博士時代には大規模なスーパーコンピューティング資源とMPIを用いたクレイジーな研究に取り組めました。しかし、このような人脈とリソースの中にあってもMPIの学習は依然として難しいと未だに思っています。
+
+MPI学習が難しいと感じたのには3つの理由があります。まず、MPI学習のオンラインリソースは時代遅れか不十分な内容でした。次に、自分でクラスタを簡単に利用・構築する方法が記載されたリソースを見つけるのは困難でした。最後に、大学院時代の最も安価なMPIの本は60ドルもしました。これは大学院生にとっては高額です。現代おける並列プログラミングの重要性を考えると、並列アプリケーションのための基本的なインターフェイスの1つに関する情報に人々がアクセスがとても重要だと考えています。
+
+私はMPIの専門家ではありません。ですが、大学院時代に学んだMPIに関するすべての情報を、*皆さん自身の*クラスタで実行できるサンプルコードを含む簡単なチュートリアル形式で広めることは有益だと考えています。このWebサイトの情報が皆さんのキャリア、研究、または人生にとって貴重なツールとなることを願っています。並列プログラミングは現在だけでなく*未来でもある*からです。
+
+## MPIの歴史(概略): A brief history of MPI
+1990年代以前、現在のMPIのようなスタイルで並列計算プログラムを書くことはできませんでした。異なるコンピュータアーキテクチャに対するプログラミングと言うのは困難で退屈を極めました。いくつかのライブラリが生まれましたがそれらが標準となることはありませんでした。
+
+その時代の並列アプリケーションはほとんどが科学や研究領域のためのものでした。この中で、最も一般的に採用されたライブラリのモデルがメッセージ・パッシング・モデルです。このモデルを利用するアプリケーションは、あるタスクを実行するためにプロセス間でメッセージを受け渡しを行います。このモデルは並列アプリケーションで非常にうまく機能します。マネージャー・プロセスはワーカー・プロセスに操作命令を含んだメッセージを渡して、ワーカー・プロセスに操作を割り当てることができます。例えばマージソートを並列処理するとします。各プロセス上はローカルのデータをソートし、そのローカルのソート済みのデータを全体でマージするために、隣接したプロセスに結果を渡します。このように多くの並列アプリケーションはメッセージパッシングモデルで表現できます。
+
+この時代に登場したライブラリはメッセージパッシングモデルであり各ライブラリ間の機能の違い少なかったため、1992年のSupercomputingカンファレンスにおいて各ライブラリの作者が集ってメッセージパッシングの実行標準インターフェースであるMessage Passing Interfaceを定義しました。これを契機にプログラマはすべての主要な並列アーキテクチャに移植可能な並列アプリケーションを書くことができるようになりました。また、現在も一般的に使われているライブラリの使われている機能やモデルもこの時から出現しました。
+
+そして1994年までに完全なインターフェースと標準が定義されこれはMPI-1と呼ばれています。とはいえ、MPIは特定の実装でなくインターフェイスの定義です。各々のアーキテクチャに対応したインタフェースを実装を作成するのは開発者に委ねられていました。しかし、幸いにもMPIの完全な実装が利用できるようになるまで、1年程度しかかかりませんでした。この最初の実装が作られた後、MPIは広く採用され今でもメッセージパッシング・アプリケーションを書くための*事実上の(デファクトの)*手法であり続けています。
+
+
+*最初のMPIプログラマ*
+
+## MPIメッセージパッシングモデルの設計: MPI's design for the message passing model
+並列プログラミングのメッセージパッシングモデルであるMPIの設計における、いくつかの古典的な概念について説明します。まずは、`コミュニケータ`という概念です。コミュニケータは互いに通信可能なプロセスのグループです。このプロセスグループでは、各プロセスに固有の`ランク`が割り当てられており、そのランクで互いを区別して明示的に通信を行います。
+
+通信の基本はプロセス間の送受信操作です。送信側のプロセスはランクとメッセージを識別するためのユニークな`タグ`を用いて別のプロセスにメッセージを送信します。受信側のプロセスは指定されたタグを持つメッセージの受信してデータを処理します。このような1つの送信者と受信者が関わる通信を`ポイント・ツー・ポイント通信`と呼びます。
+
+プロセスが他の全プロセスと通信したいことがあります。例えば、マネージャー・プロセスがワーカー・プロセスすべてにデータをブロードキャストすることを考えましょう。1つ1つの送受信を行うコードを書くのは面倒ですし、最適な方法でネットワークを利用するのも難しいです。MPIはこのような全プロセスを含む様々な種類の`集団通信(collective communication)`を扱うことができます。
+
+ポイント・ツー・ポイント通信と集団通信を組み合わせることで、非常に複雑な並列プログラムが作成できます。これらの機能は非常にパワフルな高度なメカニズムを持ちます。この説明は後のレッスンまで取っておくことにして、今は[MPIをシングルマシンにインストールする]({{ site.baseurl }}/tutorials/installing-mpich2/)か、[Amazon EC2 MPIクラスタを起動する]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/)に取り組んでください。既にMPIがインストールされているなら、それは素晴らしいに進んでください。
\ No newline at end of file
diff --git a/tutorials/mpi-introduction/zh_cn.md b/tutorials/mpi-introduction/zh_cn.md
new file mode 100644
index 0000000..b9e5465
--- /dev/null
+++ b/tutorials/mpi-introduction/zh_cn.md
@@ -0,0 +1,39 @@
+---
+layout: post
+title: MPI 教程介绍
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/mpi-introduction/zh_cn'
+---
+
+分布式计算现在对于我们来说,就跟日常生活里的手机和电脑一样普及。你很明显应该认同这个观点,因为你发现了这个了不起的 MPI 教程网站!不管你是出于什么原因想学习并行编程(parallel programming),或者说分布式编程、并行编程,也许是因为课程需要,或者是工作,或者单纯地觉得好玩,我觉得你都应该选择一项在未来几年依然十分有价值的技术去学习。我觉得「消息传递接口」(Message Passing Interface, MPI)就是这样一项技术,而且学习它确实可以让你的并行编程知识变得更深厚。尽管 MPI 比大多数并行框架要更底层(比如 Hadoop),但是学习 MPI 会为你的并行编程打下良好的基础。
+
+在我开始介绍 MPI 之前,我想要解释下我为什么做这个教程。当我在读研究生的时候,我大量的用到了 MPI。当我在 [Argonne National Laboratory](http://www.anl.gov) 实习的时候,我很幸运地可以跟 MPI 社区里很厉害的一些人一起工作,并且使用 MPI 在庞大的超级计算(supercomputing)集群上面做了很多疯狂的事情。然而,即使有这些资源和懂行的人可以问,我还是觉得学习 MPI 是件苦差事。
+
+对我来说学习 MPI 很难主要是因为以下三个方面。第一,网上关于 MPI 的资料几乎都是过时的,或者不那么全的。第二,我想要自己简单地搭建一个可以运行 MPI 的集群环境,但是找不到这样的教程。最后,我读研究生的时候能买到的最便宜的关于 MPI 的书要60美元 - 对研究生来说太贵了。就目前分布式编程对我们生活的重要性来说,我觉得提供一个更好的教程能让别人学习 MPI 这样一个并行编程最重要的协议同等重要。
+
+尽管我不敢自称是 MPI 专家,我觉得以简单易读的教程形式传播这些我在研究生阶段学习到的知识还是很有意义的一件事,你可以根据教程在*你自己*的集群上运行 MPI 程序!我希望这个教程能对你所有帮助,也许是事业上的,也许是学习上的,或者可能是生活上的帮助 - 因为分布式编程不仅仅意味着现在,它*还是*未来!
+
+## MPI 的历史简介
+在 90 年代之前,程序员可没我们这么幸运。对于不同的计算架构写并发程序是一件困难而且冗长的事情。当时,很多软件库可以帮助写并发程序,但是没有一个大家都接受的标准来做这个事情。
+
+在当时,大多数的并发程序只出现在科学和研究的领域。最广为接受的模型就是消息传递模型。什么是消息传递模型?它其实只是指程序通过在进程间传递消息(消息可以理解成带有一些信息和数据的一个数据结构)来完成某些任务。在实践中,并发程序用这个模型去实现特别容易。举例来说,主进程(manager process)可以通过对从进程(worker process)发送一个描述工作的消息来把这个工作分配给它。另一个例子就是一个并发的排序程序可以在当前进程中对当前进程可见的(我们称作本地的,locally)数据进行排序,然后把排好序的数据发送的邻居进程上面来进行合并的操作。几乎所有的并行程序可以使用消息传递模型来描述。
+
+由于当时很多软件库都用到了这个消息传递模型,但是在定义上有些微小的差异,这些库的作者以及一些其他人为了解决这个问题就在 Supercomputing 1992 大会上定义了一个消息传递接口的标准- 也就是 MPI。这个标准接口使得程序员写的并发程序可以在所有主流的并发框架中运行。并且允许他们可以使用当时已经在使用的一些流行库的特性和模型。
+
+到 1994 年的时候,一个完整的接口标准定义好了(MPI-1)。我们要记住 MPI *只是*一个接口的定义而已。然后需要程序员去根据不同的架构去实现这个接口。很幸运的是,仅仅一年之后,一个完整的 MPI 实现就已经出现了。在第一个实现之后,MPI 就被大量地使用在消息传递应用程序中,并且依然是写这类程序的*标准*(de-facto)。
+
+
+
+*第一批 MPI 程序员的一个真实写照*
+
+## MPI 对于消息传递模型的设计
+在开始教程之前,我会先解释一下 MPI 在消息传递模型设计上的一些经典概念。第一个概念是*通讯器*(communicator)。通讯器定义了一组能够互相发消息的进程。在这组进程中,每个进程会被分配一个序号,称作*秩*(rank),进程间显性地通过指定秩来进行通信。
+
+通信的基础建立在不同进程间发送和接收操作。一个进程可以通过指定另一个进程的秩以及一个独一无二的消息*标签*(*tag*)来发送消息给另一个进程。接受者可以发送一个接收特定标签标记的消息的请求(或者也可以完全不管标签,接收任何消息),然后依次处理接收到的数据。类似这样的涉及一个发送者以及一个接受者的通信被称作*点对点*(point-to-point)通信。
+
+
+当然在很多情况下,某个进程可能需要跟所有其他进程通信。比如主进程想发一个广播给所有的从进程。在这种情况下,手动去写一个个进程点对点的信息传递就显得很笨拙。而且事实上这样会导致网络利用率低下。MPI 有专门的接口来帮我们处理这类所有进程间的*集体性*(collective)通信。
+
+把点对点通信和集体性通信这两个机制合在一起已经可以创造十分复杂的并发程序了。事实上,这两个功能已经强大到我现在不需要再介绍任何 MPI 高级的特性了,我会把那些放到后面的教程中。现在,我们可以从[在单机上安装 MPI]({{ site.baseurl }}/tutorials/installing-mpich2/)或 [启动一个 Amazon EC2 MPI 集群]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/) 开始我们的 MPI 旅途了!如果你已经把 MPI 装好了,那太好了,直接开始这个[MPI Hello World 课程]({{ site.baseurl }}/tutorials/mpi-hello-world)吧。
diff --git a/tutorials/mpi-reduce-and-allreduce/code/reduce_avg.c b/tutorials/mpi-reduce-and-allreduce/code/reduce_avg.c
index 0b84599..c60247f 100644
--- a/tutorials/mpi-reduce-and-allreduce/code/reduce_avg.c
+++ b/tutorials/mpi-reduce-and-allreduce/code/reduce_avg.c
@@ -2,7 +2,7 @@
// Copyright 2013 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Program that computes the average of an array of elements in parallel using
// MPI_Reduce.
@@ -11,7 +11,8 @@
#include
#include
#include
-
+#include
+
// Creates an array of random numbers. Each number has a value from 0 - 1
float *create_rand_nums(int num_elements) {
float *rand_nums = (float *)malloc(sizeof(float) * num_elements);
@@ -30,8 +31,6 @@ int main(int argc, char** argv) {
}
int num_elements_per_proc = atoi(argv[1]);
- // Seed the random number generator to get different results each time
- srand(time(NULL));
MPI_Init(NULL, NULL);
@@ -41,7 +40,7 @@ int main(int argc, char** argv) {
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// Create a random array of elements on all processes.
- srand(world_rank);
+ srand(time(NULL)*world_rank); // Seed the random number generator to get different results each time for each processor
float *rand_nums = NULL;
rand_nums = create_rand_nums(num_elements_per_proc);
@@ -69,7 +68,7 @@ int main(int argc, char** argv) {
// Clean up
free(rand_nums);
-
+
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
}
diff --git a/tutorials/mpi-reduce-and-allreduce/code/reduce_stddev.c b/tutorials/mpi-reduce-and-allreduce/code/reduce_stddev.c
index c360167..4560d37 100644
--- a/tutorials/mpi-reduce-and-allreduce/code/reduce_stddev.c
+++ b/tutorials/mpi-reduce-and-allreduce/code/reduce_stddev.c
@@ -2,7 +2,7 @@
// Copyright 2013 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Program that computes the standard deviation of an array of elements in parallel using
// MPI_Reduce.
@@ -12,7 +12,7 @@
#include
#include
#include
-
+
// Creates an array of random numbers. Each number has a value from 0 - 1
float *create_rand_nums(int num_elements) {
float *rand_nums = (float *)malloc(sizeof(float) * num_elements);
@@ -31,8 +31,6 @@ int main(int argc, char** argv) {
}
int num_elements_per_proc = atoi(argv[1]);
- // Seed the random number generator to get different results each time
- srand(time(NULL));
MPI_Init(NULL, NULL);
@@ -42,7 +40,7 @@ int main(int argc, char** argv) {
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// Create a random array of elements on all processes.
- srand(world_rank);
+ srand(time(NULL)*world_rank); // Seed the random number generator of processes uniquely
float *rand_nums = NULL;
rand_nums = create_rand_nums(num_elements_per_proc);
@@ -82,7 +80,7 @@ int main(int argc, char** argv) {
// Clean up
free(rand_nums);
-
+
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
}
diff --git a/tutorials/mpi-reduce-and-allreduce/index.md b/tutorials/mpi-reduce-and-allreduce/index.md
index cb5ad4a..9011084 100644
--- a/tutorials/mpi-reduce-and-allreduce/index.md
+++ b/tutorials/mpi-reduce-and-allreduce/index.md
@@ -3,13 +3,14 @@ layout: post
title: MPI Reduce and Allreduce
author: Wes Kendall
categories: Beginner MPI
+translations: zh_cn,ja_jp
tags: MPI_Allreduce, MPI_Reduce
redirect_from: '/mpi-reduce-and-allreduce/'
---
In the [previous lesson]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi), we went over an application example of using `MPI_Scatter` and `MPI_Gather` to perform parallel rank computation with MPI. We are going to expand on collective communication routines even more in this lesson by going over `MPI_Reduce` and `MPI_Allreduce`.
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-reduce-and-allreduce/code]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-reduce-and-allreduce/code]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code).
## An introduction to reduce
*Reduce* is a classic concept from functional programming. Data reduction involves reducing a set of numbers into a smaller set of numbers via a function. For example, let's say we have a list of numbers `[1, 2, 3, 4, 5]`. Reducing this list of numbers with the sum function would produce `sum([1, 2, 3, 4, 5]) = 15`. Similarly, the multiplication reduction would yield `multiply([1, 2, 3, 4, 5]) = 120`.
@@ -175,6 +176,6 @@ Mean - 0.501100, Standard deviation = 0.301126
```
## Up next
-Now that you are comfortable using all of the common collectives - `MPI_Bcast`, `MPI_Scatter`, `MPI_Gather`, and `MPI_Reduce`, we can utilize them to build a sophisticated parallel application. In the next lesson, we will utilize most of our collective routines to create a parallel sorting application. Stay tuned!
+Now that you are comfortable using all of the common collectives - `MPI_Bcast`, `MPI_Scatter`, `MPI_Gather`, and `MPI_Reduce`, we can utilize them to build a sophisticated parallel application. In the next lesson, we will start diving into [MPI groups and communicators]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/).
-For all beginner lessons, go the the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/).
\ No newline at end of file
+For all lessons, go the the [MPI tutorials section]({{ site.baseurl }}/tutorials/).
\ No newline at end of file
diff --git a/tutorials/mpi-reduce-and-allreduce/ja_jp.md b/tutorials/mpi-reduce-and-allreduce/ja_jp.md
new file mode 100644
index 0000000..3cbef83
--- /dev/null
+++ b/tutorials/mpi-reduce-and-allreduce/ja_jp.md
@@ -0,0 +1,178 @@
+---
+layout: post
+title: MPI Reduce と Allreduce - MPI Reduce and Allreduce
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Allreduce, MPI_Reduce
+redirect_from: '/mpi-reduce-and-allreduce/'
+---
+
+前回のレッスンでは`MPI_Scatter`と`MPI_Gather`を使用してMPIで並列に順位計算を実行するアプリケーションを説明しました。このレッスンでは、`MPI_Reduce`と`MPI_Allreduce`を説明して集団通信への理解をさらに深めます。
+
+> **Note** : このサイトのコードはすべて [GitHub]({{ site.github.repo }})にあります。このチュートリアルのコードは [tutorials/mpi-reduce-and-allreduce/code]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code)にあります。
+
+## Reduce - An introduction to reduce
+*reduce*は関数型プログラミングで使われる基本的な概念です。データのreduceはある関数を使用して数値のセットを小さな数値のセットにします。たとえば、`[1, 2, 3, 4, 5]`というリストがあるとしましょう。これをsum 関数でreduceすると`sum([1, 2, 3, 4, 5]) = 15`が計算されることになります。掛け算でreduceすると`multiply([1, 2, 3, 4, 5]) = 120`となります。
+
+分散されている数値の集合にreduceするのは非常に面倒です。さらに集合の順序を考慮しながらのreduceを効率的にプログラムするのも面倒です。MPIには `MPI_Reduce` 関数があり、プログラマが並列アプリケーションで行う必要のある一般的なreduceのほとんどを扱うことができます。
+
+## MPI_Reduce
+`MPI_Reduce` は`MPI_Gather` と同様、各プロセスで入力要素の配列を受け取って出力要素の配列をルートプロセスに返します。出力要素にはreduceの結果が含まれます。`MPI_Reduce`の関数定義は次のとおりです。
+
+```cpp
+MPI_Reduce(
+ void* send_data,
+ void* recv_data,
+ int count,
+ MPI_Datatype datatype,
+ MPI_Op op,
+ int root,
+ MPI_Comm communicator)
+```
+
+`send_data`引数は、各プロセスがreduceしたい`datatype`型の要素の配列のポインタです。`recv_data`はルートプロセスだけの引数でreduceの結果が格納されて`sizeof(datatype) * count`のサイズを持もちます。`op` パラメータには、データに適用したい処理を指定します。MPIには一般的なreduce演算が用意されています。自作のreduceの関数を作成することもできますがこのレッスンでは扱いません。以下にMPIでサポートされている操作を紹介します。
+
+* `MPI_MAX` - 最大値
+* `MPI_MIN` - 最小値
+* `MPI_SUM` - 合計
+* `MPI_PROD` - 乗算
+* `MPI_LAND` - 各要素の論理的なAND演算
+* `MPI_LOR` - 各要素の論理的なOR演算
+* `MPI_BAND` - 各要素のビットAND演算
+* `MPI_BOR` - 各要素のビットOR演算
+* `MPI_MAXLOC` - 最大値とそれを持つプロセスのランク
+* `MPI_MINLOC` - 最小値とそれを持つプロセスのランク
+
+`MPI_Reduce`のイメージを示します。
+
+
+
+この例では各プロセスは整数を1つ持ちます。`MPI_Reduce`はプロセス0で呼び出され`MPI_SUM`をreduce演算として使用します。合計された結果がルートプロセスに格納されます。
+
+次は各プロセスが整数を複数持った時のことを考えます。
+
+
+
+この例では各プロセスは2つの整数を持ちます。ルートのプロセスではこの2つの数値を一緒にして合計を求めるのではなく、結果を入れる配列のi番目に対して、各プロセスのi番目の数の合計を集約することに注意してください。
+つまり、すべての配列の要素を合計して1つの要素にするのではなく、各配列の要素を合計してプロセス0の結果配列のi番目の要素に格納します
+
+`MPI_Reduce` がどのように見えるか理解できたので次のトピックに進みましょう。
+
+## MPI_Reduceを使った平均の計算 - Computing average of numbers with MPI_Reduce
+一つ前のレッスンでは`MPI_Scatter`と`MPI_Gather`で数値の平均を求めましたが、`MPI_Reduce`を使うことでより簡単に実装することができます。次に[reduce_avg.c]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code/reduce_avg.c)を示します。
+
+```cpp
+float *rand_nums = NULL;
+rand_nums = create_rand_nums(num_elements_per_proc);
+
+// 各のプロセスのsumを求める
+float local_sum = 0;
+int i;
+for (i = 0; i < num_elements_per_proc; i++) {
+ local_sum += rand_nums[i];
+}
+
+// 各のローカル変数の平均を合計と平均を表示する
+printf("Local sum for process %d - %f, avg = %f\n",
+ world_rank, local_sum, local_sum / num_elements_per_proc);
+
+// 各プロセスで求めたsumをグローバルsumとしてreduceする
+float global_sum;
+MPI_Reduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, 0,
+ MPI_COMM_WORLD);
+
+// 結果の表示
+if (world_rank == 0) {
+ printf("Total sum = %f, avg = %f\n", global_sum,
+ global_sum / (world_size * num_elements_per_proc));
+}
+```
+
+このコードでは各プロセスが(floatの)乱数を作成し、`local_sum`を計算します。次に、`local_sum`を`MPI_SUM`によってルートプロセスにreduceします。そして全部のプロセスの平均を`global_sum / (world_size * num_elements_per_proc)`で求めます。reduce_avgプログラムを[レポジトリ]({{ site.github.code }})の*tutorials*ディレクトリから実行しましょう。
+
+```
+>>> cd tutorials
+>>> ./run.py reduce_avg
+mpirun -n 4 ./reduce_avg 100
+Local sum for process 0 - 51.385098, avg = 0.513851
+Local sum for process 1 - 51.842468, avg = 0.518425
+Local sum for process 2 - 49.684948, avg = 0.496849
+Local sum for process 3 - 47.527420, avg = 0.475274
+Total sum = 200.439941, avg = 0.501100
+```
+
+`MPI_Reduce` について学んだので次は`MPI_Allreduce`です!
+
+## MPI_Allreduce
+ほとんどの並列アプリケーションでは、ルートプロセスだけでなく全プロセスがreduceの結果にアクセスする必要があるでしょう。`MPI_Allgather`と`MPI_Gather`の関係のように`MPI_Allreduce`は値をreduceした結果を全プロセスに配布します。関数定義は以下の通りです。
+
+```cpp
+MPI_Allreduce(
+ void* send_data,
+ void* recv_data,
+ int count,
+ MPI_Datatype datatype,
+ MPI_Op op,
+ MPI_Comm communicator)
+```
+
+`MPI_Allreduce` は `MPI_Reduce` 同じ引数に見えますがルートプロセス IDは不要です (結果が全プロセスに配布されるため)。以下に `MPI_Allreduce` の通信パターンを図示します。
+
+
+
+`MPI_Allreduce`は`MPI_Reduce`した後に`MPI_Bcast`しているのと同じです。
+
+## MPI_Allreduce による標準偏差の計算 - Computing standard deviation with MPI_Allreduce
+計算問題の多くは、問題を解くためにreduceを何度も呼び出します。ここで例に示す例は分散している数の標準偏差を求める計算です。標準偏差とは平均値からの数値のばらつきを表す尺度で、標準偏差が低ければ低いほど数値がより近くに集まっていることを意味し、標準偏差が高ければその逆です。
+
+標準偏差を求めるには、まず全ての数値の平均が必要です。次に平均からの差の2乗和を計算します。この和の平均の平方根が最終結果となります。ということは2つの和が必要ということで、2つのreduceが必要になるわけです。レッスンコードの[reduce_stddev.c]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code/reduce_stddev.c)でこれを示します。
+
+```cpp
+rand_nums = create_rand_nums(num_elements_per_proc);
+
+// プロセス内の合計を求める
+float local_sum = 0;
+int i;
+for (i = 0; i < num_elements_per_proc; i++) {
+ local_sum += rand_nums[i];
+}
+
+// 平均を求めるためのグローバル和を計算する
+float global_sum;
+MPI_Allreduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM,
+ MPI_COMM_WORLD);
+float mean = global_sum / (num_elements_per_proc * world_size);
+
+// 平均からの差の2乗和をローカルで合計する。
+float local_sq_diff = 0;
+for (i = 0; i < num_elements_per_proc; i++) {
+ local_sq_diff += (rand_nums[i] - mean) * (rand_nums[i] - mean);
+}
+
+// 2乗和をルートプロセスに集めて合計を取る
+float global_sq_diff;
+MPI_Reduce(&local_sq_diff, &global_sq_diff, 1, MPI_FLOAT, MPI_SUM, 0,
+ MPI_COMM_WORLD);
+
+// 標準偏差stddevは二乗差の平均の平方根。表示する。
+if (world_rank == 0) {
+ float stddev = sqrt(global_sq_diff /
+ (num_elements_per_proc * world_size));
+ printf("Mean - %f, Standard deviation = %f\n", mean, stddev);
+}
+```
+
+まず、各プロセスは要素の`local_sum`を計算します。そして`MPI_Allreduce` を使用してそれらを合計します。各プロセスは全ての平均がわかるようになったので、`local_sq_diff`を計算するための`mean` を計算します。そしてローカルで平均からの差の2乗和を計算すると、ルートプロセスは`MPI_Reduce`を使用して`global_sq_diff`を計算します。最後にルートプロセスはグローバル平方差の平均の平方根を取ることによって標準偏差を計算します。
+
+サンプルプログラムの動作結果を示します。
+
+```
+>>> ./run.py reduce_stddev
+mpirun -n 4 ./reduce_stddev 100
+Mean - 0.501100, Standard deviation = 0.301126
+```
+
+## 次は?
+`MPI_Bcast`, `MPI_Scatter`, `MPI_Gather`, `MPI_Reduce` といった一般的な集団計算の使い方に慣れたので、これらを利用した洗練された並列アプリケーションを構築してみましょう。次回のレッスンは[MPI groups and communicators]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/)です。
+
+全てのレッスンは、[MPIチュートリアルセクション]({{ site.baseurl }}/tutorials/)を参照してください。
diff --git a/tutorials/mpi-reduce-and-allreduce/zh_cn.md b/tutorials/mpi-reduce-and-allreduce/zh_cn.md
new file mode 100644
index 0000000..1e2b9e3
--- /dev/null
+++ b/tutorials/mpi-reduce-and-allreduce/zh_cn.md
@@ -0,0 +1,224 @@
+---
+layout: post
+title: MPI Reduce and Allreduce
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Allreduce, MPI_Reduce
+redirect_from: '/mpi-reduce-and-allreduce/'
+---
+
+在 [上一节]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi) 中,我们介绍了一个使用`MPI_Scatter`和`MPI_Gather`的计算并行排名的示例。 。
+在本课中,我们将通过`MPI_Reduce`和`MPI_Allreduce`进一步扩展集体通信例程。
+
+> **Note** - 本教程的所有代码都在 [GitHub]({{ site.github.repo }}) 上。本教程的代码位于 [tutorials/mpi-reduce-and-allreduce/code]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code) 下。
+
+## 归约简介
+
+*归约* 是函数式编程中的经典概念。
+数据归约包括通过函数将一组数字归约为较小的一组数字。
+例如,假设我们有一个数字列表 `[1,2,3,4,5]`。
+用 sum 函数归约此数字列表将产生 `sum([1、2、3、4、5]) = 15`。
+类似地,乘法归约将产生 `multiply([1、2、3、4、5]) = 120`。
+
+就像您想象的那样,在一组分布式数字上应用归约函数可能非常麻烦。
+随之而来的是,难以有效地实现非可交换的归约,即必须以设定顺序发生的缩减。
+幸运的是,MPI 有一个方便的函数,`MPI_Reduce`,它将处理程序员在并行程序中需要执行的几乎所有常见的归约操作。
+
+## MPI_Reduce
+
+与 `MPI_Gather` 类似,`MPI_Reduce` 在每个进程上获取一个输入元素数组,并将输出元素数组返回给根进程。
+输出元素包含减少的结果。
+`MPI_Reduce` 的原型如下所示:
+
+```cpp
+MPI_Reduce(
+ void* send_data,
+ void* recv_data,
+ int count,
+ MPI_Datatype datatype,
+ MPI_Op op,
+ int root,
+ MPI_Comm communicator)
+```
+
+`send_data` 参数是每个进程都希望归约的 `datatype` 类型元素的数组。
+`recv_data` 仅与具有 `root` 秩的进程相关。
+`recv_data` 数组包含归约的结果,大小为`sizeof(datatype)* count`。
+`op` 参数是您希望应用于数据的操作。
+MPI 包含一组可以使用的常见归约运算。
+尽管可以定义自定义归约操作,但这超出了本教程的范围。
+MPI 定义的归约操作包括:
+
+* `MPI_MAX` - 返回最大元素。
+* `MPI_MIN` - 返回最小元素。
+* `MPI_SUM` - 对元素求和。
+* `MPI_PROD` - 将所有元素相乘。
+* `MPI_LAND` - 对元素执行逻辑*与*运算。
+* `MPI_LOR` - 对元素执行逻辑*或*运算。
+* `MPI_BAND` - 对元素的各个位按位*与*执行。
+* `MPI_BOR` - 对元素的位执行按位*或*运算。
+* `MPI_MAXLOC` - 返回最大值和所在的进程的秩。
+* `MPI_MINLOC` - 返回最小值和所在的进程的秩。
+
+下面是 `MPI_Reduce` 通信模式的说明。
+
+
+
+在上图中,每个进程包含一个整数。
+调用 `MPI_Reduce` 的根进程为 0,并使用 `MPI_SUM` 作为归约运算。
+这四个数字相加后将结果存储在根进程中。
+
+查看当进程拥有多个元素时会发生什么也很有用。
+下图显示了每个进程归约多个数字的情况。
+
+
+
+上图中的每个进程都有两个元素。
+结果求和基于每个元素进行。
+换句话说,不是将所有数组中的所有元素累加到一个元素中,而是将每个数组中的第 i 个元素累加到进程 0 结果数组中的第 i 个元素中。
+
+现在您了解了 `MPI_Reduce` 的外观,我们可以尝试一些代码示例。
+
+## 使用 MPI_Reduce 计算均值
+
+在 [上一节]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather) 中,我们展示了如何使用 `MPI_Scatter` 和 `MPI_Gather` 计算平均值。
+使用 `MPI_Reduce` 可以简化上一节的代码。
+以下是本节示例代码中 [reduce_avg.c]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code/reduce_avg.c) 的片段。
+
+```cpp
+float *rand_nums = NULL;
+rand_nums = create_rand_nums(num_elements_per_proc);
+
+// Sum the numbers locally
+float local_sum = 0;
+int i;
+for (i = 0; i < num_elements_per_proc; i++) {
+ local_sum += rand_nums[i];
+}
+
+// Print the random numbers on each process
+printf("Local sum for process %d - %f, avg = %f\n",
+ world_rank, local_sum, local_sum / num_elements_per_proc);
+
+// Reduce all of the local sums into the global sum
+float global_sum;
+MPI_Reduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, 0,
+ MPI_COMM_WORLD);
+
+// Print the result
+if (world_rank == 0) {
+ printf("Total sum = %f, avg = %f\n", global_sum,
+ global_sum / (world_size * num_elements_per_proc));
+}
+```
+
+在上面的代码中,每个进程都会创建随机数并计算和保存在 `local_sum` 中。
+然后使用 `MPI_SUM` 将 `local_sum` 归约至根进程。
+然后,全局平均值为 `global_sum / (world_size * num_elements_per_proc)`。
+如果您从 [repo]({{site.github.code}}) 的 *tutorials* 目录中运行 reduce_avg 程序,则输出应与此类似。
+
+```
+>>> cd tutorials
+>>> ./run.py reduce_avg
+mpirun -n 4 ./reduce_avg 100
+Local sum for process 0 - 51.385098, avg = 0.513851
+Local sum for process 1 - 51.842468, avg = 0.518425
+Local sum for process 2 - 49.684948, avg = 0.496849
+Local sum for process 3 - 47.527420, avg = 0.475274
+Total sum = 200.439941, avg = 0.501100
+```
+
+现在是时候接触 `MPI_Reduce` 的同级对象 - `MPI_Allreduce` 了。
+
+## MPI_Allreduce
+
+许多并行程序中,需要在所有进程而不是仅仅在根进程中访问归约的结果。
+以与 `MPI_Gather` 相似的补充方式,`MPI_Allreduce` 将归约值并将结果分配给所有进程。
+函数原型如下:
+
+```cpp
+MPI_Allreduce(
+ void* send_data,
+ void* recv_data,
+ int count,
+ MPI_Datatype datatype,
+ MPI_Op op,
+ MPI_Comm communicator)
+```
+
+您可能已经注意到,`MPI_Allreduce` 与 `MPI_Reduce` 相同,不同之处在于它不需要根进程 ID(因为结果分配给所有进程)。
+下图介绍了 `MPI_Allreduce` 的通信模式:
+
+
+`MPI_Allreduce` 等效于先执行 `MPI_Reduce`,然后执行 `MPI_Bcast`。
+很简单,对吧?
+
+## 使用 MPI_Allreduce 计算标准差
+
+许多计算问题需要进行多次归约来解决。
+一个这样的问题是找到一组分布式数字的标准差。
+或许您可能已经遗忘了什么是标准差,标准差是数字与均值之间的离散程度的度量。
+较低的标准差表示数字靠得更近,对于较高的标准差则相反。
+
+要找到标准差,必须首先计算所有数字的平均值。
+总和均值的平方根是最终结果。
+给定问题描述,我们知道所有数字至少会有两个和,转化为两个归约。
+本文代码 [reduce_stddev.c]({{ site.github.code }}/tutorials/mpi-reduce-and-allreduce/code/reduce_stddev.c) 中的一个片段展示了如何应用 MPI 解决此问题的概况。
+
+```cpp
+rand_nums = create_rand_nums(num_elements_per_proc);
+
+// Sum the numbers locally
+float local_sum = 0;
+int i;
+for (i = 0; i < num_elements_per_proc; i++) {
+ local_sum += rand_nums[i];
+}
+
+// Reduce all of the local sums into the global sum in order to
+// calculate the mean
+float global_sum;
+MPI_Allreduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM,
+ MPI_COMM_WORLD);
+float mean = global_sum / (num_elements_per_proc * world_size);
+
+// Compute the local sum of the squared differences from the mean
+float local_sq_diff = 0;
+for (i = 0; i < num_elements_per_proc; i++) {
+ local_sq_diff += (rand_nums[i] - mean) * (rand_nums[i] - mean);
+}
+
+// Reduce the global sum of the squared differences to the root
+// process and print off the answer
+float global_sq_diff;
+MPI_Reduce(&local_sq_diff, &global_sq_diff, 1, MPI_FLOAT, MPI_SUM, 0,
+ MPI_COMM_WORLD);
+
+// The standard deviation is the square root of the mean of the
+// squared differences.
+if (world_rank == 0) {
+ float stddev = sqrt(global_sq_diff /
+ (num_elements_per_proc * world_size));
+ printf("Mean - %f, Standard deviation = %f\n", mean, stddev);
+}
+```
+
+在上面的代码中,每个进程都会计算元素的局部总和 `local_sum`,并使用 `MPI_Allreduce `对它们求和。
+在所有进程上都有全局总和后,将计算均值 `mean`,以便可以计算局部距平的平方 `local_sq_diff`。
+一旦计算出所有局部距平的平方,就可以通过使用 `MPI_Reduce` 得到全局距平的平方 `global_sq_diff`。
+然后,根进程可以通过取全局距平的平方的平均值的平方根来计算标准差。
+
+使用运行脚本运行示例代码将产生如下输出:
+
+```
+>>> ./run.py reduce_stddev
+mpirun -n 4 ./reduce_stddev 100
+Mean - 0.501100, Standard deviation = 0.301126
+```
+
+## Up next
+
+现在您可以轻松使用所有常见的集合 - `MPI_Bcast`,`MPI_Scatter`,`MPI_Gather` 和 `MPI_Reduce`,我们可以利用它们来构建复杂的并行程序。
+在下一节中,我们将开始研究 [MPI 组和通讯器]({{ site.baseurl }}/tutorials/introduction-to-groups-and-communicators/).
+
+对于所有教程,请转到 [MPI 教程部分]({{ site.baseurl }}/tutorials/).
diff --git a/tutorials/mpi-scatter-gather-and-allgather/code/all_avg b/tutorials/mpi-scatter-gather-and-allgather/code/all_avg
deleted file mode 100755
index 041e074..0000000
Binary files a/tutorials/mpi-scatter-gather-and-allgather/code/all_avg and /dev/null differ
diff --git a/tutorials/mpi-scatter-gather-and-allgather/code/all_avg.c b/tutorials/mpi-scatter-gather-and-allgather/code/all_avg.c
index 981773d..c7bea7d 100644
--- a/tutorials/mpi-scatter-gather-and-allgather/code/all_avg.c
+++ b/tutorials/mpi-scatter-gather-and-allgather/code/all_avg.c
@@ -2,16 +2,17 @@
// Copyright 2012 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Program that computes the average of an array of elements in parallel using
// MPI_Scatter and MPI_Allgather
//
#include
#include
+#include
#include
#include
-
+
// Creates an array of random numbers. Each number has a value from 0 - 1
float *create_rand_nums(int num_elements) {
float *rand_nums = (float *)malloc(sizeof(float) * num_elements);
@@ -70,7 +71,7 @@ int main(int argc, char** argv) {
// Compute the average of your subset
float sub_avg = compute_avg(sub_rand_nums, num_elements_per_proc);
-
+
// Gather all partial averages down to all the processes
float *sub_avgs = (float *)malloc(sizeof(float) * world_size);
assert(sub_avgs != NULL);
@@ -89,7 +90,7 @@ int main(int argc, char** argv) {
}
free(sub_avgs);
free(sub_rand_nums);
-
+
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
}
diff --git a/tutorials/mpi-scatter-gather-and-allgather/code/avg b/tutorials/mpi-scatter-gather-and-allgather/code/avg
deleted file mode 100755
index ffce387..0000000
Binary files a/tutorials/mpi-scatter-gather-and-allgather/code/avg and /dev/null differ
diff --git a/tutorials/mpi-scatter-gather-and-allgather/code/avg.c b/tutorials/mpi-scatter-gather-and-allgather/code/avg.c
index ebb650a..a2639f5 100644
--- a/tutorials/mpi-scatter-gather-and-allgather/code/avg.c
+++ b/tutorials/mpi-scatter-gather-and-allgather/code/avg.c
@@ -2,16 +2,17 @@
// Copyright 2012 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Program that computes the average of an array of elements in parallel using
// MPI_Scatter and MPI_Gather
//
#include
#include
+#include
#include
#include
-
+
// Creates an array of random numbers. Each number has a value from 0 - 1
float *create_rand_nums(int num_elements) {
float *rand_nums = (float *)malloc(sizeof(float) * num_elements);
@@ -70,7 +71,7 @@ int main(int argc, char** argv) {
// Compute the average of your subset
float sub_avg = compute_avg(sub_rand_nums, num_elements_per_proc);
-
+
// Gather all partial averages down to the root process
float *sub_avgs = NULL;
if (world_rank == 0) {
@@ -98,7 +99,7 @@ int main(int argc, char** argv) {
free(sub_avgs);
}
free(sub_rand_nums);
-
+
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
}
diff --git a/tutorials/mpi-scatter-gather-and-allgather/index.md b/tutorials/mpi-scatter-gather-and-allgather/index.md
index 70074df..787e391 100644
--- a/tutorials/mpi-scatter-gather-and-allgather/index.md
+++ b/tutorials/mpi-scatter-gather-and-allgather/index.md
@@ -4,12 +4,13 @@ title: MPI Scatter, Gather, and Allgather
author: Wes Kendall
categories: Beginner MPI
tags: MPI_Gather, MPI_Allgather, MPI_Scatter
+translations: zh_cn,ja_jp
redirect_from: '/mpi-scatter-gather-and-allgather/'
---
In the [previous lesson]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/), we went over the essentials of collective communication. We covered the most basic collective communication routine - `MPI_Bcast`. In this lesson, we are going to expand on collective communication routines by going over two very important routines - `MPI_Scatter` and `MPI_Gather`. We will also cover a variant of `MPI_Gather`, known as `MPI_Allgather`.
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-scatter-gather-and-allgather/code]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-scatter-gather-and-allgather/code]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code).
## An introduction to MPI_Scatter
`MPI_Scatter` is a collective routine that is very similar to `MPI_Bcast` (If you are unfamiliar with these terms, please read the [previous lesson]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/)). `MPI_Scatter` involves a designated root process sending data to all processes in a communicator. The primary difference between `MPI_Bcast` and `MPI_Scatter` is small but important. `MPI_Bcast` sends the *same* piece of data to all processes while `MPI_Scatter` sends *chunks of an array* to different processes. Check out the illustration below for further clarification.
@@ -153,4 +154,4 @@ As you may have noticed, the only difference between all_avg.c and avg.c is that
## Up next
In the next lesson, I cover an application example of using `MPI_Gather` and `MPI_Scatter` to [perform parallel rank computation]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/).
-For all beginner lessons, go the the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/).
\ No newline at end of file
+For all lessons, go the the [MPI tutorials]({{ site.baseurl }}/tutorials/).
\ No newline at end of file
diff --git a/tutorials/mpi-scatter-gather-and-allgather/ja_jp.md b/tutorials/mpi-scatter-gather-and-allgather/ja_jp.md
new file mode 100644
index 0000000..344bd45
--- /dev/null
+++ b/tutorials/mpi-scatter-gather-and-allgather/ja_jp.md
@@ -0,0 +1,154 @@
+---
+layout: post
+title: MPI Scatter, Gather, Allgather
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Gather, MPI_Allgather, MPI_Scatter
+redirect_from: '/mpi-scatter-gather-and-allgather/'
+---
+
+[前のレッスン]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/)では集団通信の基本である集団コミュニケーションルーチン`MPI_Bcast`を説明しました。今回のレッスンではさらに集団通信を学んでいきましょう。非常に重要なルーチン`MPI_Scatter`と`MPI_Gather`そして`MPI_Allgather`をみていきましょう。
+
+> **Note** - 全てのコードは[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは[tutorials/mpi-scatter-gather-and-allgather/code]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code)をみてください。
+
+## MPI_Scatterとは - An introduction to MPI_Scatter
+`MPI_Scatter`は`MPI_Bcast`に似た集団通信ルーチンです。ルートプロセスはコミュニケータ内のすべてのプロセスにデータを送信します。ただし、`MPI_Bcast`はルートの持つ同じデータと全く同じデータをすべてのプロセスに送信していたのに対し、`MPI_Scatter`はルートの持つ配列のチャンクをそれぞれ異なるプロセスに送信します。
+
+
+
+`MPI_Bcast`はルートプロセス(赤)のデータを全てのプロセスにコピーしました。一方、`MPI_Scatter`は配布したい配列をプロセスランクの順に要素を分けて配布します。最初の要素 (赤) はプロセス0に配る, 2番目の要素 (緑) はプロセス1に配る、といったようにです。ルートプロセス(プロセス0)はデータの配列全体を持っており、`MPI_Scatter`は適切な要素をプロセスの受信バッファにコピーします。`MPI_Scatter`の関数定義は以下の通りです。
+
+```cpp
+MPI_Scatter(
+ void* send_data,
+ int send_count,
+ MPI_Datatype send_datatype,
+ void* recv_data,
+ int recv_count,
+ MPI_Datatype recv_datatype,
+ int root,
+ MPI_Comm communicator)
+```
+
+最初の引数`send_data`はルートプロセスにおける配布したい配列のポインタです。2番目と3番目の引数`send_count`と`send_datatype`は、各プロセスに対してどんなMPI Datatypeの要素を何個送信するかを指定します。`send_count` が1で`send_datatype`が MPI_INTならば、プロセス0は配列の最初の整数を受け取り、プロセス 1は2番目の整数を受け取ります。`send_count`が2なら、プロセス0は1,2番目の整数を担当し、プロセス1は3,4番目の整数を担当します。実際には`send_count`は配列の要素数をプロセス数で割ったものに等しいでしょう。要素数がプロセス数で割り切れない?ご心配なく。それは後で見ていきましょう。
+
+後続の引数はほぼ同様です。`recv_data`は`recv_datatype`のデータ型を持つ`recv_count`個の要素を保持できるデータのバッファです。`root`と`communicator`はルートプロセスのランクと、コミュニケータです。
+
+## MPI_Gatherとは - An introduction to MPI_Gather
+`MPI_Gather`は`MPI_Scatter`の逆の動きをする関数です。要素を1つのプロセスから多数のプロセスに分散させるのではなく、`MPI_Gather`多数のプロセスから要素を取得して1つのプロセスに集めます。このルーチンは並列ソートや並列検索などの多くの並列アルゴリズムで使われます。
+
+
+
+`MPI_Scatter` の逆と考えれば良いです。 `MPI_Gather` は各プロセスから要素を受け取ってルートプロセスに集めます。そして要素を受け取ったプロセスの順位で並べます。`MPI_Gather` の関数宣言は `MPI_Scatter` と同じです。
+
+```cpp
+MPI_Gather(
+ void* send_data,
+ int send_count,
+ MPI_Datatype send_datatype,
+ void* recv_data,
+ int recv_count,
+ MPI_Datatype recv_datatype,
+ int root,
+ MPI_Comm communicator)
+```
+
+`MPI_Gather` では、ルートプロセスだけが有効な受信バッファを持てば良いので、ルートプロセス以外は`recv_data` に `NULL` を渡して良いです。また、*recv_count* パラメータは、全プロセスからのカウントの合計ではなく、*プロセスごとに*受信した要素のカウントであることを忘れないでください。これはMPIを触ったばかりのプログラマを混乱させます。
+
+## 数値の平均 - Computing average of numbers with MPI_Scatter and MPI_Gather
+レッスンの[レポジトリ]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code)では、配列内の数値の平均を計算するサンプルプログラム([avg.c]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code/avg.c))を提供しています。MPIを使用してプロセスを分割し、プロセスごとに計算を実行し、それらの小さな結果を集約して最終的な答えを出すという簡単なプログラムです。まずは動作を確認しましょう。
+
+1. ルートプロセス(プロセス0) でランダムな内容のの配列を生成します。
+2. 配列をすべてのプロセスを分散します。各プロセスに同じ数のデータを割り当てます。
+3. 各プロセスは割り当てられた数値の平均を計算します。
+4. 各プロセスは結果をルートプロセスに収集します。ルートプロセスはこれらの数値の平均を計算して最終的な平均を取得します。
+
+```cpp
+if (world_rank == 0) {
+ rand_nums = create_rand_nums(elements_per_proc * world_size);
+}
+
+// 乱数の配列を作ります。長さは固定
+float *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);
+
+// 配列を全てのプロセスにScatterする
+MPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums,
+ elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);
+
+// 各プロセスは自分に割り当てられた配列の平均を計算する
+float sub_avg = compute_avg(sub_rand_nums, elements_per_proc);
+// ルートプロセスは各プロセスの値を集める
+float *sub_avgs = NULL;
+if (world_rank == 0) {
+ sub_avgs = malloc(sizeof(float) * world_size);
+}
+MPI_Gather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0,
+ MPI_COMM_WORLD);
+
+// ルートプロセスは集めた平均を計算する
+if (world_rank == 0) {
+ float avg = compute_avg(sub_avgs, world_size);
+}
+```
+
+最初にルートプロセスは乱数の配列を作成します。 `MPI_Scatter`によって各プロセスには`elements_per_proc`のデータが配布されます。各プロセスは自分に割り当てられた配列の平均を計算し、ルートプロセスはそれぞれの平均を収集します。ルートプロセスでの全体の平均計算処理は、非常に小さい配列に基づいて計算すれば良いです。
+
+[レポジトリ]({{ site.github.code }})のチュートリアルディレクトリから avg プログラムを実行すると、出力は次のようになります。数字はランダムなので、この出力結果と異なるでしょう。
+
+```
+>>> cd tutorials
+>>> ./run.py avg
+/home/kendall/bin/mpirun -n 4 ./avg 100
+Avg of all elements is 0.478699
+Avg computed across original data is 0.478699
+```
+
+## MPI_Allgatherと平均プログラムの修正 - MPI_Allgather and modification of average program
+さて、多くのプロセスが1つのプロセスに対してある送受信を行う、つまり多対1または1対多の通信パターンを実行する2つのMPIルーチンをみてきました。ところで、複数のプロセルから多くのプロセスに要素を送信できることは便利です。そう、多対多の集団通信パターンです。`MPI_Allgather`というルーチンを説明します。
+
+`MPI_Allgather`は全プロセスに分散している要素を全プロセスに配布するルーチンです。つまり、`MPI_Allgather`は`MPI_Gather`を行った後に`MPI_Bcast`しているような動作をします。下図は`MPI_Allgather`を呼び出したデータの動きです。
+
+
+
+`MPI_Gather`と同じように、各プロセスに対して各プロセスが持っていた要素をランク順に集める。それだけです。`MPI_Allgather`の引数は`MPI_Gather`とほぼ同じですが、`MPI_Allgather`にはルートプロセスの引数は存在しません。
+
+```cpp
+MPI_Allgather(
+ void* send_data,
+ int send_count,
+ MPI_Datatype send_datatype,
+ void* recv_data,
+ int recv_count,
+ MPI_Datatype recv_datatype,
+ MPI_Comm communicator)
+```
+
+`MPI_Allgather`を使うように平均計算コードを修正しました。このレッスンのコードからall_avg.c]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code/all_avg.c)のソースを見ることができます。コードの主な違いは次のとおりです。
+
+```cpp
+// 全てのプロセスに対して各プロセスで計算した平均を集める
+float *sub_avgs = (float *)malloc(sizeof(float) * world_size);
+MPI_Allgather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT,
+ MPI_COMM_WORLD);
+
+// 各プロセスは全ての平均を求める
+float avg = compute_avg(sub_avgs, world_size);
+```
+
+各プロセスの平均を`MPI_Allgather`を使って全プロセスに集めます。そして、すべてのプロセスでその平均を計算して出力します。
+
+```
+>>> ./run.py all_avg
+/home/kendall/bin/mpirun -n 4 ./all_avg 100
+Avg of all elements from proc 1 is 0.479736
+Avg of all elements from proc 3 is 0.479736
+Avg of all elements from proc 0 is 0.479736
+Avg of all elements from proc 2 is 0.479736
+```
+
+このようにall_avg.c では`MPI_Allgather`で全てのプロセスに各プロセスの平均を集めて表示します。
+
+## Up next
+次のレッスンでは `MPI_Gather`と`MPI_Scatter`を利用して[並列なランク計算]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/)を説明します。
+
+その他のレッスンは[MPI tutorials]({{ site.baseurl }}/tutorials/) にあります。
\ No newline at end of file
diff --git a/tutorials/mpi-scatter-gather-and-allgather/zh_cn.md b/tutorials/mpi-scatter-gather-and-allgather/zh_cn.md
new file mode 100644
index 0000000..5eee930
--- /dev/null
+++ b/tutorials/mpi-scatter-gather-and-allgather/zh_cn.md
@@ -0,0 +1,157 @@
+---
+layout: post
+title: MPI Scatter, Gather, and Allgather
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Gather, MPI_Allgather, MPI_Scatter
+redirect_from: '/mpi-scatter-gather-and-allgather/'
+---
+在[之前的课程]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/zh_cn)里,我们讲述了集体通信的必要知识点。我们讲了基础的广播通信机制 - `MPI_Bcast`。在这节课里,我们会讲述两个额外的机制来补充集体通信的知识 - `MPI_Scatter` 以及 `MPI_Gather`。我们还会讲一个 `MPI_Gather` 的变体:`MPI_Allgather`。
+
+> **注意** - 这个网站的提到的所有代码都在 [GitHub]({{ site.github.repo }}) 上面。这篇教程的代码在 [tutorials/mpi-scatter-gather-and-allgather/code]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code)。
+
+## MPI_Scatter 的介绍
+`MPI_Scatter` 是一个跟 `MPI_Bcast` 类似的集体通信机制(如果你对这些词汇不熟悉的话,请阅读[上一节课]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/zh_cn)。`MPI_Scatter` 的操作会设计一个指定的根进程,根进程会将数据发送到 communicator 里面的所有进程。`MPI_Bcast` 和 `MPI_Scatter` 的主要区别很小但是很重要。`MPI_Bcast` 给每个进程发送的是*同样*的数据,然而 `MPI_Scatter` 给每个进程发送的是*一个数组的一部分数据*。下图进一步展示了这个区别。
+
+
+
+在图中我们可以看到,`MPI_Bcast` 在根进程上接收一个单独的数据元素(红色的方块),然后把它复制到所有其他的进程。`MPI_Scatter` 接收一个数组,并把元素按进程的秩分发出去。第一个元素(红色方块)发往进程0,第二个元素(绿色方块)发往进程1,以此类推。尽管根进程(进程0)拥有整个数组的所有元素,`MPI_Scatter` 还是会把正确的属于进程0的元素放到这个进程的接收缓存中。下面的 `MPI_Scatter` 函数的原型。
+
+```cpp
+MPI_Scatter(
+ void* send_data,
+ int send_count,
+ MPI_Datatype send_datatype,
+ void* recv_data,
+ int recv_count,
+ MPI_Datatype recv_datatype,
+ int root,
+ MPI_Comm communicator)
+```
+
+这个函数看起来确实很大很吓人,别怕,我们来详细解释一下。第一个参数,`send_data`,是在根进程上的一个数据数组。第二个和第三个参数,`send_count` 和 `send_datatype` 分别描述了发送给每个进程的数据数量和数据类型。如果 `send_count` 是1,`send_datatype` 是 `MPI_INT`的话,进程0会得到数据里的第一个整数,以此类推。如果`send_count`是2的话,进程0会得到前两个整数,进程1会得到第三个和第四个整数,以此类推。在实践中,一般来说`send_count`会等于数组的长度除以进程的数量。除不尽怎么办?我们会在后面的课程中讲这个问题 :-)。
+
+函数定义里面接收数据的参数跟发送的参数几乎相同。`recv_data` 参数是一个缓存,它里面存了`recv_count`个`recv_datatype`数据类型的元素。最后两个参数,`root` 和 `communicator` 分别指定开始分发数组的根进程以及对应的communicator。
+
+## MPI_Gather 的介绍
+`MPI_Gather` 跟 `MPI_Scatter` 是相反的。`MPI_Gather` 从好多进程里面收集数据到一个进程上面而不是从一个进程分发数据到多个进程。这个机制对很多平行算法很有用,比如并行的排序和搜索。下图是这个算法的一个示例。
+
+
+
+跟`MPI_Scatter`类似,`MPI_Gather`从其他进程收集元素到根进程上面。元素是根据接收到的进程的秩排序的。`MPI_Gather`的函数原型跟`MPI_Scatter`长的一样。
+
+```cpp
+MPI_Gather(
+ void* send_data,
+ int send_count,
+ MPI_Datatype send_datatype,
+ void* recv_data,
+ int recv_count,
+ MPI_Datatype recv_datatype,
+ int root,
+ MPI_Comm communicator)
+```
+
+在`MPI_Gather`中,只有根进程需要一个有效的接收缓存。所有其他的调用进程可以传递`NULL`给`recv_data`。另外,别忘记*recv_count*参数是从*每个进程*接收到的数据数量,而不是所有进程的数据总量之和。这一点对MPI初学者来说经常容易搞错。
+
+
+## 使用 `MPI_Scatter` 和 `MPI_Gather` 来计算平均数
+在[这节课的代码]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code)里,我提供了一个用来计算数组里面所有数字的平均数的样例程序([avg.c]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code/avg.c))。尽管这个程序十分简单,但是它展示了我们如何使用MPI来把工作拆分到不同的进程上,每个进程对一部分数据进行计算,然后再把每个部分计算出来的结果汇集成最终的答案。这个程序有以下几个步骤:
+1. 在根进程(进程0)上生成一个充满随机数字的数组。
+2. 把所有数字用`MPI_Scatter`分发给每个进程,每个进程得到的同样多的数字。
+3. 每个进程计算它们各自得到的数字的平均数。
+4. 根进程收集所有的平均数,然后计算这个平均数的平均数,得出最后结果。
+
+代码里面有 MPI 调用的主要部分如下所示:
+
+```cpp
+if (world_rank == 0) {
+ rand_nums = create_rand_nums(elements_per_proc * world_size);
+}
+
+// Create a buffer that will hold a subset of the random numbers
+float *sub_rand_nums = malloc(sizeof(float) * elements_per_proc);
+
+// Scatter the random numbers to all processes
+MPI_Scatter(rand_nums, elements_per_proc, MPI_FLOAT, sub_rand_nums,
+ elements_per_proc, MPI_FLOAT, 0, MPI_COMM_WORLD);
+
+// Compute the average of your subset
+float sub_avg = compute_avg(sub_rand_nums, elements_per_proc);
+// Gather all partial averages down to the root process
+float *sub_avgs = NULL;
+if (world_rank == 0) {
+ sub_avgs = malloc(sizeof(float) * world_size);
+}
+MPI_Gather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, 0,
+ MPI_COMM_WORLD);
+
+// Compute the total average of all numbers.
+if (world_rank == 0) {
+ float avg = compute_avg(sub_avgs, world_size);
+}
+```
+
+代码开头根进程创建里一个随机数的数组。当`MPI_Scatter`被调用的时候,每个进程现在都持有`elements_per_proc`个原始数据里面的元素。每个进程计算子数组的平均数,然后根进程收集这些平均数。然后总的平均数就可以在这个小的多的平均数数组里面被计算出来。
+
+如果你运行这个[repo]({{ site.github.code }})下面*tutorials*目录下的代码,输出应该跟下面的类似。注意因为数字是随机生成的,所以你的最终结果可能跟我的不一样。
+
+
+```
+>>> cd tutorials
+>>> ./run.py avg
+/home/kendall/bin/mpirun -n 4 ./avg 100
+Avg of all elements is 0.478699
+Avg computed across original data is 0.478699
+```
+
+## MPI_Allgather 以及修改后的平均程序
+到目前为止,我们讲解了两个用来操作*多对一*或者*一对多*通信模式的MPI方法,也就是说多个进程要么向一个进程发送数据,要么从一个进程接收数据。很多时候发送多个元素到多个进程也很有用(也就是*多对多*通信模式)。`MPI_Allgather`就是这个作用。
+
+对于分发在所有进程上的一组数据来说,`MPI_Allgather`会收集所有数据到所有进程上。从最基础的角度来看,`MPI_Allgather`相当于一个`MPI_Gather`操作之后跟着一个`MPI_Bcast`操作。下面的示意图显示了`MPI_Allgather`调用之后数据是如何分布的。
+
+
+
+就跟`MPI_Gather`一样,每个进程上的元素是根据他们的秩为顺序被收集起来的,只不过这次是收集到了所有进程上面。很简单吧?`MPI_Allgather`的方法定义跟`MPI_Gather`几乎一样,只不过`MPI_Allgather`不需要root这个参数来指定根节点。
+
+```cpp
+MPI_Allgather(
+ void* send_data,
+ int send_count,
+ MPI_Datatype send_datatype,
+ void* recv_data,
+ int recv_count,
+ MPI_Datatype recv_datatype,
+ MPI_Comm communicator)
+```
+
+我把计算平均数的代码修改成了使用`MPI_Allgather`来计算。你可以在[all_avg.c]({{ site.github.code }}/tutorials/mpi-scatter-gather-and-allgather/code/all_avg.c)这个文件里看到源代码。主要的不同点如下所示。
+
+```cpp
+// Gather all partial averages down to all the processes
+float *sub_avgs = (float *)malloc(sizeof(float) * world_size);
+MPI_Allgather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT,
+ MPI_COMM_WORLD);
+
+// Compute the total average of all numbers.
+float avg = compute_avg(sub_avgs, world_size);
+```
+
+现在每个子平均数被`MPI_Allgather`收集到了所有进程上面。最终平均数在每个进程上面都打印出来了。样例运行之后应该跟下面的输出结果类似。
+
+
+```
+>>> ./run.py all_avg
+/home/kendall/bin/mpirun -n 4 ./all_avg 100
+Avg of all elements from proc 1 is 0.479736
+Avg of all elements from proc 3 is 0.479736
+Avg of all elements from proc 0 is 0.479736
+Avg of all elements from proc 2 is 0.479736
+```
+
+跟你注意到的一样,all_avg.c 和 avg.c 之间的唯一的区别就是 all_avg.c 使用`MPI_Allgather`把平均数在每个进程上都打印出来了。
+
+## 接下来
+下节课,我会使用`MPI_Gather`和`MPI_Scatter`做一个应用程序来[计算并行排名]({{ site.baseurl }}/tutorials/performing-parallel-rank-with-mpi/zh_cn)。
+
+你也可以在 [MPI tutorials]({{ site.baseurl }}/tutorials/) 查看所有课程。
diff --git a/tutorials/mpi-send-and-receive/code/ping_pong.c b/tutorials/mpi-send-and-receive/code/ping_pong.c
index fe134fc..776de7e 100644
--- a/tutorials/mpi-send-and-receive/code/ping_pong.c
+++ b/tutorials/mpi-send-and-receive/code/ping_pong.c
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Ping pong example with MPI_Send and MPI_Recv. Two processes ping pong a
// number back and forth, incrementing it until it reaches a given value.
@@ -22,10 +22,10 @@ int main(int argc, char** argv) {
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
- // We are assuming at least 2 processes for this task
+ // We are assuming 2 processes for this task
if (world_size != 2) {
fprintf(stderr, "World size must be two for %s\n", argv[0]);
- MPI_Abort(MPI_COMM_WORLD, 1);
+ MPI_Abort(MPI_COMM_WORLD, 1);
}
int ping_pong_count = 0;
diff --git a/tutorials/mpi-send-and-receive/code/ring.c b/tutorials/mpi-send-and-receive/code/ring.c
index 1211de1..8f6ab35 100644
--- a/tutorials/mpi-send-and-receive/code/ring.c
+++ b/tutorials/mpi-send-and-receive/code/ring.c
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Example using MPI_Send and MPI_Recv to pass a message around in a ring.
//
diff --git a/tutorials/mpi-send-and-receive/code/send_recv.c b/tutorials/mpi-send-and-receive/code/send_recv.c
index 0c9340f..1af8f40 100644
--- a/tutorials/mpi-send-and-receive/code/send_recv.c
+++ b/tutorials/mpi-send-and-receive/code/send_recv.c
@@ -2,10 +2,10 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// MPI_Send, MPI_Recv example. Communicates the number -1 from process 0
-// to processe 1.
+// to process 1.
//
#include
#include
@@ -23,16 +23,29 @@ int main(int argc, char** argv) {
// We are assuming at least 2 processes for this task
if (world_size < 2) {
fprintf(stderr, "World size must be greater than 1 for %s\n", argv[0]);
- MPI_Abort(MPI_COMM_WORLD, 1);
+ MPI_Abort(MPI_COMM_WORLD, 1);
}
int number;
if (world_rank == 0) {
// If we are rank 0, set the number to -1 and send it to process 1
number = -1;
- MPI_Send(&number, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
+ MPI_Send(
+ /* data = */ &number,
+ /* count = */ 1,
+ /* datatype = */ MPI_INT,
+ /* destination = */ 1,
+ /* tag = */ 0,
+ /* communicator = */ MPI_COMM_WORLD);
} else if (world_rank == 1) {
- MPI_Recv(&number, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ MPI_Recv(
+ /* data = */ &number,
+ /* count = */ 1,
+ /* datatype = */ MPI_INT,
+ /* source = */ 0,
+ /* tag = */ 0,
+ /* communicator = */ MPI_COMM_WORLD,
+ /* status = */ MPI_STATUS_IGNORE);
printf("Process 1 received number %d from process 0\n", number);
}
MPI_Finalize();
diff --git a/tutorials/mpi-send-and-receive/index.md b/tutorials/mpi-send-and-receive/index.md
index b8d5b68..c76f236 100644
--- a/tutorials/mpi-send-and-receive/index.md
+++ b/tutorials/mpi-send-and-receive/index.md
@@ -3,18 +3,19 @@ layout: post
title: MPI Send and Receive
author: Wes Kendall
categories: Beginner MPI
+translations: zh_cn,ja_jp
tags: MPI_Recv, MPI_Send
redirect_from: '/mpi-send-and-receive/'
---
-Sending and receiving are the two foundational concepts of MPI. Almost every single function in MPI can be implemented with basic send and receive calls. In this lesson, I will discuss how to use MPI's blocking sending and receiving functions, and I will also overview other basic concepts associated with transmitting data using MPI.
+Sending and receiving are the two foundational concepts of MPI. Almost every single function in MPI can be implemented with basic send and receive calls. In this lesson, I will discuss how to use MPI's blocking sending and receiving functions, and I will also overview other basic concepts associated with transmitting data using MPI.
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code).
## Overview of sending and receiving with MPI
MPI's send and receive calls operate in the following manner. First, process *A* decides a message needs to be sent to process *B*. Process A then packs up all of its necessary data into a buffer for process B. These buffers are often referred to as *envelopes* since the data is being packed into a single message before transmission (similar to how letters are packed into envelopes before transmission to the post office). After the data is packed into a buffer, the communication device (which is often a network) is responsible for routing the message to the proper location. The location of the message is defined by the process's rank.
-Even though the message is routed to B, process B still has to acknowledge that it wants to receive A's data. Once it does this, the data has been transmitted. Process A is acknowledged that the data has been transmitted and may go back to work.
+Even though the message is routed to B, process B still has to acknowledge that it wants to receive A's data. Once it does this, the data has been transmitted. Process A is acknowledged that the data has been transmitted and may go back to work.
Sometimes there are cases when A might have to send many different types of messages to B. Instead of B having to go through extra measures to differentiate all these messages, MPI allows senders and receivers to also specify message IDs with the message (known as *tags*). When process B only requests a message with a certain tag number, messages with different tags will be buffered by the network until B is ready for them.
@@ -44,28 +45,28 @@ MPI_Recv(
Although this might seem like a mouthful when reading all of the arguments, they become easier to remember since almost every MPI call uses similar syntax. The first argument is the data buffer. The second and third arguments describe the count and type of elements that reside in the buffer. `MPI_Send` sends the exact count of elements, and `MPI_Recv` will receive **at most** the count of elements (more on this in the next lesson). The fourth and fifth arguments specify the rank of the sending/receiving process and the tag of the message. The sixth argument specifies the communicator and the last argument (for `MPI_Recv` only) provides information about the received message.
## Elementary MPI datatypes
-The `MPI_Send` and `MPI_Recv` functions utilize MPI Datatypes as a means to specify the structure of a message at a higher level. For example, if the process wishes to send one integer to another, it would use a count of one and a datatype of `MPI_INT`. The other elementary MPI datatypes are listed below with their equivalent C datatypes.
+The `MPI_Send` and `MPI_Recv` functions utilize MPI Datatypes as a means to specify the structure of a message at a higher level. For example, if the process wishes to send one integer to another, it would use a count of one and a datatype of `MPI_INT`. The other elementary MPI datatypes are listed below with their equivalent C datatypes.
| MPI datatype | C equivalent |
| --- | --- |
| MPI_SHORT | short int |
-| MPI_INT | int |
-| MPI_LONG | long int |
-| MPI_LONG_LONG | long long int |
-| MPI_UNSIGNED_CHAR | unsigned char |
-| MPI_UNSIGNED_SHORT | unsigned short int |
-| MPI_UNSIGNED | unsigned int |
-| MPI_UNSIGNED_LONG | unsigned long int |
-| MPI_UNSIGNED_LONG_LONG | unsigned long long int |
-| MPI_FLOAT | float |
-| MPI_DOUBLE | double |
-| MPI_LONG_DOUBLE | long double |
-| MPI_BYTE | char |
-
-For now, we will only make use of these datatypes in the beginner MPI tutorial. Once we have covered enough basics, you will learn how to create your own MPI datatypes for characterizing more complex types of messages.
+| MPI_INT | int |
+| MPI_LONG | long int |
+| MPI_LONG_LONG | long long int |
+| MPI_UNSIGNED_CHAR | unsigned char |
+| MPI_UNSIGNED_SHORT | unsigned short int |
+| MPI_UNSIGNED | unsigned int |
+| MPI_UNSIGNED_LONG | unsigned long int |
+| MPI_UNSIGNED_LONG_LONG | unsigned long long int |
+| MPI_FLOAT | float |
+| MPI_DOUBLE | double |
+| MPI_LONG_DOUBLE | long double |
+| MPI_BYTE | char |
+
+For now, we will only make use of these datatypes in the following MPI tutorials in the beginner category. Once we have covered enough basics, you will learn how to create your own MPI datatypes for characterizing more complex types of messages.
## MPI send / recv program
-As stated in the beginning, the code for this is available on [Github]({{ site.github.repo }}), and this tutorial's code is under [tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code).
+As stated in the beginning, the code for this is available on [GitHub]({{ site.github.repo }}), and this tutorial's code is under [tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code).
The first example in the tutorial code is in [send_recv.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/send_recv.c). Some of the major parts of the program are shown below.
@@ -91,7 +92,7 @@ if (world_rank == 0) {
`MPI_Comm_rank` and `MPI_Comm_size` are first used to determine the world size along with the rank of the process. Then process zero initializes a number to the value of negative one and sends this value to process one. As you can see in the `else if` statement, process one is calling `MPI_Recv` to receive the number. It also prints off the received value.
Since we are sending and receiving exactly one integer, each process requests that one `MPI_INT` be sent/received. Each process also uses a tag number of zero to identify the message. The processes could have also used the predefined constant `MPI_ANY_TAG` for the tag number since only one type of message was being transmitted.
-You can run the example code by checking it out on [Github]({{ site.github.repo }}) and using the `run.py` script.
+You can run the example code by checking it out on [GitHub]({{ site.github.repo }}) and using the `run.py` script.
```
>>> git clone {{ site.github.repo }}
@@ -119,7 +120,7 @@ while (ping_pong_count < PING_PONG_LIMIT) {
"%d to %d\n", world_rank, ping_pong_count,
partner_rank);
} else {
- MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0,
+ MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
printf("%d received ping_pong_count %d from %d\n",
world_rank, ping_pong_count, partner_rank);
@@ -141,19 +142,19 @@ This example is meant to be executed with only two processes. The processes firs
0 received ping_pong_count 8 from 1
0 sent and incremented ping_pong_count 9 to 1
0 received ping_pong_count 10 from 1
-1 sent and incremented ping_pong_count 1 to 0
-1 received ping_pong_count 2 from 0
-1 sent and incremented ping_pong_count 3 to 0
-1 received ping_pong_count 4 from 0
-1 sent and incremented ping_pong_count 5 to 0
-1 received ping_pong_count 6 from 0
-1 sent and incremented ping_pong_count 7 to 0
-1 received ping_pong_count 8 from 0
-1 sent and incremented ping_pong_count 9 to 0
-1 received ping_pong_count 10 from 0
+1 received ping_pong_count 1 from 0
+1 sent and incremented ping_pong_count 2 to 0
+1 received ping_pong_count 3 from 0
+1 sent and incremented ping_pong_count 4 to 0
+1 received ping_pong_count 5 from 0
+1 sent and incremented ping_pong_count 6 to 0
+1 received ping_pong_count 7 from 0
+1 sent and incremented ping_pong_count 8 to 0
+1 received ping_pong_count 9 from 0
+1 sent and incremented ping_pong_count 10 to 0
```
-The output of the programs on other machines will likely be different because of process scheduling. However, as you can see, process zero and one are both taking turns sending and receiving the ping pong counter to each other.
+The output of the programs on other machines will likely be different because of process scheduling. However, as you can see, process zero and one are both taking turns sending and receiving the ping pong counter to each other.
## Ring Program
I have included one more example of `MPI_Send` and `MPI_Recv` using more than two processes. In this example, a value is passed around by all processes in a ring-like fashion. Take a look at [ring.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/ring.c). The major portion of the code looks like this.
@@ -197,6 +198,6 @@ As we can see, process zero first sends a value of negative one to process one.
## Up next
-Now that you have a basic understanding of `MPI_Send` and `MPI_Recv`, it is now time to go a little bit deeper into these functions. In the next lesson, I cover [how to probe and dynamically receive messages]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/). Feel free to also examine the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/) for a complete reference of all of the beginning MPI lessons.
+Now that you have a basic understanding of `MPI_Send` and `MPI_Recv`, it is now time to go a little bit deeper into these functions. In the next lesson, I cover [how to probe and dynamically receive messages]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/). Feel free to also examine the [MPI tutorials]({{ site.baseurl }}/tutorials/) for a complete reference of all of the MPI lessons.
-Having trouble? Confused? Feel free to leave a comment below and perhaps I or another reader can be of help.
\ No newline at end of file
+Having trouble? Confused? Feel free to leave a comment below and perhaps I or another reader can be of help.
diff --git a/tutorials/mpi-send-and-receive/ja_jp.md b/tutorials/mpi-send-and-receive/ja_jp.md
new file mode 100644
index 0000000..fcaf21d
--- /dev/null
+++ b/tutorials/mpi-send-and-receive/ja_jp.md
@@ -0,0 +1,200 @@
+---
+layout: post
+title: MPIのsendとreceive - MPI Send and Receive
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Recv, MPI_Send
+redirect_from: '/mpi-send-and-receive/'
+---
+
+送信と受信(send,recv)は、MPIもっとも基本的な概念です。MPIのほぼすべての機能はsend,recvをによって実装できます。このレッスンではブロッキング送受信について説明し、MPIのデータ送信の基本的な概念を説明します。
+
+> **Note** - このサイトのコードはすべて[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは[tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code)にあります。
+
+## MPI による送受信の概要: Overview of sending and receiving with MPI
+MPIの基本的な送受信を見ていきましょう。まず、プロセス*A*は、プロセス*B*にメッセージを送信したいとします。プロセス*A*はプロセス*B*に送信したいデータをすべて1つにまとめて(パックして)バッファに格納します。このバッファは、データを1つのメッセージにまとめているのでエンベロープ(*envelopes*)と呼ばれることがあります(郵便の手紙が封筒にパックされるのと同じです)。データがバッファにパックされた後は通信デバイス(多くはネットワークでしょう)がメッセージを適切にルーティングします。メッセージにおける宛先はプロセスのランクによって定義されます。
+
+メッセージは*B*にむけてルーティングされます。次にプロセス*B*は*A*からデータを受信する意思があることを明確に通知(acknowledge)する必要があります。通知が完了するとデータは送信されたことになり、プロセス*A*に対してデータが送信されたことが通知され、Aのブロッキングは完了し、次の処理に移れます。
+
+次はAがBにいくつかのタイプ種類のメッセージを送信することを考えます。Bがこれらのメッセージを区別するために特別な方法を取らずとも、MPIはメッセージにID (タグ - tagsと呼ばれる)をつけた送信と受信ができます。プロセスBは特定のタグのメッセージのみを要求でき、その他の異なるタグのメッセージはBがそれを受け取る要求をするまでネットワークレイヤによってバッファリングされています。
+
+MPI send関数とrecv関数の定義を見てみましょう。
+
+```cpp
+MPI_Send(
+ void* data,
+ int count,
+ MPI_Datatype datatype,
+ int destination,
+ int tag,
+ MPI_Comm communicator)
+```
+
+```cpp
+MPI_Recv(
+ void* data,
+ int count,
+ MPI_Datatype datatype,
+ int source,
+ int tag,
+ MPI_Comm communicator,
+ MPI_Status* status)
+```
+
+複雑で長い引数に見えるかもしれませんが、多くのMPI関数では同様の構文が使用されるためご安心ください。最初の引数はデータバッファのアドレスです。2番目と3番目の引数は、バッファー内にある要素の数とタイプです。`MPI_Send`は正確な数の要素を送信し、`MPI_Recv`は"最低でも"count個の要素を受信します(これについては次のレッスンで詳しく説明します)。4番目と5番目の引数は、sendあるいはrecvするプロセスのランクとタグを指定します。6番目の引数はコミュニケータを指定します。recvのみに含まれる最後の引数`status`は、受信したメッセージに関する情報のポインタです。
+
+## 基本的な MPI データ型 - Elementary MPI datatypes
+`MPI_Send`と`MPI_Recv`関数は、メッセージのデータ構造をC言語のデータ型ではなくて、より高いレベルのデータ型で指定できます。たとえば、プロセスが1つの整数の送信する場合、カウント1と`MPI_INT`データ型を使用します。基本的なMPIデータ型と、それに相当するC言語のデータ型を以下に示します。
+
+| MPIデータ型 | C言語のデータ型 |
+| ---------------------- | ---------------------- |
+| MPI_SHORT | short int |
+| MPI_INT | int |
+| MPI_LONG | long int |
+| MPI_LONG_LONG | long long int |
+| MPI_UNSIGNED_CHAR | unsigned char |
+| MPI_UNSIGNED_SHORT | unsigned short int |
+| MPI_UNSIGNED | unsigned int |
+| MPI_UNSIGNED_LONG | unsigned long int |
+| MPI_UNSIGNED_LONG_LONG | unsigned long long int |
+| MPI_FLOAT | float |
+| MPI_DOUBLE | double |
+| MPI_LONG_DOUBLE | long double |
+| MPI_BYTE | char |
+
+これらのデータ型は初心者向けの次のMPIチュートリアルでのみ使用します。基礎を学習したら複雑なメッセージを扱うための独自のMPIデータ型を作成する方法も学習します。
+
+## MPI send / recv program
+それではコードを見ていきます。冒頭にあるように、このサイトのコードはすべて[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは[tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code)にあります。
+
+最初に見るのは[send_recv.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/send_recv.c). です。プログラムのメインとなる一部は以下の通りです。
+
+```cpp
+// Find out rank, size
+int world_rank;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+int world_size;
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+int number;
+if (world_rank == 0) {
+ number = -1;
+ MPI_Send(&number, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
+} else if (world_rank == 1) {
+ MPI_Recv(&number, 1, MPI_INT, 0, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+ printf("Process 1 received number %d from process 0\n",
+ number);
+}
+```
+
+`MPI_Comm_rank`と`MPI_Comm_size`はワールドサイズとプロセスのランクを得るための関数です。プロセス0は整数データを-1に初期化し、この値をプロセス1に送信します。ステートメントでわかるように、プロセス1は数値を受信するために`else if`で分岐して、受信した値も出力します。送受信したいのは1つのINTだけなので、各プロセスは1つの`MPI_INT`の送信と受信を行います。また、メッセージを識別するためにタグ番号0を指定しました。送信されるメッセージの種類は1種類なのでプロセスはタグ番号に定義済みの定数`MPI_ANY_TAG`を使用してもよいです。
+
+サンプルコードの全文は[GitHub]({{ site.github.repo }}) で確認できますし、`run.py` を使って実行することもできます。
+
+```
+>>> git clone {{ site.github.repo }}
+>>> cd mpitutorial/tutorials
+>>> ./run.py send_recv
+mpirun -n 2 ./send_recv
+Process 1 received number -1 from process 0
+```
+
+プロセス1はプロセス0から-1を受け取りましたことがわかります。
+
+## MPI ping - MPI ping pong program
+次の例はpingプログラムです。この例では、プロセスは`MPI_Send`と`MPI_Recv`を利用して停止するまでメッセージを送受信します。[ping_pong.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/ping_pong.c)を参照してください。コードのメイン部分は次の通りです。
+
+```cpp
+int ping_pong_count = 0;
+int partner_rank = (world_rank + 1) % 2;
+while (ping_pong_count < PING_PONG_LIMIT) {
+ if (world_rank == ping_pong_count % 2) {
+ // Increment the ping pong count before you send it
+ ping_pong_count++;
+ MPI_Send(&ping_pong_count, 1, MPI_INT, partner_rank, 0,
+ MPI_COMM_WORLD);
+ printf("%d sent and incremented ping_pong_count "
+ "%d to %d\n", world_rank, ping_pong_count,
+ partner_rank);
+ } else {
+ MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("%d received ping_pong_count %d from %d\n",
+ world_rank, ping_pong_count, partner_rank);
+ }
+}
+```
+
+このコードは2つのプロセスのみで実行されることを想定します。プロセスは最初に簡単な演算で相手のランクを決定します。`ping_pong_count`は0で初期化し、プロセスの各送信ステップで+1します。そして、送信側と受信側を交互に担当します(訳註:一般的なpingと違い、互いに自発的にフレームを送ることに注意)。最後に、limitの回数に達すると(私のコードでは10)プロセスは動作を停止します。出力は次のようになります。
+
+```
+>>> ./run.py ping_pong
+0 sent and incremented ping_pong_count 1 to 1
+0 received ping_pong_count 2 from 1
+0 sent and incremented ping_pong_count 3 to 1
+0 received ping_pong_count 4 from 1
+0 sent and incremented ping_pong_count 5 to 1
+0 received ping_pong_count 6 from 1
+0 sent and incremented ping_pong_count 7 to 1
+0 received ping_pong_count 8 from 1
+0 sent and incremented ping_pong_count 9 to 1
+0 received ping_pong_count 10 from 1
+1 received ping_pong_count 1 from 0
+1 sent and incremented ping_pong_count 2 to 0
+1 received ping_pong_count 3 from 0
+1 sent and incremented ping_pong_count 4 to 0
+1 received ping_pong_count 5 from 0
+1 sent and incremented ping_pong_count 6 to 0
+1 received ping_pong_count 7 from 0
+1 sent and incremented ping_pong_count 8 to 0
+1 received ping_pong_count 9 from 0
+1 sent and incremented ping_pong_count 10 to 0
+```
+
+出力はプロセススケジューリングのため異なる可能性があります。ただし、出力が例と違ってもプロセス0とプロセス1は、交互に`ping_pong_count`ことが読み取れるでしょう。
+
+## Ring Program
+`MPI_Send`と`MPI_Recv`を使って2つ以上のプロセスで通信することを考えましょう。この例では、値をすべてのプロセス上をリング状に流れていきます。[ring.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/ring.c)を見てください。コードのメイン部分は次のようになります。
+
+```cpp
+int token;
+if (world_rank != 0) {
+ MPI_Recv(&token, 1, MPI_INT, world_rank - 1, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("Process %d received token %d from process %d\n",
+ world_rank, token, world_rank - 1);
+} else {
+ // ランク0はトークンを-1で初期化する
+ token = -1;
+}
+MPI_Send(&token, 1, MPI_INT, (world_rank + 1) % world_size,
+ 0, MPI_COMM_WORLD);
+
+// ここでプロセス0はリンク上の最後の受信処理を行います
+if (world_rank == 0) {
+ MPI_Recv(&token, 1, MPI_INT, world_size - 1, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("Process %d received token %d from process %d\n",
+ world_rank, token, world_size - 1);
+}
+```
+
+リングプログラムはプロセス0で値を初期化し、その値を次のプロセスに渡します。プロセス0が最後のプロセスから値を受信するとプログラムは終了します。デッドロックが発生しないように特別なロジックになっています。プロセス0は(最後のプロセスから)値をブロッキング受信する前に、最初の送信を完了していることが保証されます。他のすべてのプロセスはリングに沿って値を渡します。`MPI_Recv`(隣接するランクの低いプロセスから受信)で値を受け取り、次に`MPI_Send`(隣接するランクの高いプロセスに値を送信) を呼び出します。メッセージが送信されるまでブロックします。このため、printfは値が渡される順序で実行されます。5つのプロセスを使用する場合、出力は次のようになりました。
+
+```
+>>> ./run.py ring
+Process 1 received token -1 from process 0
+Process 2 received token -1 from process 1
+Process 3 received token -1 from process 2
+Process 4 received token -1 from process 3
+Process 0 received token -1 from process 4
+```
+
+プロセス0は最初にプロセス1に-1を送信します。この値(トークン)はプロセス0に戻るまでリング状に渡されています。
+
+## Up next
+
+`MPI_Send`と`MPI_Recv`の基本を理解したのでこれらの関数についてもう少し詳しく見ていきます。次のレッスンは、[how to probe and dynamically receive messages]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/)です。[MPI tutorials]({{ site.baseurl }}/tutorials/)をMPIレッスンの完全なリファレンスとして活用してください。
+
+困っていますか?混乱していますか?お気軽に下記にコメントを残してください。私や他の読者がお役に立てるかもしれません。
diff --git a/tutorials/mpi-send-and-receive/zh_cn.md b/tutorials/mpi-send-and-receive/zh_cn.md
new file mode 100644
index 0000000..cc87c90
--- /dev/null
+++ b/tutorials/mpi-send-and-receive/zh_cn.md
@@ -0,0 +1,201 @@
+---
+layout: post
+title: MPI Send and Receive
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Recv, MPI_Send
+redirect_from: '/mpi-send-and-receive/zh_cn'
+---
+
+发送和接收是 MPI 里面两个基础的概念。MPI 里面几乎所有单个的方法都可以使用基础的发送和接收 API 来实现。在这节课里,我会介绍怎么使用 MPI 的同步的(或阻塞的,原文是 blocking)发送和接收方法,以及另外的一些跟使用 MPI 进行数据传输的基础概念。
+
+> **注意** - 这个网站的提到的所有代码都在 [GitHub]({{ site.github.repo }}) 上面。这篇教程的代码在 [tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code)。
+
+
+## MPI 的发送和接收简介
+MPI 的发送和接收方法是按以下方式进行的:开始的时候,*A* 进程决定要发送一些消息给 *B* 进程。A进程就会把需要发送给B进程的所有数据打包好,放到一个缓存里面。因为所有数据会被打包到一个大的信息里面,因此缓存常常会被比作*信封*(就像我们把好多信纸打包到一个信封里面然后再寄去邮局)。数据打包进缓存之后,通信设备(通常是网络)就需要负责把信息传递到正确的地方。这个正确的地方也就是根据特定秩确定的那个进程。
+
+尽管数据已经被送达到 B 了,但是进程 B 依然需要确认它想要接收 A 的数据。一旦它确定了这点,数据就被传输成功了。进程 A 会接收到数据传递成功的信息,然后去干其他事情。
+
+有时候 A 需要传递很多不同的消息给 B。为了让 B 能比较方便地区分不同的消息,MPI 运行发送者和接受者额外地指定一些信息 ID (正式名称是*标签*, *tags*)。当 B 只要求接收某种特定标签的信息的时候,其他的不是这个标签的信息会先被缓存起来,等到 B 需要的时候才会给 B。
+
+把这些概念记在心里的同时,让我们来看一下 MPI 发送和接收方法的定义。
+
+```cpp
+MPI_Send(
+ void* data,
+ int count,
+ MPI_Datatype datatype,
+ int destination,
+ int tag,
+ MPI_Comm communicator)
+```
+
+```cpp
+MPI_Recv(
+ void* data,
+ int count,
+ MPI_Datatype datatype,
+ int source,
+ int tag,
+ MPI_Comm communicator,
+ MPI_Status* status)
+```
+
+尽管一开始看起来参数有点多,慢慢地你会发现其实这些参数还是很好记忆的,因为大多数的 MPI 方法定义是类似的。第一个参数是数据缓存。第二个和第三个参数分别描述了数据的数量和类型。`MPI_send` 会精确地发送 count 指定的数量个元素,`MPI_Recv` 会**最多**接受 count 个元素(之后会详细讲)。第四个和第五个参数指定了发送方/接受方进程的秩以及信息的标签。第六个参数指定了使用的 communicator。`MPI_Recv` 方法特有的最后一个参数提供了接受到的信息的状态。
+
+## 基础 MPI 数据结构
+`MPI_send` 和 `MPI_Recv` 方法使用了 MPI 的数据结构作为一种在更高层次指定消息结构的方法。举例来说,如果一个进程想要发送一个整数给另一个进程,它会指定 count 为 1,数据结构为 `MPI_INT`。其他的 MPI 数据结构以及它们在 C 语言里对应的结构如下:
+
+| MPI datatype | C equivalent |
+| --- | --- |
+| MPI_SHORT | short int |
+| MPI_INT | int |
+| MPI_LONG | long int |
+| MPI_LONG_LONG | long long int |
+| MPI_UNSIGNED_CHAR | unsigned char |
+| MPI_UNSIGNED_SHORT | unsigned short int |
+| MPI_UNSIGNED | unsigned int |
+| MPI_UNSIGNED_LONG | unsigned long int |
+| MPI_UNSIGNED_LONG_LONG | unsigned long long int |
+| MPI_FLOAT | float |
+| MPI_DOUBLE | double |
+| MPI_LONG_DOUBLE | long double |
+| MPI_BYTE | char |
+
+目前来说,我们在 beginner 栏目里面只会使用到这些基础的数据结构。当我们有了足够多的基础知识之后,你会学习到如何创建自己的 MPI 数据类型来构建更复杂的消息类型。
+
+## MPI 发送 / 接收 程序
+跟开头说的一样,所有代码会在 [GitHub]({{ site.github.repo }}) 上, 这节课的代码在 [tutorials/mpi-send-and-receive/code]({{ site.github.code }}/tutorials/mpi-send-and-receive/code)。
+
+第一个例子的代码在 [send_recv.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/send_recv.c).
+我们来看一下主要的部分:
+
+```cpp
+// 得到当前进程的 rank 以及整个 communicator 的大小
+int world_rank;
+MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
+int world_size;
+MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+int number;
+if (world_rank == 0) {
+ number = -1;
+ MPI_Send(&number, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
+} else if (world_rank == 1) {
+ MPI_Recv(&number, 1, MPI_INT, 0, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+ printf("Process 1 received number %d from process 0\n",
+ number);
+}
+```
+
+`MPI_Comm_rank` 和 `MPI_Comm_size` 一开始是用来得到整个 communicator 空间的大小(也就是所有进程的数量)以及当前进程的秩。然后如果当前进程是 0 进程,那么我们就初始化一个数字 -1 然后把它发送给 1 进程。然后你可以看到 `else if` 条件语句里的话题,进程 1 会调用 `MPI_Recv` 去接受这个数字。然后会将接收到的数字打印出来。由于我们明确地发送接收了一个整数,因此 `MPI_INT` 数据类型被使用了。每个进程还使用了 0 作为消息标签来指定消息。由于我们这里只有一种类型的信息被传递了,因此进程也可以使用预先定义好的常量 `MPI_ANY_TAG` 来作为标签数字。
+
+你可以把代码从[GitHub]({{ site.github.repo }})下载下来并运行 `run.py` 脚本.
+
+
+```
+>>> git clone {{ site.github.repo }}
+>>> cd mpitutorial/tutorials
+>>> ./run.py send_recv
+mpirun -n 2 ./send_recv
+Process 1 received number -1 from process 0
+```
+可以看到跟我们预想的一样,进程一收到了来自进程零传递的数字 -1。
+
+## MPI 乒乓程序
+接下来的程序比较有趣,是一个乒乓游戏。两个进程会一直使用 `MPI_Send` 和 `MPI_Recv` 方法来“推挡”消息,直到他们决定不玩了。
+你可以看一眼代码[ping_pong.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/ping_pong.c)。主要部分如下所示。
+
+```cpp
+int ping_pong_count = 0;
+int partner_rank = (world_rank + 1) % 2;
+while (ping_pong_count < PING_PONG_LIMIT) {
+ if (world_rank == ping_pong_count % 2) {
+ // Increment the ping pong count before you send it
+ ping_pong_count++;
+ MPI_Send(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD);
+ printf("%d sent and incremented ping_pong_count %d to %d\n",
+ world_rank, ping_pong_count,
+ partner_rank);
+ } else {
+ MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("%d received ping_pong_count %d from %d\n",
+ world_rank, ping_pong_count, partner_rank);
+ }
+}
+```
+这个程序是为2个进程执行而设计的。这两个进程一开始会根据我们写的一个简单的求余算法来确定各自的对手。`ping_pong_count` 一开始被初始化为0,然后每次发送消息之后会递增1。随着 `ping_pong_count` 的递增,两个进程会轮流成为发送者和接受者。最后,当我们设定的 limit 被触发的时候(我的代码里设定为10),进程就停止了发送和接收。程序的输出如下。
+
+```
+>>> ./run.py ping_pong
+0 sent and incremented ping_pong_count 1 to 1
+0 received ping_pong_count 2 from 1
+0 sent and incremented ping_pong_count 3 to 1
+0 received ping_pong_count 4 from 1
+0 sent and incremented ping_pong_count 5 to 1
+0 received ping_pong_count 6 from 1
+0 sent and incremented ping_pong_count 7 to 1
+0 received ping_pong_count 8 from 1
+0 sent and incremented ping_pong_count 9 to 1
+0 received ping_pong_count 10 from 1
+1 received ping_pong_count 1 from 0
+1 sent and incremented ping_pong_count 2 to 0
+1 received ping_pong_count 3 from 0
+1 sent and incremented ping_pong_count 4 to 0
+1 received ping_pong_count 5 from 0
+1 sent and incremented ping_pong_count 6 to 0
+1 received ping_pong_count 7 from 0
+1 sent and incremented ping_pong_count 8 to 0
+1 received ping_pong_count 9 from 0
+1 sent and incremented ping_pong_count 10 to 0
+```
+
+这个程序在其他机器上运行的输出可能会由于进程调度的不同跟上面的不一样。不管怎么样,你可以看到,进程0和进程1在轮流发送和接收 ping_pong_count。
+
+## 环程序
+我还添加了另一个使用 `MPI_Send` 和 `MPI_Recv` 的样例程序,这个程序使用到了多个进程。在这个例子里,一个值会在各个进程之间以一个环的形式传递。代码在 [ring.c]({{ site.github.code }}/tutorials/mpi-send-and-receive/code/ring.c)。主要的部分如下。
+
+```cpp
+int token;
+if (world_rank != 0) {
+ MPI_Recv(&token, 1, MPI_INT, world_rank - 1, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("Process %d received token %d from process %d\n",
+ world_rank, token, world_rank - 1);
+} else {
+ // Set the token's value if you are process 0
+ token = -1;
+}
+MPI_Send(&token, 1, MPI_INT, (world_rank + 1) % world_size,
+ 0, MPI_COMM_WORLD);
+
+// Now process 0 can receive from the last process.
+if (world_rank == 0) {
+ MPI_Recv(&token, 1, MPI_INT, world_size - 1, 0,
+ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ printf("Process %d received token %d from process %d\n",
+ world_rank, token, world_size - 1);
+}
+```
+
+这个环程序在进程0上面初始化了一个值-1,赋值给 token。然后这个值会依次传递给每个进程。程序会在进程0从最后一个进程接收到值之后结束。如你所见,我们的逻辑避免了死锁的发生。具体来说,进程0保证了在想要接受数据之前发送了 token。所有其他的进程只是简单的调用 `MPI_Recv` (从他们的邻居进程接收数据),然后调用 `MPI_Send` (发送数据到他们的邻居进程)把数据从环上传递下去。
+`MPI_Send` 和 `MPI_Recv` 会阻塞直到数据传递完成。因为这个特性,打印出来的数据是跟数据传递的次序一样的。用5个进程的话,输出应该是这样的:
+
+```
+>>> ./run.py ring
+Process 1 received token -1 from process 0
+Process 2 received token -1 from process 1
+Process 3 received token -1 from process 2
+Process 4 received token -1 from process 3
+Process 0 received token -1 from process 4
+```
+
+如你所见,进程0先把-1这个值传递给了进程1。然后数据会在环里一直传递到进程0。
+## 接下来
+
+现在你有了对于 `MPI_Send` 和 `MPI_Recv` 的基础理解,是时候对这些方法进行一些深入研究了。下节课,我会讲解[如何预估和动态地接受信息]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/)。你也可以再去 [MPI tutorials]({{ site.baseurl }}/tutorials/) 首页查看所有其他的教程。
+
+有问题或者感到疑惑?欢迎在下面留言,也许我或者其他的读者可以帮到你。
\ No newline at end of file
diff --git a/tutorials/performing-parallel-rank-with-mpi/code/makefile b/tutorials/performing-parallel-rank-with-mpi/code/makefile
index d5aee62..51d4237 100644
--- a/tutorials/performing-parallel-rank-with-mpi/code/makefile
+++ b/tutorials/performing-parallel-rank-with-mpi/code/makefile
@@ -3,11 +3,11 @@ MPICC?=mpicc
all: ${EXECS}
-mpi_rank.o: mpi_rank.c
- ${MPICC} -c mpi_rank.c
+tmpi_rank.o: tmpi_rank.c
+ ${MPICC} -c tmpi_rank.c
-random_rank: mpi_rank.o random_rank.c
- ${MPICC} -o random_rank random_rank.c mpi_rank.o
+random_rank: tmpi_rank.o random_rank.c
+ ${MPICC} -o random_rank random_rank.c tmpi_rank.o
clean:
rm -f ${EXECS} *.o
diff --git a/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c b/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c
index 526b849..6854536 100644
--- a/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c
+++ b/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c
@@ -2,14 +2,15 @@
// Copyright 2013 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
-// Runs the MPI_Rank function with random input.
+// Runs the TMPI_Rank function with random input.
//
#include
#include
#include
-#include "mpi_rank.h"
+#include "tmpi_rank.h"
+#include
int main(int argc, char** argv) {
MPI_Init(NULL, NULL);
@@ -24,7 +25,7 @@ int main(int argc, char** argv) {
float rand_num = rand() / (float)RAND_MAX;
int rank;
- MPI_Rank(&rand_num, &rank, MPI_FLOAT, MPI_COMM_WORLD);
+ TMPI_Rank(&rand_num, &rank, MPI_FLOAT, MPI_COMM_WORLD);
printf("Rank for %f on process %d - %d\n", rand_num, world_rank, rank);
MPI_Barrier(MPI_COMM_WORLD);
diff --git a/tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.c b/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c
similarity index 93%
rename from tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.c
rename to tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c
index c9b5be5..8ecdb3a 100644
--- a/tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.c
+++ b/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c
@@ -2,7 +2,7 @@
// Copyright 2013 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Code that performs a parallel rank
//
@@ -12,7 +12,7 @@
// Holds the communicator rank of a process along with the corresponding number.
// This struct is used for sorting the values and keeping the owning process information
-// in tact.
+// intact.
typedef struct {
int comm_rank;
union {
@@ -21,7 +21,7 @@ typedef struct {
} number;
} CommRankNumber;
-// Gathers numbers for MPI_Rank to process zero. Allocates enough space given the MPI datatype and
+// Gathers numbers for TMPI_Rank to process zero. Allocates enough space given the MPI datatype and
// returns a void * buffer to process 0. It returns NULL to all other processes.
void *gather_numbers_to_root(void *number, MPI_Datatype datatype, MPI_Comm comm) {
int comm_rank, comm_size;
@@ -77,7 +77,7 @@ int *get_ranks(void *gathered_numbers, int gathered_number_count, MPI_Datatype d
// Convert the gathered number array to an array of CommRankNumbers. This allows us to
// sort the numbers and also keep the information of the processes that own the numbers
- // in tact.
+ // intact.
CommRankNumber *comm_rank_numbers = malloc(gathered_number_count * sizeof(CommRankNumber));
int i;
for (i = 0; i < gathered_number_count; i++) {
@@ -104,9 +104,9 @@ int *get_ranks(void *gathered_numbers, int gathered_number_count, MPI_Datatype d
return ranks;
}
-// Gets the rank of the recv_data, which is of type datatype. The rank is returned
-// in send_data and is of type datatype.
-int MPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm) {
+// Gets the rank of the send_data, which is of type datatype. The rank is returned
+// in recv_data and is of type datatype.
+int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm) {
// Check base cases first - Only support MPI_INT and MPI_FLOAT for this function.
if (datatype != MPI_INT && datatype != MPI_FLOAT) {
return MPI_ERR_TYPE;
diff --git a/tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.h b/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.h
similarity index 72%
rename from tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.h
rename to tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.h
index abe5880..ae0efb3 100644
--- a/tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.h
+++ b/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.h
@@ -2,13 +2,13 @@
// Copyright 2013 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
-// Header file for MPI_Rank
+// Header file for TMPI_Rank
//
#ifndef __PARALLEL_RANK_H
#define __PARALLEL_RANK_H 1
-int MPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm);
+int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm);
#endif
diff --git a/tutorials/performing-parallel-rank-with-mpi/index.md b/tutorials/performing-parallel-rank-with-mpi/index.md
index 264ece1..607a71e 100644
--- a/tutorials/performing-parallel-rank-with-mpi/index.md
+++ b/tutorials/performing-parallel-rank-with-mpi/index.md
@@ -4,15 +4,17 @@ title: Performing Parallel Rank with MPI
author: Wes Kendall
categories: Beginner MPI
tags: MPI_Type_size
+translations: zh_cn,ja_jp
redirect_from: '/performing-parallel-rank-with-mpi/'
---
In the [previous lesson]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/), we went over `MPI_Scatter`, `MPI_Gather`, and `MPI_Allgather`. We are going to expand on basic collectives in this lesson by coding a useful function for your MPI toolkit - parallel rank.
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/performing-parallel-rank-with-mpi/code]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code).
+
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/performing-parallel-rank-with-mpi/code]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code).
## Parallel rank - problem overview
-When processes all have a single number stored in their local memory, it can be useful to know what order their number is in respect to the entire set of numbers contained by all processes. For example, a user might be benchmarking the processors in an MPI cluster and want to know the order of how fast each processor relative to the others. This information can be used for scheduling tasks and so on. As you can imagine, it is rather difficult to find out a number's order in the context of all other numbers if they are spread across processes. This problem - the parallel rank problem - is what we are going to solve in this lesson.
+When processes all have a single number stored in their local memory, it can be useful to know what order their number is in respect to the entire set of numbers contained by all processes. For example, a user might be benchmarking the processors in an MPI cluster and want to know the order of how fast each processor is relative to the others. This information can be used for scheduling tasks and so on. As you can imagine, it is rather difficult to find out a number's order in the context of all other numbers if they are spread across processes. This problem - the parallel rank problem - is what we are going to solve in this lesson.
An illustration of the input and output of parallel rank is below:
@@ -24,24 +26,24 @@ The processes in the illustration (labeled 0 through 3) start with four numbers
Before we dive into solving the parallel rank problem, let's first decide on how our function is going to behave. Our function needs to take a number on each process and return its associated rank with respect to all of the other numbers across all processes. Along with this, we will need other miscellaneous information, such as the communicator that is being used, and the datatype of the number being ranked. Given this function definition, our prototype for the rank function looks like this:
```cpp
-MPI_Rank(
+TMPI_Rank(
void *send_data,
void *recv_data,
MPI_Datatype datatype,
MPI_Comm comm)
```
-`MPI_Rank` takes a `send_data` buffer that contains one number of `datatype` type. The `recv_data` receives exactly one integer on each process that contains the rank value for `send_data`. The `comm` variable is the communicator in which ranking is taking place.
+`TMPI_Rank` takes a `send_data` buffer that contains one number of `datatype` type. The `recv_data` receives exactly one integer on each process that contains the rank value for `send_data`. The `comm` variable is the communicator in which ranking is taking place.
-> **Note** - `MPI_Rank` is not part of the MPI standard. We are just making it look like all of the other MPI functions for consistency.
+> **Note** - The MPI standard explicitly says that users should not name their own functions `MPI_` to avoid confusing user functions with functions in the MPI standard itself. Thus, we will prefix functions in these tutorials with `T`.
## Solving the parallel rank problem
-Now that we have our API definition, we can dive into how the parallel rank problem is solved. The first step in solving the parallel rank problem is ordering all of the numbers across all of the processes. This has to be accomplished so that we can find the rank of each number in the entire set of numbers. There are quite a few ways how we could accomplish this. The easiest way is gathering all of the numbers to one process and sorting the numbers. In the example code ([mpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/mpi_rank.c)), the `gather_numbers_to_root` function is responsible for gathering all of the numbers to the root process.
+Now that we have our API definition, we can dive into how the parallel rank problem is solved. The first step in solving the parallel rank problem is ordering all of the numbers across all of the processes. This has to be accomplished so that we can find the rank of each number in the entire set of numbers. There are quite a few ways how we could accomplish this. The easiest way is gathering all of the numbers to one process and sorting the numbers. In the example code ([tmpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c)), the `gather_numbers_to_root` function is responsible for gathering all of the numbers to the root process.
```cpp
-// Gathers numbers for MPI_Rank to process zero. Allocates space for
-// the MPI datatype and returns a void * buffer to process 0.
+// Gathers numbers for TMPI_Rank to process zero. Allocates space for
+// the MPI datatype and returns a void * buffer to process 0.
// It returns NULL to all other processes.
void *gather_numbers_to_root(void *number, MPI_Datatype datatype,
MPI_Comm comm) {
@@ -74,10 +76,10 @@ Sorting numbers is not necessarily a difficult problem in our ranking function.
In order to facilitate attaching the owning process to the numbers, we create a struct in the code that holds this information. Our struct definition is as follows:
```cpp
-// Holds the communicator rank of a process along with the
+// Holds the communicator rank of a process along with the
// corresponding number. This struct is used for sorting
// the values and keeping the owning process information
-// in tact.
+// intact.
typedef struct {
int comm_rank;
union {
@@ -102,13 +104,13 @@ int *get_ranks(void *gathered_numbers, int gathered_number_count,
// Convert the gathered number array to an array of CommRankNumbers.
// This allows us to sort the numbers and also keep the information
- // of the processes that own the numbers in tact.
+ // of the processes that own the numbers intact.
CommRankNumber *comm_rank_numbers = malloc(
gathered_number_count * sizeof(CommRankNumber));
int i;
for (i = 0; i < gathered_number_count; i++) {
comm_rank_numbers[i].comm_rank = i;
- memcpy(&(comm_rank_numbers[i].number),
+ memcpy(&(comm_rank_numbers[i].number),
gathered_numbers + (i * datatype_size),
datatype_size);
}
@@ -136,19 +138,19 @@ int *get_ranks(void *gathered_numbers, int gathered_number_count,
}
```
-The `get_ranks` function first creates an array of `CommRankNumber` structs and attaches the communicator rank of the process that owns the number. If the datatype is `MPI_FLOAT`, `qsort` is called with a special sorting function for our array of structs (see [mpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code) for the code). Likewise, we use a different sorting function if the datatype is `MPI_INT`.
+The `get_ranks` function first creates an array of `CommRankNumber` structs and attaches the communicator rank of the process that owns the number. If the datatype is `MPI_FLOAT`, `qsort` is called with a special sorting function for our array of structs (see [tmpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c) for the code). Likewise, we use a different sorting function if the datatype is `MPI_INT`.
After the numbers are sorted, we must create an array of ranks in the proper order so that they can be scattered back to the requesting processes. This is accomplished by making the `ranks` array and filling in the proper rank values for each of the sorted `CommRankNumber` structs.
## Putting it all together
-Now that we have our two primary functions, we can put them all together into our `MPI_Rank` function. This function gathers the numbers to the root process, sorts the numbers to determine their ranks, and then scatters the ranks back to the requesting processes. The code is shown below:
+Now that we have our two primary functions, we can put them all together into our `TMPI_Rank` function. This function gathers the numbers to the root process, sorts the numbers to determine their ranks, and then scatters the ranks back to the requesting processes. The code is shown below:
```cpp
-// Gets the rank of the recv_data, which is of type datatype. The rank
-// is returned in send_data and is of type datatype.
-int MPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
+// Gets the rank of the send_data, which is of type datatype. The rank
+// is returned in recv_data and is of type datatype.
+int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
MPI_Comm comm) {
- // Check base cases first - Only support MPI_INT and MPI_FLOAT for
+ // Check base cases first - Only support MPI_INT and MPI_FLOAT for
// this function.
if (datatype != MPI_INT && datatype != MPI_FLOAT) {
return MPI_ERR_TYPE;
@@ -158,7 +160,7 @@ int MPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
MPI_Comm_size(comm, &comm_size);
MPI_Comm_rank(comm, &comm_rank);
- // To calculate the rank, we must gather the numbers to one
+ // To calculate the rank, we must gather the numbers to one
// process, sort the numbers, and then scatter the resulting rank
// values. Start by gathering the numbers on process 0 of comm.
void *gathered_numbers = gather_numbers_to_root(send_data, datatype,
@@ -181,7 +183,7 @@ int MPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
}
```
-The `MPI_Rank` function uses the two functions we just created, `gather_numbers_to_root` and `get_ranks`, to get the ranks of the numbers. The function then performs the final `MPI_Scatter` to scatter the resulting ranks back to the processes.
+The `TMPI_Rank` function uses the two functions we just created, `gather_numbers_to_root` and `get_ranks`, to get the ranks of the numbers. The function then performs the final `MPI_Scatter` to scatter the resulting ranks back to the processes.
If you have had trouble following the solution to the parallel rank problem, I have included an illustration of the entire data flow of our problem using an example set of data:
@@ -192,7 +194,7 @@ Have any questions about how the parallel rank algorithm works? Leave them below
## Running our parallel rank algorithm
I have included a small program in the example code to help test out our parallel rank algorithm. The code can be viewed in the [random_rank.c file]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c) file in the [lesson code]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code).
-The example application simply creates a random number on each process and calls `MPI_Rank` to get the rank of each number. If you run the random_rank program from the *tutorials* directory of the [repo]({{ site.github.code }}), the output should look similar to this.
+The example application simply creates a random number on each process and calls `TMPI_Rank` to get the rank of each number. If you run the random_rank program from the *tutorials* directory of the [repo]({{ site.github.code }}), the output should look similar to this.
```
>>> cd tutorials
@@ -207,4 +209,4 @@ Rank for 0.684195 on process 3 - 1
## Up next
In our next lesson, we start covering advanced collective communication. The next lesson is about [using MPI_Reduce and MPI_Allreduce to perform number reduction]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/).
-For all beginner lessons, go the the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/).
\ No newline at end of file
+For all lessons, go the the [MPI tutorials]({{ site.baseurl }}/tutorials/).
diff --git a/tutorials/performing-parallel-rank-with-mpi/ja_jp.md b/tutorials/performing-parallel-rank-with-mpi/ja_jp.md
new file mode 100644
index 0000000..5ed944d
--- /dev/null
+++ b/tutorials/performing-parallel-rank-with-mpi/ja_jp.md
@@ -0,0 +1,199 @@
+---
+layout: post
+title: 並列ランク問題 - Performing Parallel Rank with MPI
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Type_size
+translations: zh_cn
+redirect_from: '/performing-parallel-rank-with-mpi/'
+---
+
+[前のレッスン]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/)では
+ `MPI_Scatter`, `MPI_Gather`, `MPI_Allgather`を紹介しました。このレッスンでは、MPIの便利なルーチンである並列ランク(parallel rank)を実装して集団通信への理解を深めていきましょう。
+
+> **Note** - 全てのコードは[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは [tutorials/performing-parallel-rank-with-mpi/code]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code)にあります。
+
+## 問題概要 - Parallel rank - problem overview
+すべてのプロセスがローカル メモリにただ1つの数値を持っています。各プロセスは自分の持っている数値が全てのプロセスの中で何番目に小さい値を持っているのか知りたいです。このシナリオは、ユーザーがMPIクラスター内のプロセッサのベンチマークを行っており自分のプロセッサが他のプロセッサと比較してどのくらい高速であるかを知りたいケースなどに利用されます。他にもタスクのスケジュール設定などに使用できるでしょう。数値がプロセス全体に分散していると全ての数値の中での順序を見つけるのは難しいように思えます。この問題を並列ランク問題と呼び、このレッスンで解決しましょう。
+
+具体例を図で示します。
+
+
+
+4つのプロセスがあり、0から3のラベルでそれぞれを区別します。順番に5、2、7、4の4つの数字を持っているとしましょう。並列ランクアルゴリズムを実行すると全体での順番がわかるのでそれぞれは2,0,3,1の4つの数字を持つことになります。(訳注:0-indexedです)
+
+## 並列ランクAPI定義
+これを実現するためにがなにが必要かを考えましょう。すべての数値を集めること、その数値の順番を返す必要があります。さらに使用されているコミュニケータやランク付けされる数値のデータ型なども必要になります。ランク関数のプロトタイプを次のようにしましょう。
+
+```cpp
+TMPI_Rank(
+ void *send_data,
+ void *recv_data,
+ MPI_Datatype datatype,
+ MPI_Comm comm)
+```
+
+`TMPI_Rank`は、データ型の数値を1つ含む`send_data`バッファを受け取ります。`recv_data`は、`send_data`の各プロセスの順位を受け取る変数です。`comm`変数はプロセスのコミュニケータです。
+
+> **Note** -
+> MPI標準では、ユーザー関数と MPI関数が混同しないように、MPI関数には`MPI_`という名前づけがされています。したがって、このレッスンではさらに`T`を追加しています。
+
+## 並列ランク問題の解き方 - Solving the parallel rank problem
+API定義ができたので、どのように解くかを考えましょう。並列ランク問題を解決するためには全てのプロセスから集めた数値のソートが必要です。このソートは各数字の順番を知るために必要で、いくつかのアプローチで実現できます。最も簡単な方法は各プロセスの数字を1つのプロセスに集め、そのプロセスでソートすれば良いでしょう。サンプル コード ([tmpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c)) では`gather_numbers_to_root`関数はすべての数字をルート プロセスに集める役割を担います。
+
+```cpp
+// ルートプロセスに数値を集めます。
+// ルートプロセスは結果の(ランクの)配列を返します。他のプロセスにはNULLを返します。
+void *gather_numbers_to_root(void *number, MPI_Datatype datatype,
+ MPI_Comm comm) {
+ int comm_rank, comm_size;
+ MPI_Comm_rank(comm, &comm_rank);
+ MPI_Comm_size(comm, &comm_size);
+
+ // ルートプロセスでは必要なバッファを用意します。データ型のサイズを配慮します
+ int datatype_size;
+ MPI_Type_size(datatype, &datatype_size);
+ void *gathered_numbers;
+ if (comm_rank == 0) {
+ gathered_numbers = malloc(datatype_size * comm_size);
+ }
+
+ // 全ての数値をルートプロセスに集めます
+ MPI_Gather(number, 1, datatype, gathered_numbers, 1,
+ datatype, 0, comm);
+
+ return gathered_numbers;
+}
+```
+
+`gather_numbers_to_root`関数はルートに送りたい数値(`send_data`変数)と数値の`datatype`および`comm`コミュニケータを引数で受け取ります。ルート・プロセスは`comm_size`の数値を受け取ればいいとわかるので`datatype_size * comm_size`の長さの配列をメモリ確保します。`datatype_size`変数は、このチュートリアルで初めて使う`MPI_Type_size`を使用して収集されます。私たちの今回のプログラムは`MPI_INT`と`MPI_FLOAT`のみを使用しますが、この関数を使うと様々なサイズのデータ型をサポートするように拡張することができます。これで数値が集まったので`MPI_Gather`でルートプロセスに数を集めます。次はルートプロセスで数をソートし、ランクを決定します。
+
+## ソートとその所有権の維持 - Sorting numbers and maintaining ownership
+ソートの処理自身は難しくありません。C標準ライブラリには`qsort` などの一般的なソートアルゴリズムが用意されています。ただし、並列ランク問題ソートでは数字を送信したプロセスのランクをルートプロセスのソートの間で維持する必要あることに注意します。ルートプロセス対して数字だけでは数字の順位をどのプロセスに返せばいいかわからないのです。
+
+数字とそれに対応する送信元のプロセスのランク(つまり所有権)を保持する構造体を定義しましょう。
+
+```cpp
+// 値とコミュニケータランクを保持する
+// この構造体はソートをされてもランクの情報を失わない
+typedef struct {
+ int comm_rank;
+ union {
+ float f;
+ int i;
+ } number;
+} CommRankNumber;
+```
+
+構造体`CommRankNumber`はソートする数値を保持し、対応するプロセスのコミュニケータランクを保持します。ここで数値はfloatまたはintの可能性があるので、unionで両方を保持できるようにしていることに注意してください。次にこの構造体をソートする`get_ranks`関数を考えていきましょう。
+
+```cpp
+// この関数はルートプロセスに値を集めて、その順位を元のプロセスに返します
+// この関数はルートでのみ実行されます。
+int *get_ranks(void *gathered_numbers, int gathered_number_count,
+ MPI_Datatype datatype) {
+ int datatype_size;
+ MPI_Type_size(datatype, &datatype_size);
+
+ // 集めた値をCommRankNumbersに保持します。
+ // これによりソートしてもコミュニケータランクを失いません。
+ CommRankNumber *comm_rank_numbers = malloc(
+ gathered_number_count * sizeof(CommRankNumber));
+ int i;
+ for (i = 0; i < gathered_number_count; i++) {
+ comm_rank_numbers[i].comm_rank = i;
+ memcpy(&(comm_rank_numbers[i].number),
+ gathered_numbers + (i * datatype_size),
+ datatype_size);
+ }
+
+ // ランクの情報を持ったまま値をソートする
+ if (datatype == MPI_FLOAT) {
+ qsort(comm_rank_numbers, gathered_number_count,
+ sizeof(CommRankNumber), &compare_float_comm_rank_number);
+ } else {
+ qsort(comm_rank_numbers, gathered_number_count,
+ sizeof(CommRankNumber), &compare_int_comm_rank_number);
+ }
+
+ // ソートが完了したので値を戻すための結果の配列を用意する。iにはプロセスiの順位が含まれる。
+ int *ranks = (int *)malloc(sizeof(int) * gathered_number_count);
+ for (i = 0; i < gathered_number_count; i++) {
+ ranks[comm_rank_numbers[i].comm_rank] = i;
+ }
+
+ // comm_rank_numbersを解放する
+ free(comm_rank_numbers);
+ return ranks;
+}
+```
+`get_ranks`は`CommRankNumber`構造体の配列を作成して、各プロセスのランクを設定します。`MPI_FLOAT`と`MPI_INT`である場合で違ったsort関数の呼び出しを行います。 ([tmpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c)を参照してください)。
+
+ソートしたら順番を格納した配列を作成して各々のプロセスに正しく戻せるようにする必要があります。ソートされた`comm_rank_numbers`を順に見ていき、戻すための配列`ranks`を作ります。
+
+## 全てを一緒にする - Putting it all together
+さて、2つの重要なルーチンが実装できたのでこれらをまとめて `TMPI_Rank` を作りましょう。ルートプロセスに各プロセスの数字を集めてから、計算した順位を各プロセスに戻します。このコードを以下に示します。
+```cpp
+// datatype型のsend_dataの順位を取得する。
+// ランクはrecv_dataで戻ってくるdatatype型である。
+int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
+ MPI_Comm comm) {
+ // datatypeがMPI_INTかMPI_FLOATであるかを確認します
+ if (datatype != MPI_INT && datatype != MPI_FLOAT) {
+ return MPI_ERR_TYPE;
+ }
+
+ int comm_size, comm_rank;
+ MPI_Comm_size(comm, &comm_size);
+ MPI_Comm_rank(comm, &comm_rank);
+
+ // 順位を計算するために値をルートプロセスに集めます。
+ // そして、ソートして結果を求め、Scatterで順位情報を戻します。
+ // ルートプロセスにはプロセス0を使います
+ void *gathered_numbers = gather_numbers_to_root(send_data, datatype,
+ comm);
+
+ // 各プロセスの順位を計算する
+ int *ranks = NULL;
+ if (comm_rank == 0) {
+ ranks = get_ranks(gathered_numbers, comm_size, datatype);
+ }
+
+ // Scatterする
+ MPI_Scatter(ranks, 1, MPI_INT, recv_data, 1, MPI_INT, 0, comm);
+
+ // clean up
+ if (comm_rank == 0) {
+ free(gathered_numbers);
+ free(ranks);
+ }
+}
+```
+
+TMPI_Rank関数は`gather_numbers_to_root`と`get_ranks`の2つの関数を使用して各の順位を計算します。この関数は`MPI_Scatter`によって各プロセスに順位を戻します。
+
+全体のデータフローを図解します。
+
+
+
+もし、これに関する質問があれば以下を参照して質問してください。
+
+## 実行 - Running our parallel rank algorithm
+このアルゴリズムのサンプルプログラムがあります。[レッスンコード]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code)の[random_rank.c file]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c)で確認できます。
+
+このサンプルプログラムは各プロセスで乱数を生成した後、各順位を取得するために`TMPI_Rank`の呼び出しします。[レポジトリ]({{ site.github.code }})のチュートリアルディレクトリから random_rank プログラムを実行すると、出力は次のようになります。
+
+```
+>>> cd tutorials
+>>> ./run.py random_rank
+mpirun -n 4 ./random_rank 100
+Rank for 0.242578 on process 0 - 0
+Rank for 0.894732 on process 1 - 3
+Rank for 0.789463 on process 2 - 2
+Rank for 0.684195 on process 3 - 1
+```
+
+## Up next
+次はさらに高度な集団通信について説明します。次のレッスンは、[using MPI_Reduce and MPI_Allreduce to perform number reduction]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/)です。
+
+[MPI tutorials]({{ site.baseurl }}/tutorials/) に全てのレッスンの目次があります。
\ No newline at end of file
diff --git a/tutorials/performing-parallel-rank-with-mpi/zh_cn.md b/tutorials/performing-parallel-rank-with-mpi/zh_cn.md
new file mode 100644
index 0000000..4145f9d
--- /dev/null
+++ b/tutorials/performing-parallel-rank-with-mpi/zh_cn.md
@@ -0,0 +1,210 @@
+---
+layout: post
+title: 使用MPI计算并行排名
+author: Wes Kendall
+categories: Beginner MPI
+tags: MPI_Type_size
+redirect_from: '/performing-parallel-rank-with-mpi/zh_cn'
+---
+
+在[之前的课程]({{ site.baseurl }}/tutorials/mpi-scatter-gather-and-allgather/)里,我们介绍了 `MPI_Scatter`、`MPI_Gather` 和 `MPI_Allgather`。在这一节中,我们将通过为MPI工具包编写一个实用的功能——并行排名来拓展基本的集体通信(collective communication)。
+
+> **注意** - 这个网站的提到的所有代码都在 [GitHub]({{ site.github.repo }}) 上面。这篇教程的代码在 [tutorials/performing-parallel-rank-with-mpi/code]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code)。
+
+## 并行排名 - 问题概述
+当每一个进程都在其本地内存中存储了一个数,所有进程中存储的数字构成了一个数字集合(set of numbers),了解该数相对于整个数字集合的顺序是有用的。例如,用户可能正在对MPI群集中的处理器进行基准测试,并想知道每个处理器相对于其他处理器有多快。这个信息可用于安排、调度任务等。可以想象,如果所有其他数字分散在各个进程中,那么很难找出一个数字相对于所有其他数字的顺序。这个并行排名问题是我们在本课中要解决的问题。
+
+下图说明了并行排名的输入和输出:
+
+
+
+图示中的进程(标记为0到3)开始时有四个数字—— 5、2、7和4。然后,并行排名算法算出进程1在数字集合中的排名为0(即第一个数字),进程3排名为1,进程0排名为2,进程2排在整个数字集合的最后。很简单,对吧?
+
+## 并行排名API定义
+在深入研究并行排名问题之前,让我们首先确定函数的行为方式。我们的函数需要在每个进程上取一个数字,并返回其相对于所有其他进程中的数字的排名。与此同时,我们将需要其他各种信息,例如正在使用的通讯器(communicator)以及被排名的数字的数据类型。 给定这个函数定义后,我们的排名函数原型如下所示:
+
+ ```cpp
+TMPI_Rank(
+ void *send_data,
+ void *recv_data,
+ MPI_Datatype datatype,
+ MPI_Comm comm)
+```
+
+`TMPI_Rank` 把 `send_data` 作为缓冲区,其中包含一个类型为 `datatype` 的数字。
+`recv_data` 在每个进程中只接收一个整数,即 `send_data` 的排名。`comm` 变量是进行排名的通讯器。
+
+> **注意** - MPI标准明确指出,用户不应以 `MPI` 起头命名自己的函数,如 `MPI_`,以避免将用户函数与MPI标准本身的函数混淆。 因此,在这些教程中,我们将在函数前面加上 `T`。
+
+## 解决并行排名问题
+现在我们有了API定义,我们可以深入研究如何解决并行排名问题。解决并行排名问题的第一步是对所有进程中的数字进行排序。 这一点必须做到,以便我们找到整个数字集中每个数字的排名。我们可以通过多种方式来实现这一目标。最简单的方法是将所有数字收集到一个进程中并对数字进行排序。在示例代码([tmpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c))中,`gather_numbers_to_root` 函数负责将所有数字收集到根进程(root process)。
+
+```cpp
+// 为进程0的TMPI_Rank收集数字。为MPI的数据类型分配空间
+// 对进程0返回 void * 指向的缓冲区
+// 对所有其他进程返回NULL
+void *gather_numbers_to_root(void *number, MPI_Datatype datatype,
+ MPI_Comm comm) {
+ int comm_rank, comm_size;
+ MPI_Comm_rank(comm, &comm_rank);
+ MPI_Comm_size(comm, &comm_size);
+
+ // 在根进程上分配一个数组
+ // 数组大小取决于所用的MPI数据类型
+ int datatype_size;
+ MPI_Type_size(datatype, &datatype_size);
+ void *gathered_numbers;
+ if (comm_rank == 0) {
+ gathered_numbers = malloc(datatype_size * comm_size);
+ }
+
+ // 在根进程上收集所有数字
+ MPI_Gather(number, 1, datatype, gathered_numbers, 1,
+ datatype, 0, comm);
+
+ return gathered_numbers;
+}
+```
+
+`gather_numbers_to_root` 函数获取要收集的数字(即 `send_data` 变量)、数字的数据类型 `datatype` 和 `comm` 通讯器。根进程必须在此函数中收集 `comm_size` 个数字,因此它会分配 `datatype_size * comm_size` 长度的数组。在本教程中,通过使用新的MPI函数- `MPI_Type_size` 来收集`datatype_size`变量。尽管我们的代码仅支持将 `MPI_INT` 和 `MPI_FLOAT` 作为数据类型,但可将其扩展以支持不同大小的数据类型。 在使用 `MPI_Gather` 在根进程上收集了数字之后,必须在根进程上对数字进行排序,以确定它们排名。
+
+## 排序数字并维护所属
+在我们的排名函数中,排序数字不一定是难题。 C标准库为我们提供了流行的排序算法,例如 `qsort`。 在并行排名问题中,排序的困难在于,我们必须维护各个进程将数字发送到根进程的次序。 如果我们要对收集到根进程的数组进行排序而不给数字附加信息,则根进程将不知道如何将数字的排名发送回原来请求的进程!
+
+为了便于将所属进程附到对应数字上,我们在代码中创建了一个结构体(struct)来保存此信息。 我们的结构定义如下:
+
+```cpp
+// 保存进程在通讯器中的次序(rank)和对应数字
+// 该结构体用于数组排序,
+// 并同时完整保留所属进程信息
+
+typedef struct {
+ int comm_rank;
+ union {
+ float f;
+ int i;
+ } number;
+} CommRankNumber;
+```
+
+`CommRankNumber` 结构体保存了我们要排序的数字(记住它可以是浮点数或整数,因此我们使用联合体union),并且它拥有该数字所属进程在通讯器中的次序(rank)。 代码的下一部分,即 `get_ranks` 函数,负责创建这些结构体并对它们进行排序。
+
+
+```cpp
+// 这个函数在根进程上对收集到的数字排序
+// 返回一个数组,数组按进程在通讯器中的次序排序
+// 注意 - 该函数只在根进程上运行
+
+int *get_ranks(void *gathered_numbers, int gathered_number_count,
+ MPI_Datatype datatype) {
+ int datatype_size;
+ MPI_Type_size(datatype, &datatype_size);
+
+ // 将收集到的数字数组转换为CommRankNumbers数组
+ // 这允许我们在排序的同时,完整保留数字所属进程的信息
+
+ CommRankNumber *comm_rank_numbers = malloc(
+ gathered_number_count * sizeof(CommRankNumber));
+ int i;
+ for (i = 0; i < gathered_number_count; i++) {
+ comm_rank_numbers[i].comm_rank = i;
+ memcpy(&(comm_rank_numbers[i].number),
+ gathered_numbers + (i * datatype_size),
+ datatype_size);
+ }
+
+ // 根据数据类型对comm_rank_numbers排序
+ if (datatype == MPI_FLOAT) {
+ qsort(comm_rank_numbers, gathered_number_count,
+ sizeof(CommRankNumber), &compare_float_comm_rank_number);
+ } else {
+ qsort(comm_rank_numbers, gathered_number_count,
+ sizeof(CommRankNumber), &compare_int_comm_rank_number);
+ }
+
+ // 现在comm_rank_numbers是排好序的,下面生成一个数组,
+ // 包含每个进程的排名,数组第i个元素是进程i的数字的排名
+
+ int *ranks = (int *)malloc(sizeof(int) * gathered_number_count);
+ for (i = 0; i < gathered_number_count; i++) {
+ ranks[comm_rank_numbers[i].comm_rank] = i;
+ }
+
+ // 清理并返回排名数组
+ free(comm_rank_numbers);
+ return ranks;
+}
+```
+
+`get_ranks` 函数首先创建一个CommRankNumber结构体数组,并附上该数字所属进程在通讯器中的次序。 如果数据类型为 `MPI_FLOAT` ,则对我们的结构体数组调用 `qsort` 时,会使用特殊的排序函数,(代码见[tmpi_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/tmpi_rank.c)。 类似的,如果数据类型为 `MPI_INT` ,我们将使用不同的排序函数。
+
+在对数字进行排序之后,我们必须以适当的顺序创建一个排名数组(array of ranks),以便将它们分散(scatter)回到请求的进程中。这是通过创建 `ranks` 数组并为每个已排序的 `CommRankNumber` 结构体填充适当的排名来实现的。
+
+## 整合
+现在我们有了两个主要函数,我们可以将它们全部整合到我们的 `TMPI_Rank` 函数中。此函数将数字收集到根进程,并对数字进行排序以确定其排名,然后将排名分散回请求的进程。 代码如下所示:
+
+```cpp
+// 获取send_data的排名, 类型为datatype
+// 排名用recv_data返回,类型为datatype
+int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
+ MPI_Comm comm) {
+ // 首先检查基本情况 - 此函数只支持MPI_INT和MPI_FLOAT
+
+ if (datatype != MPI_INT && datatype != MPI_FLOAT) {
+ return MPI_ERR_TYPE;
+ }
+
+ int comm_size, comm_rank;
+ MPI_Comm_size(comm, &comm_size);
+ MPI_Comm_rank(comm, &comm_rank);
+
+ // 为了计算排名,必须将数字收集到一个进程中
+ // 对数字排序, 然后将排名结果分散传回
+ // 首先在进程0上收集数字
+ void *gathered_numbers = gather_numbers_to_root(send_data, datatype,
+ comm);
+
+ // 获取每个进程的次序(rank)
+ int *ranks = NULL;
+ if (comm_rank == 0) {
+ ranks = get_ranks(gathered_numbers, comm_size, datatype);
+ }
+
+ // 分散发回排名结果
+ MPI_Scatter(ranks, 1, MPI_INT, recv_data, 1, MPI_INT, 0, comm);
+
+ // 清理
+ if (comm_rank == 0) {
+ free(gathered_numbers);
+ free(ranks);
+ }
+}
+```
+
+`TMPI_Rank` 函数使用我们刚刚创建的两个函数 `gather_numbers_to_root` 和 `get_ranks` 来获取数字的排名。然后,函数执行最后的 `MPI_Scatter`,以将所得的排名分散传回进程。
+
+如果你在阅读并行排名问题的解决方案时遇到麻烦,那么我将使用一组示例数据对问题的整个数据流进行了说明:
+
+
+
+对并行秩算法如何工作有疑问吗? 把它们留在下面!
+
+## 运行我们的并行排名算法
+我已在示例代码中包含了一个小程序,以帮助测试我们的并行排名算法。代码见[课程代码]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code)中的[random_rank.c]({{ site.github.code }}/tutorials/performing-parallel-rank-with-mpi/code/random_rank.c)文件。
+
+该示例应用程序只是在每个进程上创建一个随机数,然后调用 `TMPI_Rank` 以获取每个数的排名。如果从[代码库]({{ site.github.code }})的 *tutorials* 目录中运行 `random_rank` 程序,则输出应与此类似。
+
+```
+>>> cd tutorials
+>>> ./run.py random_rank
+mpirun -n 4 ./random_rank 100
+Rank for 0.242578 on process 0 - 0
+Rank for 0.894732 on process 1 - 3
+Rank for 0.789463 on process 2 - 2
+Rank for 0.684195 on process 3 - 1
+```
+
+## 接下来
+在下一节中,我们将开始介绍进阶的集体通信。 下一节课是关于[用 MPI_Reduce 和 MPI_Allreduce 对数字执行reduce]({{ site.baseurl }}/tutorials/mpi-reduce-and-allreduce/)。
+
+For all lessons, go the the [MPI tutorials]({{ site.baseurl }}/tutorials/).
diff --git a/tutorials/point-to-point-communication-application-random-walk/code/random_walk.cc b/tutorials/point-to-point-communication-application-random-walk/code/random_walk.cc
index c32584b..1794ec3 100644
--- a/tutorials/point-to-point-communication-application-random-walk/code/random_walk.cc
+++ b/tutorials/point-to-point-communication-application-random-walk/code/random_walk.cc
@@ -2,7 +2,7 @@
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
-// either provide a link to www.mpitutorial.com or keep this header in tact.
+// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Example application of random walking using MPI_Send, MPI_Recv, and
// MPI_Probe.
@@ -37,7 +37,7 @@ void decompose_domain(int domain_size, int world_rank,
}
void initialize_walkers(int num_walkers_per_proc, int max_walk_size,
- int subdomain_start, int subdomain_size,
+ int subdomain_start,
vector* incoming_walkers) {
Walker walker;
for (int i = 0; i < num_walkers_per_proc; i++) {
@@ -126,7 +126,7 @@ int main(int argc, char** argv) {
&subdomain_start, &subdomain_size);
// Initialize walkers in your subdomain
initialize_walkers(num_walkers_per_proc, max_walk_size, subdomain_start,
- subdomain_size, &incoming_walkers);
+ &incoming_walkers);
cout << "Process " << world_rank << " initiated " << num_walkers_per_proc
<< " walkers in subdomain " << subdomain_start << " - "
diff --git a/tutorials/point-to-point-communication-application-random-walk/index.md b/tutorials/point-to-point-communication-application-random-walk/index.md
index c0794aa..371da4f 100644
--- a/tutorials/point-to-point-communication-application-random-walk/index.md
+++ b/tutorials/point-to-point-communication-application-random-walk/index.md
@@ -4,14 +4,15 @@ title: Point-to-Point Communication Application - Random Walk
author: Wes Kendall
categories: Beginner MPI
tags:
+translations: zh_cn,ja_jp
redirect_from: '/point-to-point-communication-application-random-walk/'
---
It's time to go through an application example using some of the concepts introduced in the [sending and receiving tutorial]({{ site.baseurl }}/tutorials/mpi-send-and-receive/) and the [MPI_Probe and MPI_Status lesson]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/). The application simulates a process which I refer to as "random walking."
-> **Note** - All of the code for this site is on [Gitub]({{ site.github.repo }}). This tutorial's code is under [tutorials/point-to-point-communication-application-random-walk/code]({{ site.github.code }}/tutorials/point-to-point-communication-application-random-walk/code).
+> **Note** - All of the code for this site is on [GitHub]({{ site.github.repo }}). This tutorial's code is under [tutorials/point-to-point-communication-application-random-walk/code]({{ site.github.code }}/tutorials/point-to-point-communication-application-random-walk/code).
-The basic problem definition of a random walk is as follows. Given a *Min*, *Max*, and random walker *W*, make walker *W* take *S* random walks of arbitrary length to the right. If the process goes out of bounds, it wraps back around. *S* can only move one unit to the right or left at a time.
+The basic problem definition of a random walk is as follows. Given a *Min*, *Max*, and random walker *W*, make walker *W* take *S* random walks of arbitrary length to the right. If the process goes out of bounds, it wraps back around. *W* can only move one unit to the right or left at a time.

@@ -281,7 +282,7 @@ The output continues until processes finish all sending and receiving of all wal
## So what's next?
If you have made it through this entire application and feel comfortable, then good! This application is quite advanced for a first real application.
-Next, we will start learning about *collective* communication in MPI. We will start off by going over [MPI Broadcast]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/). For all beginner lessons, go to the [beginner MPI tutorial]({{ site.baseurl }}/beginner-mpi-tutorial/).
+Next, we will start learning about *collective* communication in MPI. We will start off by going over [MPI Broadcast]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/). For all lessons, go to the [MPI tutorials]({{ site.baseurl }}/tutorials/).
Also, at the beginning, I told you that the concepts of this program are applicable to many parallel programs. I don't want to leave you hanging, so I have included some additional reading material below for anyone that wishes to learn more. Enjoy :-)
@@ -300,4 +301,4 @@ In this illustration, we see that the domain is split among six process. Particl
The parallel particle tracing problem can be solved with `MPI_Send`, `MPI_Recv`, and `MPI_Probe` in a similar manner to our application that we just coded. There are, however, much more sophisticated MPI routines that can get the job done more efficiently. We will talk about these in the coming lessons :-)
-I hope you can now see at least one example of how the random walk problem is similar to other parallel applications!
\ No newline at end of file
+I hope you can now see at least one example of how the random walk problem is similar to other parallel applications!
diff --git a/tutorials/point-to-point-communication-application-random-walk/ja_jp.md b/tutorials/point-to-point-communication-application-random-walk/ja_jp.md
new file mode 100644
index 0000000..61bd01e
--- /dev/null
+++ b/tutorials/point-to-point-communication-application-random-walk/ja_jp.md
@@ -0,0 +1,302 @@
+---
+layout: post
+title: ポイントツーポイント通信アプリケーション ランダムウォーク - Point-to-Point Communication Application - Random Walk
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/point-to-point-communication-application-random-walk/'
+---
+
+[sending and receiving tutorial]({{ site.baseurl }}/tutorials/mpi-send-and-receive/)と[MPI_Probe and MPI_Status lesson]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/) の レッスンで紹介された概念のいくつかを使用して実際のアプリケーションを作成しましょう。これは「ランダムウォーク」と呼ばれるシミュレートションです。
+
+
+> **Note** - このサイトのコードはすべて[GitHub]({{ site.github.repo }})にあります。このレッスンのコードは[tutorials/point-to-point-communication-application-random-walk/code]({{ site.github.code }}/tutorials/point-to-point-communication-application-random-walk/code)にあります。
+
+ランダムウォークの問題設定を説明します。この問題ではある数直線の*Min*、*Max*とおよびランダムウォーカー*W*が与えられます。ウォーカー*W*は適当なの長さのランダムウォークを右に*S*回実行します。右端に到達したら、プロセスは左端に戻ります(訳注: If the process goes out of bounds, it wraps back around. で折り返すとあるが、スタートに戻る実装です)。*W*は一度に右か1ユニットしか移動できません(訳注: to the right or left at a timeとありますが、右にだけ動く実装です)。
+
+
+
+この動作はとても基本的ですが、ランダムウォークの並列化というのはさまざまな並列アプリケーションのシミュレートで用いられる手法です。このことは最後に詳しく説明します。まずは、ランダムウォークの問題を並列化する方法を考えます。
+
+## ランダムウォーキング問題の並列化 - Parallelization of the random walking problem
+多くの並列プログラミングにとって最初に考えなければいけないのは、各プロセスがが持つ領域の分割です。ランダムウォークは*Max - Min + 1* の大きさの1次元の領域を考えます(*Max*と*Min*は含まれて良い値です)。ウォーカーのステップを整数とて考えると、各領域をほぼ等しいサイズに分割することができるでしょう。例えば*Min*が0、*Max*が20で、分割するプロセスが4つの場合はドメインを次のように分割できます。
+
+
+
+最初の3プロセス(プロセス0,1,2)は5つのユニットを管理します。最後のプロセス3は合計6つのユニットを管理します。これは、同じように分割された5つのユニットに加えて残りの1つのユニットです。ドメインを分割したらアプリケーションはウォーカーを初期化します。ウォーカーはランダムなウォークサイズでS回のウォークを実行します。今説明した分割においてウォーカーがプロセス0からサイズ6のウォークを実行すると次のようになります。(訳注:サイズ6のウォークとは、6ユニット移動する動作です)
+
+1. プロセス0からウォーカーは6のステップします。値が4になった時プロセス0の境界に達しています。プロセス0はプロセス1にウォーカーを渡します。
+
+2. プロセス1はウォーカーを受け取ったあと、合計が6に達するまでウォークを続けます。このサイクルを1つとしてウォーカーは新しいランダムウォークを試行します。
+
+
+
+*W*はプロセス0からプロセス1に1回だけ移動しましたが*W*がさらに長い距離を移動する時はドメイン内のより多くのプロセスを通過する必要がある可能性があります。
+
+## CMPI_Send と MPI_Recv を使用したコーディング - oding the application using MPI_Send and MPI_Recv
+この動作を`MPI_Send` と`MPI_Recv`を使用して書いていきましょう。まず、前にプログラムの動きを整理します。
+
+* プロセスはドメイン内の自分の領域を識別します
+* 各プロセスは*N*個のウォーカーを初期化します。これらはすべてそのプロセスの最初の値から開始されます。(訳注: 5-9を管理するプロセスであれば、このプロセスに割り当てられた全てのウォーカーは5にいることにします)
+* 各ウォーカーはオブジェクトです。ウォーカーの現在位置と残歩数という 2 つのintを持ちます。
+* ウォーカーたちはドメイン内でウォークを開始し、ウォークを完了するまで他のプロセスへの移動を行います。
+* すべてのウォーカーが終了するとそのターンは終了します。
+
+まずはプロセスが領域を分割するコードを書きます。この関数はドメインの合計サイズを受け取りMPIプロセスが担当する適切なサイズの分割された領域を決定します。そして残ってしまった領域を最後のプロセスに担当させます。簡単のためエラーが見つかった場合は`MPI_Abort`を呼び出すことにします。この関数`decompose_domain`を示します。
+
+```cpp
+void decompose_domain(int domain_size, int world_rank,
+ int world_size, int* subdomain_start,
+ int* subdomain_size) {
+ if (world_size > domain_size) {
+ // 領域の数が用意できるプロセスの数より大きいならエラーとします
+ MPI_Abort(MPI_COMM_WORLD, 1);
+ }
+ *subdomain_start = domain_size / world_size * world_rank;
+ *subdomain_size = domain_size / world_size;
+ if (world_rank == world_size - 1) {
+ // 残りを最後のプロセスに割り当てます
+ *subdomain_size += domain_size % world_size;
+ }
+ }
+```
+
+関数は余りがある場合を配慮しながらドメインを均等な領域に分割します。そして関数はその領域の開始位置とサブドメインのサイズを返します。
+
+ウォーカーは次の構造体で定義します。
+
+```cpp
+typedef struct {
+ int location;
+ int num_steps_left_in_walk;
+} Walker;
+```
+
+次に`initialize_walkers`という初期化関数を考えます。この関数はそのプロセスの領域を取得し、そのプロセスが担当するウォーカーを配列`incoming_walkers`に追加します (このアプリケーションは C++ で書かれています)。
+
+```cpp
+void initialize_walkers(int num_walkers_per_proc, int max_walk_size,
+ int subdomain_start, int subdomain_size,
+ vector* incoming_walkers) {
+ Walker walker;
+ for (int i = 0; i < num_walkers_per_proc; i++) {
+ // Initialize walkers in the middle of the subdomain
+ // 訳注: "in the mid"とあるが、ウォーカーは領域の最初から開始する
+ walker.location = subdomain_start;
+ walker.num_steps_left_in_walk =
+ (rand() / (float)RAND_MAX) * max_walk_size;
+ incoming_walkers->push_back(walker);
+ }
+}
+```
+
+初期化の次はウォーカーを前進させる関数です。この関数はウォーカーが歩行を完了するまで前進させます。ウォーカーが自分の管理する領域の端より進もうとしたら、そのプロセスの配列`outgoing_walkers`に追加します。
+
+```cpp
+void walk(Walker* walker, int subdomain_start, int subdomain_size,
+ int domain_size, vector* outgoing_walkers) {
+ while (walker->num_steps_left_in_walk > 0) {
+ if (walker->location == subdomain_start + subdomain_size) {
+ // Take care of the case when the walker is at the end
+ // of the domain by wrapping it around to the beginning
+ if (walker->location == domain_size) {
+ walker->location = 0;
+ }
+ outgoing_walkers->push_back(*walker);
+ break;
+ } else {
+ walker->num_steps_left_in_walk--;
+ walker->location++;
+ }
+ }
+}
+```
+
+初期化関数 (`incoming_walkers`にデータを追加する) とウォーキング関数 (`outgoing_walkers`にデータを追加する)ができたので、あとは`outgoing_walkers`を次のプロセスに送る関数と受け取る関数の2つがあれば良いです。まずは`outgoing_walkers`を次に送る関数を見ていきます。
+
+```cpp
+void send_outgoing_walkers(vector* outgoing_walkers,
+ int world_rank, int world_size) {
+ // 配列のデータをMPI_BYTEsのバイトデータとして次のプロセスに送ります
+ // 右端のプロセスはプロセス0にデータを送ることに注意します
+ MPI_Send((void*)outgoing_walkers->data(),
+ outgoing_walkers->size() * sizeof(Walker), MPI_BYTE,
+ (world_rank + 1) % world_size, 0, MPI_COMM_WORLD);
+
+ // 次のプロセスにデータを送ったので配列はクリアします
+ outgoing_walkers->clear();
+}
+```
+
+次は受け取る関数です。受け取るウォーカーの数が事前にわからないため`MPI_Probe`を使用する必要があります。
+
+```cpp
+void receive_incoming_walkers(vector* incoming_walkers,
+ int world_rank, int world_size) {
+ MPI_Status status;
+
+ // 前のプロセスからのデータを受け取ります
+ // プロセス0は右端のプロセスからのデータを受け取ります
+ int incoming_rank =
+ (world_rank == 0) ? world_size - 1 : world_rank - 1;
+ MPI_Probe(incoming_rank, 0, MPI_COMM_WORLD, &status);
+
+ // そして受け取るべきデータのサイズ分のメモリを確保します
+ int incoming_walkers_size;
+ MPI_Get_count(&status, MPI_BYTE, &incoming_walkers_size);
+ incoming_walkers->resize(
+ incoming_walkers_size / sizeof(Walker));
+ MPI_Recv((void*)incoming_walkers->data(), incoming_walkers_size,
+ MPI_BYTE, incoming_rank, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+}
+```
+
+これでプログラムの主な機能を実装しました。次のように処理を作っていきましょう。
+
+1. ウォーカーを初期化する。
+2. `walk` 関数でウォーカーを進める。
+3. `outgoing_walkers`にあるウォーカーを次に送る
+4. 新しいウォーカーは`incoming_walkers`に入れる。
+5. すべてのウォーカーが終了するまで、ステップ2から4を繰り返す。
+
+以下の通りとなるが、まずは5で行いたいすべてのウォーカーの終了判定は気にしないものとします。このコードには誤りがあるのでそれに留意してご覧ください。
+
+```cpp
+// このプロセスの領域を決める
+decompose_domain(domain_size, world_rank, world_size,
+ &subdomain_start, &subdomain_size);
+
+// このプロセスのウォーカーを配置する(incoming_walksに入れる)
+initialize_walkers(num_walkers_per_proc, max_walk_size,
+ subdomain_start, subdomain_size,
+ &incoming_walkers);
+
+while (!all_walkers_finished) { // 全てのウォーカーが終了するまで
+ // このプロセス内全てのウォーカーの動きを実施
+ for (int i = 0; i < incoming_walkers.size(); i++) {
+ walk(&incoming_walkers[i], subdomain_start, subdomain_size,
+ domain_size, &outgoing_walkers);
+ }
+
+ // 外に出ていくウォーカーの処理
+ send_outgoing_walkers(&outgoing_walkers, world_rank,
+ world_size);
+
+ // 新しく入ってきたウォーカーの処理
+ receive_incoming_walkers(&incoming_walkers, world_rank,
+ world_size);
+}
+```
+
+良さそうに見えますか?しかし、これはデッドロックが起こりやすいコードとなっています。
+
+## デッドロックとその予防方法 - Deadlock and prevention
+Wikipediaによれば、デッドロックとは「2つ以上のプロセスがそれぞれ他のプロセスのリソース解放を待つ。あるいは2つ以上のプロセスが循環的にリソースを待っている。こういった特定の状態のこと」です。上記のコードは`MPI_Send`の循環的なチェーンが発生します。
+
+
+
+とはいえ、実施には上記コードはデッドロックはほぼ発生*しません*。`MPI_Send`はブロッキング関数ですが、[MPI specification](http://www.amazon.com/gp/product/0262692163/ref=as_li_tf_tl?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262692163)では、`MPI_Send` は*送信バッファが確保できるまで*ブロックすると記載されています。MPI_Sendはネットワークがメッセージをバッファできるようになったときにブロッキングが終わります。つまりネットワークがバッファできなければそれに対応するreceiveが呼ばれるまでsendはブロックされるということです。今回のケースでは非常に小さな送信に対して非常に頻繁に受信を呼び出すコードであるためデッドロックはほぼないでしょう。しかし、全てのケースでネットワークバッファが十分に大きいと想定して並列プログラミングを書いてはいけません。
+
+このレッスンでは `MPI_Send` と `MPI_Recv` だけに焦点をあてています。送受信のデッドロックを回避するベストの方法は送信と受信が一致するようにメッセージングを順序付けることです。いくつかの方法が考えられますが、1つとしては偶数番目のプロセスが受信の前に送信を送り、奇数番目のプロセスがその逆を行うようにすることです。もし、2つの実行ステージで考えると次のようなイメージとなります。
+
+
+
+> **Note** - これを1プロセスの環境下で実行するとデッドロックが発生する例外があります。
+
+奇数個のプロセスでもこれは機能するのでしょうか?3つのプロセスで同様の図をもう一度見てみましょう。
+
+
+
+3つのパターン全てにおいて、少なくとも1つの`MPI_Send`と`MPI_Recv`が存在するパスがあるので、デッドロックの発生を心配する必要はないとわかりました。
+
+## 終了の判断 - Determining completion of all walkers
+それでは、すべてのウォーカーが終了したかを判断するステップを考えます。ウォーカーはランダムな距離をいどうするため、あるウォーカーが移動を終了することは全てのプロセスで起こり得ることに注意します。そのため、何らかの追加通信を行わずに全プロセスがすべての歩行者が終了を知ることは困難です。考えられる解決策の1つはプロセス0がすべての歩行者を追跡し、他のすべてのプロセスに終了を伝えることが考えられます。ただし、この解決策は各移動においてプロセス0以外のプロセスはプロセス0に完了した歩行者を報告し、さらにさまざまな種類の受信メッセージを処理する必要があるため非常に面倒ですね。
+
+このレッスンではもっとシンプルに考えます。どのウォーカーも移動できる最大距離と、ある送受信のプロセスのペアで移動できる最小の合計サイズ(サブドメインのサイズ)がわかっています。このため、各プロセスが終了までに行うべき送受信の量が定まります(訳注:全てのプロセスが最大の距離を行うとき、というのがこれに当てはまります)。この特徴とデッドロックを回避する戦略を用いると以下のように考えられます。
+
+```cpp
+// このプロセスの領域を決める
+decompose_domain(domain_size, world_rank, world_size,
+ &subdomain_start, &subdomain_size);
+
+// このプロセスのウォーカーを配置する(incoming_walksに入れる)
+initialize_walkers(num_walkers_per_proc, max_walk_size,
+ subdomain_start, subdomain_size,
+ &incoming_walkers);
+
+// 全てのウォーカーが終了するのに必要なsend/recv数を計算
+int maximum_sends_recvs =
+ max_walk_size / (domain_size / world_size) + 1;
+for (int m = 0; m < maximum_sends_recvs; m++) {
+ // このプロセス内全てのウォーカーの動きを実施
+ for (int i = 0; i < incoming_walkers.size(); i++) {
+ walk(&incoming_walkers[i], subdomain_start, subdomain_size,
+ domain_size, &outgoing_walkers);
+ }
+
+ // 偶数・奇数に基づいた順序で送受信を行う
+ if (world_rank % 2 == 0) {
+ send_outgoing_walkers(&outgoing_walkers, world_rank,
+ world_size);
+ receive_incoming_walkers(&incoming_walkers, world_rank,
+ world_size);
+ } else {
+ receive_incoming_walkers(&incoming_walkers, world_rank,
+ world_size);
+ send_outgoing_walkers(&outgoing_walkers, world_rank,
+ world_size);
+ }
+}
+```
+
+## Running the application
+
+レッスンのコードは[ここ]({{ site.github.code }}/tutorials/point-to-point-communication-application-random-walk/code)で見ることができます. 他のレッスンとは異なり、このコードはC++を使用しています。[installing MPICH2]({{ site.baseurl }}/tutorials/installing-mpich2/)の際に、C++ MPIコンパイラもインストールしました(明示的に設定した場合を除く)。MPICH2をローカル・ディレクトリにインストールした場合はMPICXX環境変数が正しいmpicxxコンパイラーを指すように設定されていることを確認してください。
+
+私のコードでは、アプリケーションの実行スクリプトにプログラムのデフォルト値を設定しています。ドメインサイズは 100、最大ウォークサイズは 500、プロセスあたりのウォーカー数は 20 です。random_walkプログラムを[レポジトリ]({{ site.github.code }})の*tutorials*ディレクトリから実行すると、5つのプロセスが生成され、このような出力が得られます。
+
+```
+>>> cd tutorials
+>>> ./run.py random_walk
+mpirun -n 5 ./random_walk 100 500 20
+Process 2 initiated 20 walkers in subdomain 40 - 59
+Process 2 sending 18 outgoing walkers to process 3
+Process 3 initiated 20 walkers in subdomain 60 - 79
+Process 3 sending 20 outgoing walkers to process 4
+Process 3 received 18 incoming walkers
+Process 3 sending 18 outgoing walkers to process 4
+Process 4 initiated 20 walkers in subdomain 80 - 99
+Process 4 sending 18 outgoing walkers to process 0
+Process 0 initiated 20 walkers in subdomain 0 - 19
+Process 0 sending 17 outgoing walkers to process 1
+Process 0 received 18 incoming walkers
+Process 0 sending 16 outgoing walkers to process 1
+Process 0 received 20 incoming walkers
+```
+
+プロセスはすべてのウォーカーの送受信を終える(と想定される回数)まで出力を続けます。
+
+## So what's next?
+いかがでしょうか。もしこのレッスンを心地よいと感じたなら良いことです。このアプリケーションは、初めての実際のアプリケーションとしてはかなり発展的なものです。
+
+次回のレッスンからはMPIでの*集団通信*について学習します。まず、[MPI Broadcast]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/)を学習します。その他のレッスンは[MPI tutorials]({{ site.baseurl }}/tutorials/)をみてください。
+
+冒頭で、このプログラムのコンセプト(ランダムウォーク)は多くの並列プログラムにも応用できることをお伝えしました。もっと学びたい人のために、以下に追加資料を掲載したのでお楽しみください :-)
+
+## 追加資料 - ランダムウォーキングと並列粒子追跡の類似性 Additional reading - Random walking and its similarity to parallel particle tracing
+ランダムウォーク問題は、一見単純なものに見えますが実は多くの種類の並列アプリケーションのシミュレーションの基礎となります。科学分野の一部の並列アプリケーションでは、多くの種類のランダムな送受信が必要です。1つのアプリケーション例は並列粒子追跡(parallel particle tracing)です。
+
+
+
+並列粒子追跡は流れ場を可視化するための主要な手法の1つです。粒子を流れ場に仮定して数値積分技術(Runge-Kutta法など)を用いて流れに沿って追跡します。この経路は、可視化のために描画することができます。描画の一例として上のトルネード画像があります。
+
+効率的な並列粒子追跡というのは非常に困難しいです。主な理由は粒子の移動方向が積分の各増分ステップの後にしか決定できないためです。したがってプロセスがすべての通信と計算を調整してバランスをとるのは困難です。より理解するために、粒子追跡の一般的な並列化を見てみましょう。
+
+
+
+この図はドメインを6つのプロセスに分割していることがわかります。粒子(時にはシードと呼ばれる)が各サブドメインに配置され(ウォーカーをそれぞれの領域に配置した方法に似ています)、その後トレースを開始します。粒子が境界を越えると、適切なサブドメインを持つプロセスと情報を交換します。このプロセスは粒子が領域から離れるか最大トレースの回数に達するまで繰り返されます。
+
+並列粒子追跡の問題は、先ほどコーディングしたアプリケーションと同様に`MPI_Send`、`MPI_Recv`、`MPI_Probe`を使用して解決できます。より効率的に作業を行うために、もっと洗練されたMPIルーチンも存在するのでそれは次のレッスンでお話しします :-)
+
+ランダムウォークの問題が他の並列アプリケーションとどのように似ているかを示す例を少なくとも1つは確認できたと思います。
\ No newline at end of file
diff --git a/tutorials/point-to-point-communication-application-random-walk/zh_cn.md b/tutorials/point-to-point-communication-application-random-walk/zh_cn.md
new file mode 100644
index 0000000..be29600
--- /dev/null
+++ b/tutorials/point-to-point-communication-application-random-walk/zh_cn.md
@@ -0,0 +1,370 @@
+---
+layout: post
+title: Point-to-Point Communication Application - Random Walk
+author: Wes Kendall
+categories: Beginner MPI
+tags:
+redirect_from: '/point-to-point-communication-application-random-walk/'
+---
+
+是时候使用 [发送和接收教程]({{ site.baseurl }}/tutorials/mpi-send-and-receive/) 以及 [`MPI_Probe` 和 `MPI_Status` 教程]({{ site.baseurl }}/tutorials/dynamic-receiving-with-mpi-probe-and-mpi-status/) 中介绍的一些概念来研究具体的应用程序示例了。
+本文应用程序模拟了一个被称之为“随机游走”的过程。
+
+> **注意** - 该站点的所有代码都位于 [GitHub]({{ site.github.repo }})。本文的代码位于此 [tutorials/point-to-point-communication-application-random-walk/code]({{ site.github.code }}/tutorials/point-to-point-communication-application-random-walk/code) 目录下。
+
+随机游走的基本问题定义如下:
+给定 *Min*,*Max* 和随机游走器 *W*,让游走器 *W* 向右以任意长度的 *S* 随机移动。
+如果该过程越过边界,它就会绕回。
+*W* 一次只能左右移动一个单位。
+
+
+
+尽管程序本身是非常基础的,但是并行化的随机游走可以模拟各种并行程序的行为。
+具体内容以后再说。
+现在,让我们概述一下如何并行化随机游走问题。
+
+## 随机游走问题的并行化
+
+在许多并行程序的应用中,首要任务是在各个进程之间划分域。
+随机行走问题的一维域大小为 *Max - Min + 1*(因为游走器包含 *Max* 和 *Min*)。
+假设游走器只能采取整数大小的步长,我们可以轻松地将域在每个进程中划分为大小近乎相等的块。
+例如,如果 *Min* 为 0,*Max* 为 20,并且我们有四个进程,则将像这样拆分域。
+
+
+
+前三个进程拥有域的五个单元,而最后一个进程则拥有最后五个单元并且再加上一个剩余的单元。
+一旦对域进行了分区,应用程序将初始化游走器。
+如前所述,游走器将以步长 *S* 进行总步数随机的游走。
+例如,如果游走器在进程 0(使用先前的分解域)上进行了移动总数为 6 的游走,则游走器的执行将如下所示:
+
+1. 游走器的步行长度开始增加。但是,当它的值达到 4 时,它已到达进程 0 的边界。因此,进程 0 必须与进程 1 交流游走器的信息。
+
+2. 进程 1 接收游走器,并继续移动,直到达到移动总数 6。然后,游走器可以继续进行新的随机移动。
+
+
+
+在此示例中,*W* 仅需从进程 0 到进程 1 进行一次通信。
+但是,如果 *W* 必须移动更长的距离,则可能需要沿其通过域的路径将其传递给更多的进程。
+
+## 使用 `MPI_Send` 和 `MPI_Recv` 组织代码
+
+可以使用 `MPI_Send` 和 `MPI_Recv` 对组织代码。
+在开始查看代码之前,让我们建立程序的一些初步特征和功能:
+
+* 明确每个进程在域中的部分。
+* 每个进程初始化 *N* 个 walker,所有这些 walker 都从其局部域的第一个值开始。
+* 每个 walker 都有两个相关的整数值:当前位置和剩余步数。
+* Walkers 开始遍历该域,并传递到其他进程,直到完成所有移动。
+* 当所有 walker 完成时,该进程终止。
+
+让我们从编写用于分解域的代码开始。
+该函数将考虑域的总大小,并为 MPI 进程找到合适的子域。
+它还会将域的其余部分交给最终的进程。
+为了简单起见,我会调用 `MPI_Abort` 处理发现的任何错误。
+名为 `decompose_domain` 的函数如下所示:
+
+```cpp
+void decompose_domain(int domain_size, int world_rank,
+ int world_size, int* subdomain_start,
+ int* subdomain_size) {
+ if (world_size > domain_size) {
+ // Don't worry about this special case. Assume the domain
+ // size is greater than the world size.
+ MPI_Abort(MPI_COMM_WORLD, 1);
+ }
+ *subdomain_start = domain_size / world_size * world_rank;
+ *subdomain_size = domain_size / world_size;
+ if (world_rank == world_size - 1) {
+ // Give remainder to last process
+ *subdomain_size += domain_size % world_size;
+ }
+ }
+```
+
+如您所见,该函数将域分成偶数个块,并考虑了存在余数的情况。
+该函数返回一个子域开始和一个子域大小。
+
+接下来,我们需要创建一个初始化 walkers 的函数。
+我们首先定义一个如下所示的 walker 结构:
+
+```cpp
+typedef struct {
+ int location;
+ int num_steps_left_in_walk;
+} Walker;
+```
+
+我们的初始化函数为 `initialize_walkers`,它采用子域边界,并将 walker 添加到 `incoming_walkers` `vector` 中(顺便说一下,该程序采用 C++)。
+
+
+```cpp
+void initialize_walkers(int num_walkers_per_proc, int max_walk_size,
+ int subdomain_start, int subdomain_size,
+ vector* incoming_walkers) {
+ Walker walker;
+ for (int i = 0; i < num_walkers_per_proc; i++) {
+ // Initialize walkers in the middle of the subdomain
+ walker.location = subdomain_start;
+ walker.num_steps_left_in_walk =
+ (rand() / (float)RAND_MAX) * max_walk_size;
+ incoming_walkers->push_back(walker);
+ }
+}
+```
+
+初始化之后,就该使 walkers 前进了。
+让我们从一个移动功能开始。
+此功能负责使 walkers 前进,直到完成移动为止。
+如果超出局部域范围,则将其添加到 `outgoing_walkers` `vector` 中。
+
+```cpp
+void walk(Walker* walker, int subdomain_start, int subdomain_size,
+ int domain_size, vector* outgoing_walkers) {
+ while (walker->num_steps_left_in_walk > 0) {
+ if (walker->location == subdomain_start + subdomain_size) {
+ // Take care of the case when the walker is at the end
+ // of the domain by wrapping it around to the beginning
+ if (walker->location == domain_size) {
+ walker->location = 0;
+ }
+ outgoing_walkers->push_back(*walker);
+ break;
+ } else {
+ walker->num_steps_left_in_walk--;
+ walker->location++;
+ }
+ }
+}
+```
+
+现在,我们已经建立了初始化函数(用于填充传入的 walker 列表)和移动函数(用于填充传出的 walker 列表),我们仅再需要两个函数:发送待传出的 walker 的函数和接收待传入的 walker 的函数。
+发送功能如下所示:
+
+```cpp
+void send_outgoing_walkers(vector* outgoing_walkers,
+ int world_rank, int world_size) {
+ // Send the data as an array of MPI_BYTEs to the next process.
+ // The last process sends to process zero.
+ MPI_Send((void*)outgoing_walkers->data(),
+ outgoing_walkers->size() * sizeof(Walker), MPI_BYTE,
+ (world_rank + 1) % world_size, 0, MPI_COMM_WORLD);
+
+ // Clear the outgoing walkers
+ outgoing_walkers->clear();
+}
+```
+
+接收传入的 walkers 的函数应该使用 `MPI_Probe`,因为它事先不知道将接收多少 walkers。
+看起来是这样的:
+
+```cpp
+void receive_incoming_walkers(vector* incoming_walkers,
+ int world_rank, int world_size) {
+ MPI_Status status;
+
+ // Receive from the process before you. If you are process zero,
+ // receive from the last process
+ int incoming_rank =
+ (world_rank == 0) ? world_size - 1 : world_rank - 1;
+ MPI_Probe(incoming_rank, 0, MPI_COMM_WORLD, &status);
+
+ // Resize your incoming walker buffer based on how much data is
+ // being received
+ int incoming_walkers_size;
+ MPI_Get_count(&status, MPI_BYTE, &incoming_walkers_size);
+ incoming_walkers->resize(
+ incoming_walkers_size / sizeof(Walker));
+ MPI_Recv((void*)incoming_walkers->data(), incoming_walkers_size,
+ MPI_BYTE, incoming_rank, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+}
+```
+
+现在我们已经建立了程序的主要功能。
+我们必须将所有这些功能集成在一起,如下所示:
+
+1. 初始化 walkers.
+2. 使用 `walk` 函数使 walkers 前进。
+3. 发出 `outgoing_walkers` 向量中的所有的 walkers。
+4. 将新接收的 walkers 放入 `incoming_walkers` 向量中。
+5. 重复步骤 2 到 4,直到所有 walkers 完成。
+
+下面是完成此程序的第一次尝试。
+此刻,我们不必担心如何确定所有 walkers 完成的时间。
+但在查看代码之前,我必须警告您-该代码不正确!
+知晓这个问题以后,让我们看一下代码,希望您能发现它可能有什么问题。
+
+```cpp
+// Find your part of the domain
+decompose_domain(domain_size, world_rank, world_size,
+ &subdomain_start, &subdomain_size);
+
+// Initialize walkers in your subdomain
+initialize_walkers(num_walkers_per_proc, max_walk_size,
+ subdomain_start, subdomain_size,
+ &incoming_walkers);
+
+while (!all_walkers_finished) { // Determine walker completion later
+ // Process all incoming walkers
+ for (int i = 0; i < incoming_walkers.size(); i++) {
+ walk(&incoming_walkers[i], subdomain_start, subdomain_size,
+ domain_size, &outgoing_walkers);
+ }
+
+ // Send all outgoing walkers to the next process.
+ send_outgoing_walkers(&outgoing_walkers, world_rank,
+ world_size);
+
+ // Receive all the new incoming walkers
+ receive_incoming_walkers(&incoming_walkers, world_rank,
+ world_size);
+}
+```
+
+一切看起来都很正常,但是函数调用的顺序引入了一种非常可能的情形 - 死锁。
+
+## 死锁及预防
+
+根据 Wikipedia 的说法,死锁 *是指两个或多个进程各自在等待另一个进程释放资源,或者两个或多个进程在循环链中等待资源的特定条件。* 代码将导致 `MPI_Send` 调用的循环链。
+
+
+
+值得注意的是,上面的代码在大多数情况下实际上不会“死锁”。
+尽管 `MPI_Send` 是一个阻塞调用,但是 [MPI 规范](http://www.amazon.com/gp/product/0262692163/ref=as_li_tf_tl?ie=UTF8&tag=softengiintet-20&linkCode=as2&camp=217145&creative=399377&creativeASIN=0262692163) 表明 `MPI_Send` 会一直阻塞,直到可以**回收发送缓冲区为止**。
+这意味着当网络可以缓冲消息时,`MPI_Send` 将返回。
+如果发送最终无法被网络缓冲,它们将一直阻塞直到发布匹配的接收。
+在我们的例子中,有足够多的小发送和频繁匹配的接收而不必担心死锁,但是,永远不该假定有足够大的网络缓冲区。
+
+由于在本文中我们仅关注 `MPI_Send` 和 `MPI_Recv`,因此避免可能发生的发送和接收死锁的最佳方法是对消息进行排序,以使发送将具有匹配的接收,反之亦然。
+一种简单的方法是更改循环,以使偶数编号的进程在接收 walkers 之前发送传出的 walkers,而奇数编号的进程则相反。
+在执行的两个阶段,发送和接收现在看起来像这样:
+
+
+
+> **注意** - 使用一个进程执行此操作仍可能会死锁。为了避免这种情况,仅在使用一个进程时不要执行发送和接收。
+
+您可能会问,这仍然适用于奇数个进程吗?
+我们可以通过三个过程再次查看相似的图表:
+
+
+
+如您所见,在所有三个阶段中,至少有一个发布的 `MPI_Send` 与发布的 `MPI_Recv` 匹配,因此我们不必担心死锁的发生。
+
+## Determining completion of all walkers
+
+现在是程序的最后一步 - 确定每个 walker 何时结束。
+由于 walkers 可以随机行走,因此它们可以在任何一个进程中结束它们的旅程。
+因此,如果没有某种额外的通信,所有进程都很难知道 walkers 何时全部结束。
+一种可能的解决方案是让进程零跟踪所有已完成的 walker,然后告诉其他所有进程何时终止。
+但是,这样的解决方案非常麻烦,因为每个进程都必须向进程 0 报告所有完成的 walker,然后还要处理不同类型的传入消息。
+
+在本文中,我们让这件事情稍微简单一点。
+由于我们知道任意一个 walker 可以行进的最大距离和每对发送和接收对它可以行进的最小总大小(子域大小),因此我们可以计算出终止之前每个进程应该执行的发送和接收量。
+在我们避免死锁的策略中考虑这一特征,该程序的最后主要部分如下所示:
+
+```cpp
+// Find your part of the domain
+decompose_domain(domain_size, world_rank, world_size,
+ &subdomain_start, &subdomain_size);
+
+// Initialize walkers in your subdomain
+initialize_walkers(num_walkers_per_proc, max_walk_size,
+ subdomain_start, subdomain_size,
+ &incoming_walkers);
+
+// Determine the maximum amount of sends and receives needed to
+// complete all walkers
+int maximum_sends_recvs =
+ max_walk_size / (domain_size / world_size) + 1;
+for (int m = 0; m < maximum_sends_recvs; m++) {
+ // Process all incoming walkers
+ for (int i = 0; i < incoming_walkers.size(); i++) {
+ walk(&incoming_walkers[i], subdomain_start, subdomain_size,
+ domain_size, &outgoing_walkers);
+ }
+
+ // Send and receive if you are even and vice versa for odd
+ if (world_rank % 2 == 0) {
+ send_outgoing_walkers(&outgoing_walkers, world_rank,
+ world_size);
+ receive_incoming_walkers(&incoming_walkers, world_rank,
+ world_size);
+ } else {
+ receive_incoming_walkers(&incoming_walkers, world_rank,
+ world_size);
+ send_outgoing_walkers(&outgoing_walkers, world_rank,
+ world_size);
+ }
+}
+```
+
+## Running the application
+
+代码可在 [此处查看]({{ site.github.code }}/tutorials/point-to-point-communication-application-random-walk/code).
+与其他教程相反,此处代码使用 C++。
+在 [安装 MPICH2]({{ site.baseurl }}/tutorials/installing-mpich2/) 时,还安装了 C++ MPI 编译器(除非您另有明确配置)。
+如果将 MPICH2 安装在本地目录中,请确保已将 MPICXX 环境变量设置为指向正确的 mpicxx 编译器,以便使用我的 makefile。
+
+在我的代码中,我设置了运行脚本来提供运行的默认值:域大小为 100,最大步行大小为 500,每个进程的步行者数量为 20。
+如果您从 [repo]({{ site.github.code }}) 的 *tutorials* 目录运行 random_walk 程序,它应该产生 5 个进程,并产生与下方类似的输出。
+
+```
+>>> cd tutorials
+>>> ./run.py random_walk
+mpirun -n 5 ./random_walk 100 500 20
+Process 2 initiated 20 walkers in subdomain 40 - 59
+Process 2 sending 18 outgoing walkers to process 3
+Process 3 initiated 20 walkers in subdomain 60 - 79
+Process 3 sending 20 outgoing walkers to process 4
+Process 3 received 18 incoming walkers
+Process 3 sending 18 outgoing walkers to process 4
+Process 4 initiated 20 walkers in subdomain 80 - 99
+Process 4 sending 18 outgoing walkers to process 0
+Process 0 initiated 20 walkers in subdomain 0 - 19
+Process 0 sending 17 outgoing walkers to process 1
+Process 0 received 18 incoming walkers
+Process 0 sending 16 outgoing walkers to process 1
+Process 0 received 20 incoming walkers
+```
+
+输出将一直持续到各个进程完成所有 walkers 的发送和接收。
+
+## 下一步是什么?
+
+接下来,我们将开始学习 MPI 中的*集体*通信。
+我们将从 [MPI Broadcast]({{ site.baseurl }}/tutorials/mpi-broadcast-and-collective-communication/) 开始,对于所有教程,请转到 [MPI tutorials]({{ site.baseurl }}/tutorials/).
+
+另外,在一开始,我告诉您本文中的程序的概念适用于许多并行程序。
+我不想让您垂涎三尺,因此,我在下面提供了一些其他阅读材料,供那些希望了解更多信息的人使用。
+请享用 :-)
+
+## 附加阅读 - 随机游走及其与并行粒子跟踪的相似性
+
+我们刚刚实现的随机游走问题虽然看似微不足道,但实际上可以构成模拟多种并行应用程序的基础。
+科学领域中的某些并行应用程序需要多种类型的随机发送和接收。
+一种示例应用是并行粒子跟踪。
+
+
+
+并行粒子跟踪是用于可视化流场的主要方法之一。
+将粒子插入流场,然后使用数值积分技术(例如 Runge-Kutta)沿流线跟踪。
+然后可以呈现跟踪的路径以用于可视化目的。
+一个示例渲染是左上方的龙卷风图像。
+
+执行有效的并行粒子跟踪可能非常困难。
+这样做的主要原因是,只有在积分的每个增量步骤之后才能确定粒子行进的方向。
+因此,线程很难协调和平衡所有通信和计算。
+为了更好地理解这一点,让我们看一下粒子跟踪的典型并行化。
+
+
+
+在此插图中,我们看到该域分为六个过程。
+然后将粒子(有时称为*种子*)放置在子域中(类似于我们将 walkers 放置在子域中的方式),然后开始跟踪它们。
+当粒子超出范围时,必须与具有适当子域的进程进行交换。
+重复此过程,直到粒子离开整个域或达到最大迹线长度为止。
+
+可以使用 `MPI_Send`,`MPI_Recv` 和 `MPI_Probe` 来解决并行粒子跟踪问题,其方式与我们刚刚实现的应用程序类似。
+当然,还有许多更复杂的 MPI 例程可以更有效地完成这样的工作。
+我们将在接下来的教程中讨论这些问题:-)
+
+我只是希望您现在至少可以看到一个例子来说明随机游走问题与其他并行应用程序有何相似之处!
diff --git a/tutorials/run.py b/tutorials/run.py
index 4b9002d..2215481 100755
--- a/tutorials/run.py
+++ b/tutorials/run.py
@@ -19,7 +19,7 @@
'probe': ('dynamic-receiving-with-mpi-probe-and-mpi-status', 2),
# From the point-to-point-communication-application-random-walk tutorial
- 'random_walk': ('point-to-point-communication-application-random-walk', 2, ['100', '500', '20']),
+ 'random_walk': ('point-to-point-communication-application-random-walk', 5, ['100', '500', '20']),
# From the mpi-broadcast-and-collective-communication tutorial
'my_bcast': ('mpi-broadcast-and-collective-communication', 4),
@@ -35,11 +35,15 @@
# From the mpi-reduce-and-allreduce tutorial
'reduce_avg': ('mpi-reduce-and-allreduce', 4, ['100']),
'reduce_stddev': ('mpi-reduce-and-allreduce', 4, ['100']),
+
+ # From the groups-and-communicators tutorial
+ 'comm_split': ('introduction-to-groups-and-communicators', 16),
+ 'comm_groups': ('introduction-to-groups-and-communicators', 16)
}
program_to_run = sys.argv[1] if len(sys.argv) > 1 else None
if not program_to_run in programs:
- print 'Must enter program name to run. Possible programs are: {0}'.format(programs.keys())
+ print('Must enter program name to run. Possible programs are: {0}'.format(programs.keys()))
else:
# Try to compile before running
with open(os.devnull, 'wb') as devnull:
@@ -56,5 +60,5 @@
if len(programs[program_to_run]) > 2:
sys_call = '{0} {1}'.format(sys_call, ' '.join(programs[program_to_run][2]))
- print sys_call
+ print(sys_call)
subprocess.call([sys_call], shell=True)
diff --git a/tutorials/running-an-mpi-cluster-within-a-lan/index.md b/tutorials/running-an-mpi-cluster-within-a-lan/index.md
new file mode 100644
index 0000000..79e9175
--- /dev/null
+++ b/tutorials/running-an-mpi-cluster-within-a-lan/index.md
@@ -0,0 +1,265 @@
+---
+layout: post
+title: Running an MPI Cluster within a LAN
+author: Dwaraka Nath
+categories: Beginner MPI
+translations: ja_jp
+tags: MPI, Cluster, LAN
+redirect_from: '/running-an-mpi-cluster-within-a-lan'
+---
+
+Earlier, we looked at running MPI programs in a [single machine]({{ site.baseurl }}/tutorials/mpi-hello-world/) to parallel process the code, taking advantage of having more than a single core in CPU. Now, let's widen our scope a bit, taking the same from more than just one computer to a network of nodes connected together in a Local Area Network. To keep things simple, let's just consider two computers for now. It is fairly straight to implement the same with many more nodes.
+
+As with other tutorials, I am assuming you run Linux machines. The following tutorial was tested with Ubuntu, but it should be the same with any other distribution. And also, let's consider your machine to be **manager** and the other one as **worker**
+
+## Pre-requisite
+
+If you have not installed MPICH2 in each of the machines, follow the steps [here]({{ site.baseurl }}/tutorials/installing-mpich2/).
+
+## Step 1: Configure your ```hosts``` file
+
+You are gonna need to communicate between the computers and you don't want to type in the IP addresses every so often. Instead, you can give a name to the various nodes in the network that you wish to communicate with. ```hosts``` file is used by your device operating system to map hostnames to IP addresses.
+
+```bash
+
+$ cat /etc/hosts
+
+127.0.0.1 localhost
+172.50.88.34 worker
+```
+The ```worker``` here is the machine you'd like to do your computation with. Likewise, do the same about ```manager``` in the worker.
+
+## Step 2: Create a new user
+
+Though you can operate your cluster with your existing user account, I'd recommend you to create a new one to keep our configurations simple. Let us create a new user ```mpiuser```. Create new user accounts with the same username in all the machines to keep things simple.
+
+```bash
+$ sudo adduser mpiuser
+```
+Follow prompts and you will be good. Please don't use ```useradd``` command to create a new user as that doesn't create a separate home for new users.
+
+## Step 3: Setting up SSH
+
+Your machines are gonna be talking over the network via SSH and share data via [NFS](#step-4-setting-up-nfs), about which we'll talk a little later.
+
+```bash
+$ sudo apt-get install openssh-server
+```
+
+And right after that, login with your newly created account
+
+```bash
+$ su - mpiuser
+```
+Since the ```ssh``` server is already installed, you must be able to login to other machines by ```ssh username@hostname```, at which you will be prompted to enter the password of the ```username```. To enable more easier login, we generate keys and copy them to other machines' list of ```authorized_keys```.
+
+```bash
+$ ssh-keygen -t dsa
+```
+
+You can as well generate RSA keys. But again, it is totally up to you. If you want more security, go with RSA. Else, DSA should do just fine. Now, add the generated key to each of the other computers. In our case, the worker machine.
+
+```bash
+$ ssh-copy-id worker #ip-address may also be used
+```
+
+Do the above step for each of the worker machines and your own user (localhost).
+
+This will setup ```openssh-server``` for you to securely communicate with the worker machines. ```ssh``` all machines once, so they get added to your list of ```known_hosts```. This is a very simple but essential step failing which passwordless ```ssh``` will be a trouble.
+
+Now, to enable passwordless ssh,
+
+```bash
+$ eval `ssh-agent`
+$ ssh-add ~/.ssh/id_dsa
+```
+Now, assuming you've properly added your keys to other machines, you must be able to login to other machines without any password prompt.
+
+```bash
+$ ssh worker
+```
+
+> **Note** - Since I've assumed that you've created ```mpiuser``` as the common user account in all of the worker machines, this should just work fine. If you've created user accounts with different names in manager and worker machines, you'll need to work around that.
+
+## Step 4: Setting up NFS
+
+You share a directory via NFS in **manager** which the **worker** mounts to exchange data.
+
+### NFS-Server
+
+Install the required packages by
+
+```bash
+$ sudo apt-get install nfs-kernel-server
+```
+
+Now, (assuming you are still logged into ```mpiuser```), let's create a folder by the name ```cloud``` that we will share across in the network.
+
+```bash
+$ mkdir cloud
+```
+
+To export the ```cloud``` directory, you create an entry in ```/etc/exports```
+
+```bash
+$ cat /etc/exports
+/home/mpiuser/cloud *(rw,sync,no_root_squash,no_subtree_check)
+```
+Here, instead of ```*``` you can specifically give out the IP address to which you want to share this folder to. But, this will just make our job easier.
+
+* **rw**: This is to enable both read and write option. **ro** is for read-only.
+* **sync**: This applies changes to the shared directory only after changes are committed.
+* **no_subtree_check**: This option prevents the subtree checking. When a shared directory is the subdirectory of a larger filesystem, nfs performs scans of every directory above it, in order to verify its permissions and details. Disabling the subtree check may increase the reliability of NFS, but reduce security.
+* **no_root_squash**: This allows root account to connect to the folder.
+
+> Thanks to [Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nfs-mount-on-ubuntu-12-04) for help with tutorial and explanations. Content re-used on account of Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. For information, read [here](https://creativecommons.org/licenses/by-nc-sa/4.0/).
+
+After you have made the entry, run the following.
+
+```bash
+$ exportfs -a
+```
+
+Run the above command, every time you make a change to ```/etc/exports```.
+
+If required, restart the ```nfs``` server
+
+```bash
+$ sudo service nfs-kernel-server restart
+```
+
+### NFS-worker
+
+Install the required packages
+
+```bash
+$ sudo apt-get install nfs-common
+```
+
+Create a directory in the worker's machine with the samename ```cloud```
+
+```bash
+$ mkdir cloud
+```
+
+And now, mount the shared directory like
+
+```bash
+$ sudo mount -t nfs manager:/home/mpiuser/cloud ~/cloud
+```
+
+To check the mounted directories,
+
+```bash
+$ df -h
+Filesystem Size Used Avail Use% Mounted on
+manager:/home/mpiuser/cloud 49G 15G 32G 32% /home/mpiuser/cloud
+```
+
+To make the mount permanent so you don't have to manually mount the shared directory everytime you do a system reboot, you can create an entry in your file systems table - i.e., ```/etc/fstab``` file like this:
+
+```bash
+$ cat /etc/fstab
+#MPI CLUSTER SETUP
+manager:/home/mpiuser/cloud /home/mpiuser/cloud nfs
+```
+
+## Step 5: Running MPI programs
+
+For consideration sake, let's just take a sample program, that comes along with MPICH2 installation package ```mpich2/examples/cpi```. We shall take this executable and try to run it parallely.
+
+Or if you want to compile your own code, the name of which let's say is ```mpi_sample.c```, you will compile it the way given below, to generate an executable ```mpi_sample```.
+
+```bash
+$ mpicc -o mpi_sample mpi_sample.c
+```
+
+First copy your executable into the shared directory ```cloud``` or better yet, compile your code within the NFS shared directory.
+
+```bash
+$ cd cloud/
+$ pwd
+/home/mpiuser/cloud
+```
+
+To run it only in your machine, you do
+
+```bash
+$ mpirun -np 2 ./cpi # No. of processes = 2
+```
+
+Now, to run it within a cluster,
+
+```bash
+$ mpirun -np 5 -hosts worker,localhost ./cpi
+#hostnames can also be substituted with ip addresses.
+```
+
+Or specify the same in a hostfile and
+
+```bash
+$ mpirun -np 5 --hostfile mpi_file ./cpi
+```
+
+This should spin up your program in all of the machines that your **manager** is connected to.
+
+## Common errors and tips
+
+* Make sure all the machines you are trying to run the executable on, has the same version of MPI. Recommended is [MPICH2](http://www.mpich.org/downloads/).
+* The ```hosts``` file of ```manager``` should contain the local network IP address entries of ```manager``` and all of the worker nodes. For each of the workers, you need to have the IP address entry of ```manager``` and the corresponding worker node.
+
+For e.g. a sample hostfile entry of a ```manager``` node can be,
+
+```bash
+$ cat /etc/hosts
+127.0.0.1 localhost
+#127.0.1.1 1944
+
+#MPI CLUSTER SETUP
+172.50.88.22 manager
+172.50.88.56 worker1
+172.50.88.34 worker2
+172.50.88.54 worker3
+172.50.88.60 worker4
+172.50.88.46 worker5
+```
+A sample hostfile entry of ```worker3``` node can be,
+
+```bash
+$ cat /etc/hosts
+127.0.0.1 localhost
+#127.0.1.1 1947
+
+#MPI CLUSTER SETUP
+172.50.88.22 manager
+172.50.88.54 worker3
+```
+* Whenever you try to run a process parallely using MPI, you can either run the process locally or run it as a combination of local and remote nodes. You **cannot** invoke a process **only on other nodes**.
+
+To make this more clear, from ```manager``` node, this script can be invoked.
+
+```bash
+$ mpirun -np 10 --hosts manager ./cpi
+# To run the program only on the same manager node
+```
+
+So can this be. The following will also run perfectly.
+
+```bash
+$ mpirun -np 10 --hosts manager,worker1,worker2 ./cpi
+# To run the program on manager and worker nodes.
+```
+
+But, the following is **not correct** and will result in an error if invoked from ```manager```.
+
+```bash
+$ mpirun -np 10 --hosts worker1 ./cpi
+# Trying to run the program only on remote worker
+```
+
+## So, what's next?
+
+Exciting isn't it, for having built a cluster to run your code? You now need to know the specifics of writing a program that can run parallely. Best place to start off would be the lesson [MPI hello world lesson]({{ site.baseurl }}/tutorials/mpi-hello-world/). Or if you want to replicate the same using Amazon EC2 instances, I suggest you have a look at [building and running your own cluster on Amazon EC2]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/). For all the other lessons, you may go to the [MPI tutorials]({{ site.baseurl }}/tutorials/) page.
+
+Should you have any issues in setting up your local cluster, please don't hesitate to comment below so we can try to sort it out.
+
diff --git a/tutorials/running-an-mpi-cluster-within-a-lan/ja_jp.md b/tutorials/running-an-mpi-cluster-within-a-lan/ja_jp.md
new file mode 100644
index 0000000..5c6978a
--- /dev/null
+++ b/tutorials/running-an-mpi-cluster-within-a-lan/ja_jp.md
@@ -0,0 +1,262 @@
+---
+layout: post
+title: LAN上でのMPIクラスタ構築 - Running an MPI Cluster within a LAN
+author: Dwaraka Nath
+categories: Beginner MPI
+tags: MPI, Cluster, LAN
+redirect_from: '/running-an-mpi-cluster-within-a-lan'
+---
+
+先のレッスンではMPIプログラムを[1台のマシン]({{ site.baseurl }}/tutorialss/mpi-hello-world/)で実行し、CPUに1つ以上のコアがある利点を生かしてコードを並列処理することを見てきました。それでは同じことを1台のコンピュータではなく、LANに接続されたノードで実行できるようにしましょう。物事を単純にするために、このレッスンでは2台のコンピュータを考えます。ノードをもっと増やすのは同じようにできます。
+
+他のチュートリアルと同様にLinuxマシンを使用することを前提としています。以下のチュートリアルはUbuntuでテストしましたが、他のディストリビューションでも同じはずです。また、あなたの2台のマシンを**管理者(manager)**とし、もう1台を**作業者(worker)**と呼ぶとしましょう。
+
+## 前提
+
+すべてのマシンにMPICH2をインストールしていない場合は、[この手順]({{ site.baseurl }}/tutorials/installing-mpich2/)を実行してください。
+
+## Step 1: `hosts`の準備 - Configure your ```hosts``` file
+
+`hosts`を用意しましょう。これはマシンのオペレーティングシステムがホスト名をIPアドレスにマッピングするために使用します。以下に例を示します。
+
+```bash
+
+$ cat /etc/hosts
+
+127.0.0.1 localhost
+172.50.88.34 worker
+```
+これは`Manager`の`hosts`で、`worker`は、あなたが計算を行いたい他のマシン名です。同様に、Workerでは`manager`も同じようにします。
+
+## Step 2: ユーザの作成 - Create a new user
+
+既存のユーザーアカウントでクラスタを操作することもできます。しかし、設定をシンプルにするために新しいユーザーアカウントを作成することをお勧めします。全てのホストでユーザー`mpiuser`を作成しましょう。
+
+```bash
+$ sudo adduser mpiuser
+```
+プロンプトに従ってください。`adduser`ではなく`useradd`コマンドを使ってしまうとホームディレクトリが作成されないので使用しないでください。
+
+## Step 3: SSHの設定 - Setting up SSH
+
+マシン間が通信できるようにSSHの設定をします。[NFS](#step-4-setting-up-nfs)を使ってデータを共有します。
+```bash
+$ sudo apt-get install openssh-server
+```
+
+まず、このホストでユーザをmpiuserに切り替えます。
+
+```bash
+$ su - mpiuser
+```
+すでに `ssh` サーバーがインストールされているので、他のマシンに `ssh username@hostname` でログインできるはずです。パスワードを尋ねられないようにキーを生成して他のマシンの ``authorized_keys`` のリストにコピーします。(訳注:この処理は公開鍵認証を可能にする、という処理です。パスワード認証を禁止してはいないことに注意してください)
+
+```bash
+$ ssh-keygen -t dsa
+```
+
+この例ではDSA鍵を作成しましたがRSA鍵を生成することもできます。より高いセキュリティを求めるのであれば、RSAを使っても良いですがDSAでも十分です。そして、生成した鍵を他のコンピューターに追加すしましょう。wokerノードに対してコピーします。
+
+```bash
+$ ssh-copy-id worker #ip-address may also be used
+```
+
+それぞれのWorkerマシンと自分のユーザ (localhost) に対してここまでの手順を実行してください。
+
+これで `openssh-server` がセットアップされ、Workerと安全に通信できるようになります。すべてのマシンに一度 `ssh` を実行してください。そして`known_hosts` のリストに他のホストのフィンガープリントを追加します。このステップは`ssh`ログインのトラブルになりやすいので重要なステップです。
+
+パスワードなしの sshができるようにしましょう。
+
+```bash
+$ eval `ssh-agent`
+$ ssh-add ~/.ssh/id_dsa
+```
+さてパスワードのプロンプトなしで他のマシンにログインできるはずです。
+
+```bash
+$ ssh worker
+```
+
+> **Note** - すべてのワーカーマシンで共通のユーザアカウントとして `mpiuser` を作成したと仮定しています。ManagerとWorkerで異なる名前のユーザーアカウントを作成した場合は適切なユーザ名を指定してください。
+
+## Step 4: NFSの設定 - Setting up NFS
+
+**manager**はNFS経由でディレクトリを共有し、それを**worker**がマウントしてデータをやり取りします。
+
+### NFS-Server
+
+パッケージをインストールします。
+
+```bash
+$ sudo apt-get install nfs-kernel-server
+```
+
+まだ `mpiuser` にログインしていると仮定して `cloud` という名前の共有フォルダを作成します。
+
+```bash
+$ mkdir cloud
+```
+
+`cloud`を共有するために`/etc/exports`を以下のようにします。
+
+```bash
+$ cat /etc/exports
+/home/mpiuser/cloud *(rw,sync,no_root_squash,no_subtree_check)
+```
+`*`の部分にはこのフォルダを共有したいIPアドレスを指定することができますが。今回は簡単のため`*`にしています。
+
+* **rw**: read/writeを許可します。readのみを割り当てたいなら **ro**を設定します。
+* **sync**: 変更をコミットした後にのみ、共有ディレクトリに変更を適用されます
+* **no_subtree_check**: サブツリーをチェックしません。共有ディレクトリが大きなファイルシステムのサブディレクトリである場合、 nfsはその上のすべてのディレクトリのパーミッションと詳細を確認するために スキャンを実行します。サブツリー・チェックを無効にするとNFSの信頼性は向上しますがセキュリティが低下することがあります。
+* **no_root_squash**: rootアカウントによるフォルダへの接続を許可します。
+
+> この説明を作るために[Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nfs-mount-on-ubuntu-12-04)を参考にしました. このライセンスはCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International Licenseに従います。このライセンスの詳細は [ここ](https://creativecommons.org/licenses/by-nc-sa/4.0/)です。
+
+NFSを実行します。
+
+```bash
+$ exportfs -a
+```
+
+`/etc/exports` に変更を加えた場合はこのコマンドが必要です。
+
+必要に応じて`nfs` サーバを再起動してください。
+
+```bash
+$ sudo service nfs-kernel-server restart
+```
+
+### NFS-worker
+
+以下を実行します。
+
+```bash
+$ sudo apt-get install nfs-common
+```
+
+workerで同じ名前のディレクトリを作成します。
+
+```bash
+$ mkdir cloud
+```
+
+そして次のように共有ディレクトリをマウントしてください。
+
+```bash
+$ sudo mount -t nfs manager:/home/mpiuser/cloud ~/cloud
+```
+
+正常にマウントできたか確認しましょう。
+
+```bash
+$ df -h
+Filesystem Size Used Avail Use% Mounted on
+manager:/home/mpiuser/cloud 49G 15G 32G 32% /home/mpiuser/cloud
+```
+
+再起動のために手動で共有ディレクトリをマウントする必要がないようにするには ``/etc/fstab`` ファイルに次のようなエントリを作成してください。
+
+```bash
+$ cat /etc/fstab
+#MPI CLUSTER SETUP
+manager:/home/mpiuser/cloud /home/mpiuser/cloud nfs
+```
+
+## Step 5: MPIプログラムの実行 - Running MPI programs
+
+それでは正常性の確認のため、MPICH2のインストールパッケージ `mpich2/examples/cpi` に付属しているサンプルプログラムを並列実行してみましょう。
+
+自分のコードをコンパイルしたい場合は、コードを`mpi_sample.c` とすると、以下に示す方法でコンパイルして ``mpi_sample`` を生成できます。
+
+
+```bash
+$ mpicc -o mpi_sample mpi_sample.c
+```
+
+そして実行ファイルを共有ディレクトリ `cloud` にコピーしてください。もちろん、NFS 共有ディレクトリ内でコードをコンパイルしても良いです。
+
+```bash
+$ cd cloud/
+$ pwd
+/home/mpiuser/cloud
+```
+
+自分のマシン(manager)だけで実行するときは以下のようにします。
+
+```bash
+$ mpirun -np 2 ./cpi # No. of processes = 2
+```
+
+クラスタで実行してみましょう。
+
+```bash
+$ mpirun -np 5 -hosts worker,localhost ./cpi
+#ホスト名の代わりにIPアドレスでも良いです。
+```
+
+予め用意したホストファイルを使うこともできます。
+
+```bash
+$ mpirun -np 5 --hostfile mpi_file ./cpi
+```
+
+これでmanagerが接続しているマシンでMPIプログラムが動くはずです!
+
+## 一般的なエラーやTIPS - Common errors and tips
+
+* 実行ファイルを実行しようとしているすべてのマシンで、MPIのバージョンが同じであることを確認してください。これを読んでいる時の推奨バージョンは [MPICH2](http://www.mpich.org/downloads/)を参照してください。
+* manager の `hosts` ファイルには、 `manager` とワーカーノードのローカルネットワーク IP アドレスを記述します。ワーカには`manager`と自分自身のエントリが含まれている必要があります。
+
+例を示します。managerには以下のようなhostsが必要になります。
+
+```bash
+$ cat /etc/hosts
+127.0.0.1 localhost
+#127.0.1.1 1944
+
+#MPI CLUSTER SETUP
+172.50.88.22 manager
+172.50.88.56 worker1
+172.50.88.34 worker2
+172.50.88.54 worker3
+172.50.88.60 worker4
+172.50.88.46 worker5
+```
+このとき、worker3に必要な最低限のhostsは以下の通りです。
+
+```bash
+$ cat /etc/hosts
+127.0.0.1 localhost
+#127.0.1.1 1947
+
+#MPI CLUSTER SETUP
+172.50.88.22 manager
+172.50.88.54 worker3
+```
+* MPIを使用してプロセスを並列実行しようとする場合、ローカルだけ、ローカルノードとリモートノードの組み合わせでプロセスを実行することができます。リモートのみでプロセスを起動することは**できません**。
+
+以下の呼び出しは正常です。
+```bash
+$ mpirun -np 10 --hosts manager ./cpi
+# To run the program only on the same manager node
+```
+
+以下の呼び出しは正常です。
+
+```bash
+$ mpirun -np 10 --hosts manager,worker1,worker2 ./cpi
+# To run the program on manager and worker nodes.
+```
+
+ですが次の呼び出しをmanagerから行うことはできません(worker1からなら良いのですが)。
+
+```bash
+$ mpirun -np 10 --hosts worker1 ./cpi
+# Trying to run the program only on remote worker
+```
+
+## 次は? - So, what's next?
+
+クラスタが構築できました!わくわくしますか?ついに並列実行できるプログラムを書くための詳細を知る時が来ました。[MPI hello world lesson]({{ site.baseurl }}/tutorials/mpi-hello-world/)から始めるのが良いです。もしも、Amazon EC2インスタンスを使って同じことを再現したいのであれば、[building and running your own cluster on Amazon EC2]({{ site.baseurl }}/tutorials/launching-an-amazon-ec2-mpi-cluster/)を見てください。その他のレッスンについては、[MPIチュートリアル]({{ site.baseurl }}/tutorials/)のページを参照してください。
+
+ローカルクラスタのセットアップで何か問題があれば、遠慮なく以下にコメントしてください。
\ No newline at end of file