Add the FMV tutorial content.

The Function Multi-versioning tutorial content was provided by Victor Rodriguez.
This version has been edited and the markup fixed to comply with the
documentation repo's guidelines.

Signed-off-by: Rodrigo Caballero <rodrigo.caballero.abraham@intel.com>
This commit is contained in:
Rodrigo Caballero
2017-10-19 09:38:18 -05:00
parent 230d4d187a
commit 032b7974fe
2 changed files with 269 additions and 0 deletions
+268
View File
@@ -0,0 +1,268 @@
.. _fmv:
Use the Function Multi Version patch generator
##############################################
CPU architectures often gain interesting new instructions as they evolve but
application developers find it difficult to take advantage of those
instructions. The reluctance to lose backward-compatibility is one of the
main roadblocks slowing developers from using advancements in newer computing
architectures. :abbr:`FMV (Function multi-versioning)`, which first appeared
in GCC 4.8, is a way to have multiple implementations of a function, each
using a different architecture's specialized instruction-set extensions. GCC
6 introduces changes to FMV to make it even easier to bring architecture-
based optimizations to the application code.
In this tutorial we will use FMV on general code and on :abbr:`FFT Fast
Fourier Transform` library code. Upon completing the tutorial, you will be
able to use this technology on your code and use the libraries to deploy
architecture-based optimizations to your application code.
Install and configure a Clear Linux host on bare metal
******************************************************
First, follow our guide to :ref:`bare-metal-install`.
Once the bare metal installation and initial configuration are complete,
add the `desktop-dev` bundle to the system.
desktop-dev: contains the necessary development tools like GCC\* and Perl\*.
To install the bundles, run the following command in the :file:`$HOME`
directory:
.. code-block:: bash
sudo swupd bundle-add desktop-dev
Detect loop vectorization candidates
************************************
Now, we need to detect the loop vectorization candidates to be cloned for
multiple platforms with FMV. As an example, we will use the following
simple C code:
.. code-block:: c
:linenos:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#define MAX 1000000
int a[256], b[256], c[256];
void foo(){
int i,x;
for (x=0; x<MAX; x++){
for (i=0; i<256; i++){
a[i] = b[i] + c[i];
}
}
}
int main(){
foo();
return 0;
}
Save the example code as :file:`example.c` and build with the
following flags:
.. code-block:: bash
gcc -O3 -fopt-info-vec example.c -o example
The build generates the following output:
.. code-block:: console
example.c:11:9: note: loop vectorized
example.c:11:9: note: loop vectorized
The output shows that line 11 is a good candidate for vectorization:
.. code-block:: c
for (i=0; i<256; i++){
a[i] = b[i] + c[i];
Generate the FMV patch
**********************
To generate the FMV patch with the `make-fmv-patch`_ project, we
must clone the project and generate a log file with the loop vectorized
information:
.. code-block:: bash
git clone https://github.com/clearlinux/make-fmv-patch.git
gcc -O3 -fopt-info-vec example.c -o example &> log
To generate the patch files, execute:
.. code-block:: bash
perl ./make-fmv-patch/make-fmv-patch.pl log .
The make-fmv-patch.pl take two arguments: <buildlog> and <sourcecode>. Replace with the proper values and execute:
.. code-block:: bash
perl make-fmv-patch.pl <buildlog> <sourcecode>
The command generates the following :file:`example.c.patch` patch:
.. code-block:: console
--- ./example.c 2017-09-27 16:05:42.279505430 +0000
+++ ./example.c~ 2017-09-27 16:19:11.691544026 +0000
@@ -5,6 +5,7 @@
int a[256], b[256], c[256];
+__attribute__((target_clones("avx2","arch=atom","default")))
void foo(){
int i,x;
for (x=0; x<MAX; x++){
The `make-fmv-patch` is recommended to add the attribute generating the
target clones on the function foo. Thus, we can have the following code:
.. code-block:: c
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#define MAX 1000000
int a[256], b[256], c[256];
__attribute__((target_clones("avx2","arch=atom","default")))
void foo(){
int i,x;
for (x=0; x<MAX; x++){
for (i=0; i<256; i++){
a[i] = b[i] + c[i];
}
}
}
int main(){
foo();
return 0;
}
By changing the value of the `$avx2` variable, we can change the target clones when adding the patches or in the make-fmv-patch.pl script:
.. code-block:: perl
my $avx2 = '__attribute__((target_clones("avx2","arch=atom","default")))'."\n";
Compile the code again with FMV and add the option to analyze the `objdump`:
.. code-block:: bash
gcc -O3 example.c -o example -g
objdump -S example | less
You can see the multiple clones of the foo function:
.. code-block:: console
foo
foo.avx2.0
foo.arch_atom.1
The cloned functions use AVX2 registers and vectorized instructions. You can verify this with:
.. code-block:: assembly
vpaddd (%r8,%rax,1),%ymm0,%ymm0
vmovdqu %ymm0,(%rcx,%rax,1)
FTT project example
*******************
To follow the same approach with a package like FFT, we must get the build log file with the `-fopt-info-vec` flag:
::
~/make-fmv-patch/make-fmv-patch.pl results/build.log fftw-3.3.6-pl2/
patching fftw-3.3.6-pl2/libbench2/verify-lib.c @ lines (36 114 151 162 173 195 215 284)
patching fftw-3.3.6-pl2/tools/fftw-wisdom.c @ lines (150)
patching fftw-3.3.6-pl2/libbench2/speed.c @ lines (26)
patching fftw-3.3.6-pl2/tests/bench.c @ lines (27)
patching fftw-3.3.6-pl2/libbench2/util.c @ lines (181)
patching fftw-3.3.6-pl2/libbench2/problem.c @ lines (229)
patching fftw-3.3.6-pl2/tests/fftw-bench.c @ lines (101 147 162 249)
patching fftw-3.3.6-pl2/libbench2/mp.c @ lines (79 190 215)
patching fftw-3.3.6-pl2/libbench2/caset.c @ lines (5)
patching fftw-3.3.6-pl2/libbench2/verify-r2r.c @ lines (44 187 197 207 316 333 723)
Thus, files like :file:`fftw-3.3.6-pl2/tools/fftw-wisdom.c.patch` generate
patches like:
.. code-block:: git
1 --- fftw-3.3.6-pl2/libbench2/verify-lib.c 2017-01-27 21:08:13.000000000 +0000
2 +++ fftw-3.3.6-pl2/libbench2/verify-lib.c~ 2017-09-27 17:49:21.913802006 +0000
3 @@ -33,6 +33,7 @@
4
5 double dmax(double x, double y) { return (x > y) ? x : y; }
6
7 +__attribute__((target_clones("avx2","arch=atom","default")))
8 static double aerror(C *a, C *b, int n)
9 {
10 if (n > 0) {
11 @@ -111,6 +112,7 @@
12 }
13
14 /* make array hermitian */
15 +__attribute__((target_clones("avx2","arch=atom","default")))
16 void mkhermitian(C *A, int rank, const bench_iodim *dim, int stride)
17 {
18 if (rank == 0)
19 @@ -148,6 +150,7 @@
20 }
21
22 /* C = A + B */
23 +__attribute__((target_clones("avx2","arch=atom","default")))
24 void aadd(C *c, C *a, C *b, int n)
25 {
26 int i;
27 @@ -159,6 +162,7 @@
28 }
29
30 /* C = A - B */
31 +__attribute__((target_clones("avx2","arch=atom","default")))
32 void asub(C *c, C *a, C *b, int n)
33 {
34 int i;
35 @@ -170,6 +174,7 @@
36 }
37
38 /* B = rotate left A (complex) */
39 +__attribute__((target_clones("avx2","arch=atom","default")))
40 void arol(C *b, C *a, int n, int nb, int na)
41 {
42 int i, ib, ia;
43 @@ -192,6 +197,7 @@
44 }
45 }
With these patches, we can select where to apply the FMV technology making
bringing architecture-based optimizations to application code even easier.
**Congratulations! **
You have successfully installed an FMV development environment on Clear
Linux. Furthermore, you used cutting edge compiler technology to improve the
performance of your application based on Intel Architecture technology and
profiling of the specific execution of your application.
.. _make-fmv-patch: https://github.com/clearlinux/make-fmv-patch
@@ -14,3 +14,4 @@ specific |CLOSIA| use cases.
machine-learning/machine-learning
azure
multi-boot/multi-boot
fmv