100% found this document useful (6 votes)
20 views

Download ebooks file Using OpenCL Programming Massively Parallel Computers J. Kowalik all chapters

Computers

Uploaded by

evtimzipzip
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (6 votes)
20 views

Download ebooks file Using OpenCL Programming Massively Parallel Computers J. Kowalik all chapters

Computers

Uploaded by

evtimzipzip
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 61

Download Full ebookname - Read Now at ebookname.

com

Using OpenCL Programming Massively Parallel


Computers J. Kowalik

https://ebookname.com/product/using-opencl-programming-
massively-parallel-computers-j-kowalik/

OR CLICK BUTTON

DOWLOAD EBOOK

Discover More Ebook - Explore Now at ebookname.com


Instant digital products (PDF, ePub, MOBI) available
Download now and explore formats that suit you...

Computers and Programming 1st Edition Lisa Mccoy

https://ebookname.com/product/computers-and-programming-1st-edition-
lisa-mccoy/

ebookname.com

Hidden Structure Music Analysis Using Computers David Cope

https://ebookname.com/product/hidden-structure-music-analysis-using-
computers-david-cope/

ebookname.com

Using Computers in Linguistics A Practical Guide 1st


Edition John M. Lawler

https://ebookname.com/product/using-computers-in-linguistics-a-
practical-guide-1st-edition-john-m-lawler/

ebookname.com

The Dragon and the Eagle The Rise and Fall of the Chinese
and Roman Empires 1st edition (April 4, 2014) Edition
Sunny Y. Auyang
https://ebookname.com/product/the-dragon-and-the-eagle-the-rise-and-
fall-of-the-chinese-and-roman-empires-1st-edition-
april-4-2014-edition-sunny-y-auyang/
ebookname.com
Family Therapy Techniques Integrating and Tailoring
Treatment 1st Edition Jon Carlson

https://ebookname.com/product/family-therapy-techniques-integrating-
and-tailoring-treatment-1st-edition-jon-carlson/

ebookname.com

Docker for Rails Developers Build Ship and Run Your


Applications Everywhere 1 edition (February 24, 2019)
Edition Rob Isenberg
https://ebookname.com/product/docker-for-rails-developers-build-ship-
and-run-your-applications-everywhere-1-edition-
february-24-2019-edition-rob-isenberg/
ebookname.com

The Physics and Technology of Ion Sources 2ed Edition


Brown I.G. (Ed.)

https://ebookname.com/product/the-physics-and-technology-of-ion-
sources-2ed-edition-brown-i-g-ed/

ebookname.com

Attosecond and XUV Spectroscopy Ultrafast Dynamics and


Spectroscopy 1st Edition Thomas Schultz

https://ebookname.com/product/attosecond-and-xuv-spectroscopy-
ultrafast-dynamics-and-spectroscopy-1st-edition-thomas-schultz/

ebookname.com

Topsy and Tim Go to the Zoo Jean Adamson

https://ebookname.com/product/topsy-and-tim-go-to-the-zoo-jean-
adamson/

ebookname.com
Elephant Sense and Sensibility 1st Edition Michael
Garstang

https://ebookname.com/product/elephant-sense-and-sensibility-1st-
edition-michael-garstang/

ebookname.com
USING OPENCL
Advances in Parallel Computing
This book series publishes research and development results on all aspects of parallel
computing. Topics may include one or more of the following: high-speed computing
architectures (Grids, clusters, Service Oriented Architectures, etc.), network technology,
performance measurement, system software, middleware, algorithm design,
development tools, software engineering, services and applications.

Series Editor:
Professor Dr. Gerhard R. Joubert

Volume 21
Recently published in this series
Vol. 20. I. Foster, W. Gentzsch, L. Grandinetti and G.R. Joubert (Eds.), High
Performance Computing: From Grids and Clouds to Exascale
Vol. 19. B. Chapman, F. Desprez, G.R. Joubert, A. Lichnewsky, F. Peters and T. Priol
(Eds.), Parallel Computing: From Multicores and GPU’s to Petascale
Vol. 18. W. Gentzsch, L. Grandinetti and G. Joubert (Eds.), High Speed and Large Scale
Scientific Computing
Vol. 17. F. Xhafa (Ed.), Parallel Programming, Models and Applications in Grid and
P2P Systems
Vol. 16. L. Grandinetti (Ed.), High Performance Computing and Grids in Action
Vol. 15. C. Bischof, M. Bücker, P. Gibbon, G.R. Joubert, T. Lippert, B. Mohr and F.
Peters (Eds.), Parallel Computing: Architectures, Algorithms and Applications

Volumes 1–14 published by Elsevier Science.

ISSN 0927-5452 (print)


ISSN 1879-808X (online)
Usin
ng OpeenCL
Program
mming Ma
assively Pa
arallel Com
mputers

Janu
usz Kow
walik
1647
77-107th PL NE, Bothell,, WA 98011
1, USA
and
Tadeusz PuĨnia
akowski
UG, MFI, Wit
W Stwosz Street
S 57, 80-952
8 GdaĔĔsk, Poland

Amstterdam x Berrlin x Tokyo x Washington, DC


© 2012 The authors and IOS Press.

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or
transmitted, in any form or by any means, without prior written permission from the publisher.

ISBN 978-1-61499-029-1 (print)


ISBN 978-1-61499-030-7 (online)
Library of Congress Control Number: 2012932792
doi:10.3233/978-1-61499-030-7-i

Publisher
IOS Press BV
Nieuwe Hemweg 6B
1013 BG Amsterdam
Netherlands
fax: +31 20 687 0019
e-mail: order@iospress.nl

Distributor in the USA and Canada


IOS Press, Inc.
4502 Rachael Manor Drive
Fairfax, VA 22032
USA
fax: +1 703 323 3668
e-mail: iosbooks@iospress.com

LEGAL NOTICE
The publisher is not responsible for the use which might be made of the following information.

PRINTED IN THE NETHERLANDS


This book is dedicated to Alex, Bogdan and Gabriela
with love and consideration.

v
vi
Preface
This book contains the most important and essential information required for de-
signing correct and efficient OpenCL programs. Some details have been omitted but
can be found in the provided references. The authors assume that readers are famil-
iar with basic concepts of parallel computation, have some programming experience
with C or C++ and have a fundamental understanding of computer architecture.
In the book, all terms, definitions and function signatures have been copied from
official API documents available on the page of the OpenCL standards creators.
The book was written in 2011, when OpenCL was in transition from its infancy
to maturity as a practical programming tool for solving real-life problems in science
and engineering. Earlier, the Khronos Group successfully defined OpenCL specifica-
tions, and several companies developed stable OpenCL implementations ready for
learning and testing. A significant contribution to programming heterogeneous com-
puters was made by NVIDIA which created one of the first working systems for pro-
gramming massively parallel computers – CUDA. OpenCL has borrowed from CUDA
several key concepts. At this time (fall 2011), one can install OpenCL on a hetero-
geneous computer and perform meaningful computing experiments. Since OpenCL
is relatively new, there are not many experienced users or sources of practical infor-
mation. One can find on the Web some helpful publications about OpenCL, but there
is still a shortage of complete descriptions of the system suitable for students and
potential users from the scientific and engineering application communities.
Chapter 1 provides short but realistic examples of codes using MPI and OpenMP
in order for readers to compare these two mature and very successful systems with
the fledgling OpenCL. MPI used for programming clusters and OpenMP for shared
memory computers, have achieved remarkable worldwide success for several rea-
sons. Both have been designed by groups of parallel computing specialists that per-
fectly understood scientific and engineering applications and software development
tools. Both MPI and OpenMP are very compact and easy to learn. Our experience
indicates that it is possible to teach scientists or students whose disciplines are other
than computer science how to use MPI and OpenMP in a several hours time. We
hope that OpenCL will benefit from this experience and achieve, in the near future,
a similar success.
Paraphrasing the wisdom of Albert Einstein, we need to simplify OpenCL as
much as possible but not more. The reader should keep in mind that OpenCL will
be evolving and that pioneer users always have to pay an additional price in terms
of initially longer program development time and suboptimal performance before
they gain experience. The goal of achieving simplicity for OpenCL programming re-
quires an additional comment. OpenCL supporting heterogeneous computing offers
us opportunities to select diverse parallel processing devices manufactured by differ-
ent vendors in order to achieve near-optimal or optimal performance. We can select
multi-core CPUs, GPUs, FPGAs and other parallel processing devices to fit the prob-
lem we want to solve. This flexibility is welcomed by many users of HPC technology,
but it has a price.
Programming heterogeneous computers is somewhat more complicated than
writing programs in conventional MPI and OpenMP. We hope this gap will disappear
as OpenCL matures and is universally used for solving large scientific and engineer-
ing problems.

vii
Acknowledgements
It is our pleasure to acknowledge assistance and contributions made by several per-
sons who helped us in writing and publishing the book.
First of all, we express our deep gratitude to Prof. Gerhard Joubert who has
accepted the book as a volume in the book series he is editing, Advances in Parallel
Computing. We are proud to have our book in his very prestigious book series.
Two members of the Khronos organization, Elizabeth Riegel and Neil Trevett,
helped us with evaluating the initial draft of Chapter 2 Fundamentals and provided
valuable feedback. We thank them for the feedback and for their offer of promoting
the book among the Khronos Group member companies.
Our thanks are due to NVIDIA for two hardware grants that enabled our com-
puting work related to the book.
Our thanks are due to Piotr Arłukowicz, who contributed two sections to the
book and helped us with editorial issues related to using LATEX and the Blender3D
modeling open-source program.
We thank two persons who helped us improve the book structure and the lan-
guage. They are Dominic Eschweiler from FIAS, Germany and Roberta Scholz from
Redmond, USA.
We also thank several friends and family members who helped us indirectly by
supporting in various ways our book writing effort.
Janusz Kowalik
Tadeusz Puźniakowski

How to read this book


The text and the source code presented in this book are written using different text
fonts. Here are some examples of diffrent typography styles collected.
variable – for example:
. . . the variable platform represents an object of class . . .
type or class name – for example:
. . . the value is always of type cl_ulong. . .
. . . is an object of class cl::Platform. . .
constant or macro – for example:
. . . the value CL_PLATFORM_EXTENSIONS means that. . .
function, method or constructor – for example:
. . . the host program has to execute clGetPlatformIDs. . .
. . . can be retrieved using cl::Platform::getInfo method. . .
. . . the context is created by cl::Context construcor. . .
file name – for example:
. . . the cl.h header file contains. . .
keyword – for example:
. . . identified by the __kernel qualifier. . .

viii
Contents

1 Introduction 1
1.1 Existing Standard Parallel Programming Systems . . . . . . . . . . . . . . 1
1.1.1 MPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 OpenMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Two Parallelization Strategies: Data Parallelism and Task Parallelism . 9
1.2.1 Data Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 Task Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.3 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3 History and Goals of OpenCL . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.1 Origins of Using GPU in General Purpose Computing . . . . . . 12
1.3.2 Short History of OpenCL . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Heterogeneous Computer Memories and Data Transfer . . . . . . . . . . 14
1.4.1 Heterogeneous Computer Memories . . . . . . . . . . . . . . . . . 14
1.4.2 Data Transfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.4.3 The Fourth Generation CUDA . . . . . . . . . . . . . . . . . . . . . 15
1.5 Host Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.5.1 Phase a. Initialization and Creating Context . . . . . . . . . . . . 17
1.5.2 Phase b. Kernel Creation, Compilation and Preparations . . . . . 17
1.5.3 Phase c. Creating Command Queues and Kernel Execution . . . 17
1.5.4 Finalization and Releasing Resource . . . . . . . . . . . . . . . . . 18
1.6 Applications of Heterogeneous Computing . . . . . . . . . . . . . . . . . . 18
1.6.1 Accelerating Scientific/Engineering Applications . . . . . . . . . 19
1.6.2 Conjugate Gradient Method . . . . . . . . . . . . . . . . . . . . . . 19
1.6.3 Jacobi Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.6.4 Power Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.6.5 Monte Carlo Methods . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.6.6 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.7 Benchmarking CGM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.7.2 Additional CGM Description . . . . . . . . . . . . . . . . . . . . . . 24
1.7.3 Heterogeneous Machine . . . . . . . . . . . . . . . . . . . . . . . . 24
1.7.4 Algorithm Implementation and Timing Results . . . . . . . . . . 24
1.7.5 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

ix
2 OpenCL Fundamentals 27
2.1 OpenCL Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.1 What is OpenCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.2 CPU + Accelerators . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.3 Massive Parallelism Idea . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.4 Work Items and Workgroups . . . . . . . . . . . . . . . . . . . . . . 29
2.1.5 OpenCL Execution Model . . . . . . . . . . . . . . . . . . . . . . . . 29
2.1.6 OpenCL Memory Structure . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.7 OpenCL C Language for Programming Kernels . . . . . . . . . . . 30
2.1.8 Queues, Events and Context . . . . . . . . . . . . . . . . . . . . . . 30
2.1.9 Host Program and Kernel . . . . . . . . . . . . . . . . . . . . . . . . 31
2.1.10 Data Parallelism in OpenCL . . . . . . . . . . . . . . . . . . . . . . 31
2.1.11 Task Parallelism in OpenCL . . . . . . . . . . . . . . . . . . . . . . 32
2.2 How to Start Using OpenCL . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.2.1 Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2.2 Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2.3 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.3 Platforms and Devices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.3.1 OpenCL Platform Properties . . . . . . . . . . . . . . . . . . . . . . 36
2.3.2 Devices Provided by Platform . . . . . . . . . . . . . . . . . . . . . 37
2.4 OpenCL Platforms – C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.5 OpenCL Context to Manage Devices . . . . . . . . . . . . . . . . . . . . . . 41
2.5.1 Different Types of Devices . . . . . . . . . . . . . . . . . . . . . . . 43
2.5.2 CPU Device Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.5.3 GPU Device Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.5.4 Accelerator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.5.5 Different Device Types – Summary . . . . . . . . . . . . . . . . . . 44
2.5.6 Context Initialization – by Device Type . . . . . . . . . . . . . . . 45
2.5.7 Context Initialization – Selecting Particular Device . . . . . . . . 46
2.5.8 Getting Information about Context . . . . . . . . . . . . . . . . . . 47
2.6 OpenCL Context to Manage Devices – C++ . . . . . . . . . . . . . . . . . 48
2.7 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.7.1 Checking Error Codes . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.7.2 Using Exceptions – Available in C++ . . . . . . . . . . . . . . . . 53
2.7.3 Using Custom Error Messages . . . . . . . . . . . . . . . . . . . . . 54
2.8 Command Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.8.1 In-order Command Queue . . . . . . . . . . . . . . . . . . . . . . . 55
2.8.2 Out-of-order Command Queue . . . . . . . . . . . . . . . . . . . . 57
2.8.3 Command Queue Control . . . . . . . . . . . . . . . . . . . . . . . 60
2.8.4 Profiling Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.8.5 Profiling Using Events – C example . . . . . . . . . . . . . . . . . . 61
2.8.6 Profiling Using Events – C++ example . . . . . . . . . . . . . . . 63
2.9 Work-Items and Work-Groups . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.9.1 Information About Index Space from a Kernel . . . . . . . . . . 66
2.9.2 NDRange Kernel Execution . . . . . . . . . . . . . . . . . . . . . . 67
2.9.3 Task Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
2.9.4 Using Work Offset . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

x
2.10 OpenCL Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.10.1 Different Memory Regions – the Kernel Perspective . . . . . . . . 71
2.10.2 Relaxed Memory Consistency . . . . . . . . . . . . . . . . . . . . . 73
2.10.3 Global and Constant Memory Allocation – Host Code . . . . . . 75
2.10.4 Memory Transfers – the Host Code . . . . . . . . . . . . . . . . . . 78
2.11 Programming and Calling Kernel . . . . . . . . . . . . . . . . . . . . . . . . 79
2.11.1 Loading and Compilation of an OpenCL Program . . . . . . . . . 81
2.11.2 Kernel Invocation and Arguments . . . . . . . . . . . . . . . . . . 88
2.11.3 Kernel Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
2.11.4 Supported Scalar Data Types . . . . . . . . . . . . . . . . . . . . . 90
2.11.5 Vector Data Types and Common Functions . . . . . . . . . . . . . 92
2.11.6 Synchronization Functions . . . . . . . . . . . . . . . . . . . . . . . 94
2.11.7 Counting Parallel Sum . . . . . . . . . . . . . . . . . . . . . . . . . 96
2.11.8 Parallel Sum – Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . 97
2.11.9 Parallel Sum – Host Program . . . . . . . . . . . . . . . . . . . . . 100
2.12 Structure of the OpenCL Host Program . . . . . . . . . . . . . . . . . . . . 103
2.12.1 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
2.12.2 Preparation of OpenCL Programs . . . . . . . . . . . . . . . . . . . 106
2.12.3 Using Binary OpenCL Programs . . . . . . . . . . . . . . . . . . . . 107
2.12.4 Computation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
2.12.5 Release of Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
2.13 Structure of OpenCL host Programs in C++ . . . . . . . . . . . . . . . . . 114
2.13.1 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
2.13.2 Preparation of OpenCL Programs . . . . . . . . . . . . . . . . . . . 115
2.13.3 Using Binary OpenCL Programs . . . . . . . . . . . . . . . . . . . . 116
2.13.4 Computation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
2.13.5 Release of Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
2.14 The SAXPY Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
2.14.1 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
2.14.2 The Example SAXPY Application – C Language . . . . . . . . . . 123
2.14.3 The example SAXPY application – C++ language . . . . . . . . 128
2.15 Step by Step Conversion of an Ordinary C Program to OpenCL . . . . . 131
2.15.1 Sequential Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
2.15.2 OpenCL Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . 132
2.15.3 Data Allocation on the Device . . . . . . . . . . . . . . . . . . . . . 134
2.15.4 Sequential Function to OpenCL Kernel . . . . . . . . . . . . . . . 135
2.15.5 Loading and Executing a Kernel . . . . . . . . . . . . . . . . . . . . 136
2.15.6 Gathering Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
2.16 Matrix by Vector Multiplication Example . . . . . . . . . . . . . . . . . . . 139
2.16.1 The Program Calculating mat r i x × vec t or . . . . . . . . . . . . 140
2.16.2 Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
2.16.3 Experiment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
2.16.4 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

3 Advanced OpenCL 147


3.1 OpenCL Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
3.1.1 Different Classes of Extensions . . . . . . . . . . . . . . . . . . . . 147

xi
3.1.2 Detecting Available Extensions from API . . . . . . . . . . . . . . 148
3.1.3 Using Runtime Extension Functions . . . . . . . . . . . . . . . . . 149
3.1.4 Using Extensions from OpenCL Program . . . . . . . . . . . . . . 153
3.2 Debugging OpenCL codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
3.2.1 Printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
3.2.2 Using GDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
3.3 Performance and Double Precision . . . . . . . . . . . . . . . . . . . . . . 162
3.3.1 Floating Point Arithmetics . . . . . . . . . . . . . . . . . . . . . . . 162
3.3.2 Arithmetics Precision – Practical Approach . . . . . . . . . . . . . 165
3.3.3 Profiling OpenCL Application . . . . . . . . . . . . . . . . . . . . . 172
3.3.4 Using the Internal Profiler . . . . . . . . . . . . . . . . . . . . . . . 173
3.3.5 Using External Profiler . . . . . . . . . . . . . . . . . . . . . . . . . 180
3.3.6 Effective Use of Memories – Memory Access Patterns . . . . . . . 183
3.3.7 Matrix Multiplication – Optimization Issues . . . . . . . . . . . . 189
3.4 OpenCL and OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
3.4.1 Extensions Used . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
3.4.2 Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
3.4.3 Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
3.4.4 Common Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
3.4.5 OpenGL Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . 198
3.4.6 OpenCL Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . 201
3.4.7 Creating Buffer for OpenGL and OpenCL . . . . . . . . . . . . . . 203
3.4.8 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
3.4.9 Generating Effect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
3.4.10 Running Kernel that Operates on Shared Buffer . . . . . . . . . . 215
3.4.11 Results Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
3.4.12 Message Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
3.4.13 Cleanup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
3.4.14 Notes and Further Reading . . . . . . . . . . . . . . . . . . . . . . . 221
3.5 Case Study – Genetic Algorithm . . . . . . . . . . . . . . . . . . . . . . . . 221
3.5.1 Historical Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
3.5.2 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
3.5.3 Genetic Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
3.5.4 Example Problem Definition . . . . . . . . . . . . . . . . . . . . . . 225
3.5.5 Genetic Algorithm Implementation Overview . . . . . . . . . . . 225
3.5.6 OpenCL Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
3.5.7 Most Important Elements of Host Code . . . . . . . . . . . . . . . 234
3.5.8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
3.5.9 Experiment Results . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

A Comparing CUDA with OpenCL 245


A.1 Introduction to CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
A.1.1 Short CUDA Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 245
A.1.2 CUDA 4.0 Release and Compatibility . . . . . . . . . . . . . . . . . 245
A.1.3 CUDA Versions and Device Capability . . . . . . . . . . . . . . . . 247
A.2 CUDA Runtime API Example . . . . . . . . . . . . . . . . . . . . . . . . . . 249
A.2.1 CUDA Program Explained . . . . . . . . . . . . . . . . . . . . . . . 251

xii
A.2.2 Blocks and Threads Indexing Formulas . . . . . . . . . . . . . . . 257
A.2.3 Runtime Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 260
A.2.4 CUDA Driver API Example . . . . . . . . . . . . . . . . . . . . . . . 262

B Theoretical Foundations of Heterogeneous Computing 269


B.1 Parallel Computer Architectures . . . . . . . . . . . . . . . . . . . . . . . . 269
B.1.1 Clusters and SMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
B.1.2 DSM and ccNUMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
B.1.3 Parallel Chip Computer . . . . . . . . . . . . . . . . . . . . . . . . . 270
B.1.4 Performance of OpenCL Programs . . . . . . . . . . . . . . . . . . 270
B.2 Combining MPI with OpenCL . . . . . . . . . . . . . . . . . . . . . . . . . . 277

C Matrix Multiplication – Algorithm and Implementation 279


C.1 Matrix Multiplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
C.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
C.2.1 OpenCL Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
C.2.2 Initialization and Setup . . . . . . . . . . . . . . . . . . . . . . . . . 280
C.2.3 Kernel Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
C.2.4 Executing Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282

D Using Examples Attached to the Book 285


D.1 Compilation and Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
D.1.1 Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
D.1.2 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

Bibliography and References 289

xiii
xiv
Chapter 1

Introduction

1.1. Existing Standard Parallel Programming Systems


The last decade of the 20th century and the first decade of the 21st century can be
called the Era of Parallel Computing. In this period of time, not only were extremely
powerful supercomputers designed and built, but two de facto standard parallel pro-
gramming systems for scientific and engineering applications were successfully intro-
duced worldwide. They are MPI (Message Passing Interface) for clusters of comput-
ers and OpenMP for shared memory multi-processors. Both systems are predecessors
of the subject of this book on OpenCL. They deserve short technical description and
discussion. This will help to see differences between the older MPI/OpenMP and the
newest OpenCL parallel programming systems.

1.1.1. MPI
MPI is a programming system but not a programming language. It is a library of func-
tions for C and subroutines for FORTRAN that are used for message communication
between parallel processes created by MPI. The message-passing computing model
(Fig. 1.1) is a collection of interconnected processes that use their local memories
exclusively. Each process has an individual address identification number called the
rank. Ranks are used for sending and receiving messages and for workload distribu-
tion. There are two kinds of messages: point-to-point messages and collective mes-
sages. Point-to-point message C functions contain several parameters: the address of
the sender, the address of the receiver, the message size and type and some additional
information. In general, message parameters describe the nature of the transmitted
data and the delivery envelope description.
The collection of processes that can exchange messages is called the communica-
tor. In the simplest case there is only one communicator and each process is assigned
to one processor. In more general settings, there are several communicators and sin-
gle processors serve several processes. A processor rank is usually an integer number
running from 0 to p-1 where p is the total number of processes. It is also possible to
number processes in a more general way than by consecutive integer numbers. For
example, their address ID can be a double number such as a point (x, y) in Carte-

1
sian space. This method of identifying processes may be very helpful in handling ma-
trix operations or partial differential equations (PDEs) in two-dimensional Cartesian
space.

Figure 1.1: The message passing model.

An example of a collective message is the broadcast message that sends data


from a single process to all other processes. Other collective messages such as Gather
and Scatter are very helpful and are often used in computational mathematics
algorithms.
For the purpose of illustrating MPI, consider a parallel computation of the SAXPY
operation. SAXPY is a linear algebra operation z = ax+y where a is a constant scalar
and x, y, z are vectors of the same size. The name SAXPY comes from Sum of ax plus
y. An example of a SAXPY operation is presented in section 2.14.
The following assumptions and notations are made:

1. The number of processes is p.


2. The vector size n is divisible by p.
3. Only a part of the code for parallel calculation of the vector z will be written.
4. All required MPI initialization instructions have been done in the front part of
the code.
5. The vectors x and y have been initialized.
6. The process ID is called my_rank from 0 to p−1. The number of vector element
pairs that must be computed by each process is: N = n/p. The communicator
is the entire system.
7. n = 10000 and p = 10 so N = 1000.

The C loop below will be executed by all processes from the process from
my_rank equal to 0 to my_rank equal to p − 1.

1 {
2 int i;
3 for (i = my_rank*N; i < (my_rank+1)*N; i++)
4 z[i]=a*x[i]+y[i];
5 }

2
For example, the process with my_rank=1 will add one thousand ele-
ments a*x[i] and y[i] from i=N =1000 to 2N –1=1999. The process with the
my_rank=p–1=9 will add elements from i=n–N =9000 to n–1=9999.
One can now appreciate the usefulness of assigning a rank ID to each process.
This simple concept makes it possible to send and receive messages and share the
workloads as illustrated above. Of course, before each process can execute its code
for computing a part of the vector z it must receive the needed data. This can be
done by one process initializing the vectors x and y and sending appropriate groups
of data to all remaining processes. The computation of SAXPY is an example of data
parallelism. Each process executes the same code on different data.
To implement function parallelism where several processes execute different pro-
grams process ranks can be used. In the SAXPY example the block of code containing
the loop will be executed by all p processes without exception. But if one process, for
example, the process rank 0, has to do something different than all others this could
be accomplished by specifying the task as follows:

1 if (my_rank == 0)
2 {execute specified here task}

This block of code will be executed only by the process with my_rank==0. In the
absence of "if (my_rank==something)" instructions, all processes will execute the
block of code {execute this task}. This technique can be used for a case where
processes have to perform several different computations required in the task-parallel
algorithm. MPI is universal. It can express every kind of algorithmic parallelism.
An important concern in parallel computing is the efficiency issue. MPI often
can be computationally efficient because all processes use only local memories. On
the other hand, processes require network communication to place data in proper
process memories in proper times. This need for moving data is called the algorithmic
synchronization, and it creates the communication overhead, impacting negatively
the parallel program performance. It might significantly damage performance if the
program sends many short messages. The communication overhead can be reduced
by grouping data for communication. By grouping data for communication created
are larger user defined data types for larger messages to be sent less frequently. The
benefit is avoiding the latency times.
On the negative side of the MPI evaluation score card is its inability for incre-
mental (part by part) conversion from a serial code to an MPI parallel code that
is algorithmically equivalent. This problem is attributed to the relatively high level
of MPI program design. To design an MPI program, one has to modifiy algorithms.
This work is done at an earlier stage of the parallel computing process than develop-
ing parallel codes. Algorithmic level modification usually can’t be done piecewise. It
has to be done all at once. In contrast, in OpenMP a programmer makes changes to
sequential codes written in C or FORTRAN.
Fig. 1.2 shows the difference. The code level modification makes possible incre-
mental conversions. In a commonly practiced conversion technique, a serial code is
converted incrementally from the most compute-intensive program parts to the least
compute intensive parts until the parallelized code runs sufficiently fast. For further
study of MPI, the book [1] is highly recommended.

3
1. Mathematical model.
2. Computational model.
3. Numerical algorithm and parallel conversion: MPI
4. Serial code and parallel modification: OpenMP
5. Computer runs.

Figure 1.2: The computer solution process. OpenMP will be discussed in the next
section.

1.1.2. OpenMP
OpenMP is a shared address space computer application programming interface
(API). It contains a number of compiler directives that instruct C/C++ or FORTRAN
“OpenMP aware” compilers to execute certain instructions in parallel and distribute
the workload among multiple parallel threads. Shared address space computers fall
into two major groups: centralized memory multiprocessors, also called Symmetric
Multi-Processors (SMP) as shown in Fig. 1.3, and Distributed Shared Memory (DSM)
multiprocessors whose common representative is the cache coherent Non Uniform
Memory Access architecture (ccNUMA) shown in Fig. 1.4.

Figure 1.3: The bus based SMP multiprocessor.

SMP computers are also called Uniform Memory Access (UMA). They were the
first commercially successful shared memory computers and they still remain popu-
lar. Their main advantage is uniform memory access. Unfortunately for large num-
bers of processors, the bus becomes overloaded and performance deteriorates. For
this reason, the bus-based SMP architectures are limited to about 32 or 64 proces-
sors. Beyond these sizes, single address memory has to be physically distributed.
Every processor has a chunk of the single address space.
Both architectures have single address space and can be programmed by using
OpenMP. OpenMP parallel programs are executed by multiple independent threads

4
Figure 1.4: ccNUMA with cache coherent interconnect.

that are streams of instructions having access to shared and private data, as shown
in Fig. 1.5.

Figure 1.5: The threads’ access to data.

The programmer can explicitly define data that are shared and data that are
private. Private data can be accessed only by the thread owning these data. The flow
of OpenMP computation is shown in Fig. 1.6. One thread, the master thread, runs
continuously from the beginning to the end of the program execution. The worker
threads are created for the duration of parallel regions and then are terminated.
When a programmer inserts a proper compiler directive, the system creates a
team of worker threads and distributes workload among them. This operation point
is called the fork. After forking, the program enters a parallel region where parallel
processing is done. After all threads finish their work, the program worker threads
cease to exist or are redeployed while the master thread continues its work. The
event of returning to the master thread is called the join. The fork and join tech-
nique may look like a simple and easy way for parallelizing sequential codes, but the

5
Figure 1.6: The fork and join OpenMP code flow.

reader should not be deceived. There are several difficulties that will be described
and discussed shortly. First of all, how to find whether several tasks can be run in
parallel?
In order for any group of computational tasks to be correctly executed in parallel,
they have to be independent. That means that the result of the computation does not
depend on the order of task execution. Formally, two programs or parts of programs
are independent if they satisfy the Bernstein conditions shown in 1.1.

I j ∩ Oi = 0
Ii ∩ Oj = 0 (1.1)
Oi ∩ O j = 0

Bernstein’s conditions for task independence.

Letters I and O signify input and output. The ∩ symbol means the intersection
of two sets that belong to task i or j. In practice, determining if some group of
tasks can be executed in parallel has to be done by the programmer and may not
be very easy. To discuss other shared memory computing issues, consider a very
simple programming task. Suppose the dot product of two large vectors x and y
whose number of components is n is calculated. The sequential computation would
be accomplished by a simple C loop

1 double dp = 0;
2 for(int i=0; i<n; i++)
3 dp += x[i]*y[i];

Inserting, in front of the above for-loop, the OpenMP directive for parallelizing
the loop and declaring shared and private variables leads to:

6
1 double dp = 0;
2 #pragma omp parallel for shared(x,y,dp) (private i)
3 for(int i=0; i<n; i++)
4 dp += x[i]*y[i];

Unfortunately, this solution would encounter a serious difficulty in computing


dp by this code. The difficulty arises because the computation of dp+=x[i]*y[i]; is
not atomic. This means that more threads than one may attempt to update the value
of dp simultaneously. If this happens, the value of dp will depend on the timing of
individual threads’ operations. This situation is called the data race. The difficulty
can be removed in two ways.
One way is by using the OpenMP critical construct #pragma omp critical in
front of the statement dp+=x[i]*y[i]; The directive critical forces threads to up-
date dp one at a time. In this way, the updating relation dp+=x[i]*y[i] becomes
atomic and is executed correctly. That means every thread executes this operation
alone and completely without interference of other threads. With this addition, the
code becomes:

1 double dp = 0;
2 #pragma omp parallel for shared(x,y,dp) private(i)
3 for(int i=0; i<n; i++){
4 #pragma omp critical
5 dp += x[i]*y[i];
6 }

In general, the block of code following the critical construct is computed by


one thread at a time. In our case, the critical block is just one line of code
dp+=x[i]*y[i];. The second way for fixing the problem is by using the clause re-
duction that ensures adding correctly all partial results of the dot product. Below is
a correct fragment of code for computing dp with the reduction clause.

1 double dp = 0;
2 #pragma omp parallel for reduction(+:dp) shared(x,y) private(i)
3 for (int i=0;i<n ;i++)
4 dp += x[i]*y[i];

Use of the critical construct amounts to serializing the computation of the critical
region, the block of code following the critical construct. For this reason, large critical
regions degrade program performance and ought to be avoided. Using the reduction
clause is more efficient and preferable. The reader may have noticed that the loop
counter variable i is declared private, so each thread updates its loop counter in-
dependently without interference. In addition to the parallelizing construct parallel
for that applies to C for-loops, there is a more general section construct that paral-
lelizes independent sections of code. The section construct makes it possible to apply
OpenMP to task parallelism where several threads can compute different sections of
a code. For computing two parallel sections, the code structure is:

7
1 #pragma omp parallel
2 {
3 #pragma omp sections
4 {
5 #pragma omp section
6 /* some program segment computation */
7 #pragma omp section
8 /* another program segment computation */
9 }
10 /* end of sections block */
11 }
12 /* end of parallel region */

OpenMP has tools that can be used by programmers for improving performance.
One of them is the clause nowait. Consider the program fragment:

1 #pragma omp parallel shared(a,b,c,d,e,f) private(i,j)


2 {
3 #pragma omp for nowait
4 for(int i=0; i<n; i++)
5 c[i] = a[i]+b[i];
6 #pragma omp for
7 for(int j=0; j<m; j++)
8 d[j] = e[j]*f[j];
9 #pragma omp barrier
10 g = func(d);
11 }
12 /* end of the parallel region; implied barrier */

In the first parallelized loop, there is the clause nowait. Since the second loop
variables do not depend on the results of the first loop, it is possible to use the clause
nowait – telling the compiler that as soon as any first loop thread finishes its work, it
can start doing the second loop work without waiting for other threads. This speeds
up computation by reducing the potential waiting time for the threads that finish
work at different times.
On the other hand, the construct #pragma omp barrier is inserted after the
second loop to make sure that the second loop is fully completed before the calcula-
tion of g being a function of d is performed after the second loop. At the barrier, all
threads computing the second loop must wait for the last thread to finish before they
proceed. In addition to the explicit barrier construct #pragma omp barrier used by
the programmer, there are also implicit barriers used by OpenMP automatically at
the end of every parallel region for the purpose of thread synchronization. To sum
up, barriers should be used sparingly and only if necessary. nowait clauses should
be used as frequently as possible, provided that their use is safe.
Finally, we have to point out that the major performance issue in numerical com-
putation is the use of cache memories. For example, in computing the matrix/vector
product c = Ab, two mathematically equivalent methods could be used. In the first
method, elements of the vector c are computed by multiplying each row of A by the
vector b, i.e., computing dot products. An inferior performance will be obtained if c
is computed as the sum of the columns of A multiplied by the elements of b. In this

8
case, the program would access the columns of A not in the way the matrix data are
stored in the main memory and transferred to caches.
For further in-depth study of OpenMP and its performance, reading [2] is highly
recommended. Of special value for parallel computing practitioners are Chapter 5
“How to get Good Performance by Using OpenMP” and Chapter 6 “Using OpenMP
in the Real World”. Chapter 6 offers advice for and against using combined MPI and
OpenMP. A chapter on combining MPI and OpenMP can also be found in [3]. Like
MPI and OpenMP, the OpenCL system is standardized. It has been designed to run
regardless of processor types, operating systems and memories. This makes OpenCL
programs highly portable, but the method of developing OpenCL codes is more com-
plicated. The OpenCL programmer has to deal with several low-level programming
issues, including memory management.

1.2. Two Parallelization Strategies: Data Parallelism


and Task Parallelism
There are two strategies for designing parallel algorithms and related codes: data
parallelism and task parallelism. This Section describes both concepts.

1.2.1. Data Parallelism


Data parallelism, also called Single Program Multiple Data (SPMD) is very common
in computational linear algebra. In a data parallel code, data structures such as ma-
trices are divided into blocks, sets of rows or columns, and a single program performs
identical operations on these partitions that contain different data. An example of
data parallelism is the matrix/vector multiplication a = Ab where every element of
a can be computed by performing the dot product of one row of A and the vector b.
Fig. 1.7 shows this data parallel concept.

Figure 1.7: Computing matrix/vector product.

1.2.2. Task Parallelism


The task parallel approach is more general. It is assumed that there are multiple
different independent tasks that can be computed in parallel. The tasks operate on

9
their own data sets. Task parallelism can be called Multiple Programs Multiple Data
(MPMD). A small-size example of a task parallel problem is shown in Fig. 1.8. The
directed graph indicates the task execution precedence. Two tasks can execute in
parallel if they are not dependent.

Figure 1.8: Task dependency graph.

In the case shown in Fig. 1.8, there are two options for executing the entire
set of tasks. Option 1. Execute tasks T1, T2 and T4 in parallel, followed by Task T3
and finally T5. Option 2. Execute tasks T1 and T2 in parallel, then T3 and T4 in
parallel and finally T5. In both cases, the total computing work is equal but the time
to solution may not be.

1.2.3. Example
Consider a problem that can be computed in both ways – via data parallelism and
task parallelism. The problem is to calculate C = A × B − (D + E) where A, B, D
and E are all square matrices of size n × n. An obvious task parallel version would
be to compute in parallel two tasks A × B and D + E and then subtract the sum
from the product. Of course, the task of computing A × B and the task of computing
the sum D + E can be calculated in a data parallel fashion. Here, there are two
levels of parallelism: the higher task level and the lower data parallel level. Similar
multilevel parallelism is common in real-world applications. Not surprisingly, there
is also for this problem a direct data parallel method based on the observation that
every element of C can be computed directly and independently from the coefficients
of A, B, D and E. This computation is shown in equation 1.2.


n−1
ci j = aik bk j − di j − ei j (1.2)
k=0

The direct computation of C.

10
The equation (1.2) means that it is possible to calculate all n2 elements of C
in parallel using the same formula. This is good news for OpenCL devices that can
handle only one kernel and related data parallel computation. Those devices will
be discussed in the chapters that follow. The matrix C can be computed using three
standard programming systems: MPI, OpenMP and OpenCL.
Using MPI, the matrix C could be partitioned into sub-matrix components and
assigned the subcomponents to processes. Every process would compute a set of
elements of C, using the expression 1.2. The main concern here is not computation
itself but the ease of assembling results and minimizing the cost of communication
while distributing data and assembling the results. A reasonable partitioning would
be dividing C by blocks of rows that can be scattered and gathered as user-defined
data types. Each process would get a set of the rows of A, D and E and the entire
matrix B. If matrix C size is n and the number of processes is p, then each process
would get n/p rows of C to compute. If a new data type is defined as n/p rows,
the data can easily be distributed by strips of rows to processes and then results can
be gathered. The suggested algorithm is a data parallel method. Data partitioning is
shown in Fig. 1.9.

Figure 1.9: Strips of data needed by a process to compute the topmost strip of C.

The data needed for each process include one strip of A, D and E and the entire
matrix B. Each process of rank 0<=rank<p computes one strip of C rows. After fin-
ishing computation, the matrix C can be assembled by the collective MPI communi-
cation function Gather. An alternative approach would be partitioning Gather into
blocks and assigning to each process computing one block of C. However, assembling
the results would be harder than in the strip partitioning case.
OpenMP would take advantage of the task parallel approach. First, the subtasks
A × B and D + E would be parallelized and computed separately, and then C = A ×
B − (D + E) would be computed, as shown in Fig. 1.10. One weakness of task parallel
programs is the efficiency loss if all parallel tasks do not represent equal workloads.
For example, in the matrix C computation, the task of computing A×B and the task of
computing D+E are not load-equal. Unequal workloads could cause some threads to
idle unless tasks are selected dynamically for execution by the scheduler. In general,
data parallel implementations are well load balanced and tend to be more efficient.
In the case of OpenCL implementation of the data parallel option for computing C, a
single kernel function would compute elements of C by the equation 1.2 where the
sum represents dot products of A × B and the remaining terms represent subtracting
D and E. If the matrix size is n = 1024, a compute device could execute over one
million work items in parallel. Additional performance gain can be achieved by using
the tiling technique [4].

11
Discovering Diverse Content Through
Random Scribd Documents
Teniendo en cuenta estas especiales circunstancias, la comparación
entre ambas mujeres daba nuevo incentivo a la lectura, y sobre el
sentimiento de indulgencia se agregaba el de la compasión con
cierto viso de cariño hacia la pobre muerta, parte de cuya herencia
era aquel libro. Es cierto que Manón expiró en un desierto, pero fué
en brazos del hombre que la amaba con todo el ardor de un alma
virgen, que la abrió una fosa regándola con sus lágrimas, y enterró
su corazón con el cuerpo de su adorada; mientras que Margarita,
pecadora como Manón y regenerada tal vez como ella, había
fallecido en medio del lujo, a juzgar por lo que yo acababa de ver, en
el lecho de su pasado, es cierto, pero también en medio del vacío
arenal de su corazón, más árido, más vasto, y mucho más horrible
que el en que fué enterrada Manón.
Algunos amigos, enterados de las últimas circunstancias de la vida
de Margarita, me contaron que a la cabecera de su cama no se
sentó ni una persona para consolarla en los dos meses largos que
duró su triste y dolorosa agonía.
Después de Manón y de Margarita mi pensamiento se dirigía a otras
que yo conocía y veía caminar alegres y contentas hacia una muerte
casi siempre igual.
¡Desgraciadas criaturas! si es delito el amarlas, es casi un deber
compadecerlas. Si compadecemos al ciego que jamás ha visto la luz
del sol, al sordo que jamás ha oído las armonías de la Naturaleza y
al mudo que jamás ha podido exhalar la voz de su alma, ¿por qué,
pues, bajo un falso pretexto de pudor, no hemos de compadecer
esta ceguera del corazón, esta sordera del alma, esta mudez de la
conciencia que vuelven loca a la infeliz que afligen, inhabilitándola
para ver el bien, sentir a Dios y hablar el casto y santo lenguaje del
amor y de la fe?
Hugo nos pintó Marion Delorme, de Musset Bernedette, Alejandro
Dumas Fernanda; los pensadores y poetas de todos los tiempos han
tributado a la desgraciada cortesana la ofrenda de su misericordia, y
ha habido grandes hombres que las han rehabilitado con su amor y
hasta con su nombre.
Mi insistencia sobre este punto es porque, entre los que van a
leerme, los puede haber resueltos a arrojar este libro, por el temor
de ver únicamente la apología del vicio y de la prostitución, y porque
tal vez la edad del autor puede contribuir a motivar tamaños recelos.
No teman los que esto supongan y continúen leyendo si ello sólo les
detiene.
Yo estoy altamente convencido de un principio, y es éste: A la mujer
que ignora el bien por falta de educación, Dios acostumbra trazarle
dos senderos que conducen únicamente a él: el dolor y el amor,
cuyo paso es bien difícil por cierto. Las que los siguen se
ensangrientan los pies y se lastiman las manos, pero al mismo
tiempo dejan en los abrojos del camino las galas del vicio, y llegan al
término con esa desnudez de que nadie se sonroja delante del
Señor.
Los que se encuentran con esas atrevidas viajeras, vienen obligados
a defenderlas, y decir a todo el mundo que las han encontrado,
puesto que éste es el modo más breve de enseñar la verdadera
senda.
Esto no quiere decir que se trate de colocar buenamente dos postes
a la entrada de la vida, con estas inscripciones: Senda del bien, y
Senda del mal, diciendo a los que se presenten: Elegid; sino que,
imitando a Jesús, debemos enseñar los atajos que conducen de la
segunda a la primera senda, a los que se dejaron seducir por la
amenidad de los alrededores, y sobre todo, se debe procurar que el
principio de estas veredas no sea muy escabroso, ni pueda
parecerles del todo impenetrable.
La maravillosa parábola del hijo pródigo preceptúa la indulgencia y el
perdón. Jesús prefería en su amor esas almas heridas por las
pasiones humanas, cuyas llagas se complacía en curar, sacando de
ellas mismas el remedio de salvación, cuando dijo a la Magdalena:
«Mucho se te ha de perdonar, porque has amado mucho». ¡Sublime
perdón que debía despertar una fe santa!
¿Y nosotros hemos de ser más severos que Jesús? ¿Por qué,
abroquelándonos en las opiniones de un mundo que petrifica su
sensibilidad para que se le crea fuerte, hemos de apartarnos de las
almas heridas que, con la sangre corrompida que de ellas mana,
arrastra la corrupción de su vida pasada? ¿Por qué hemos de
rechazar esas enfermedades sociales que sólo esperan una mano
amiga que las cure y les devuelva la paz del corazón?
A mi generación apelo, a las personas para quienes felizmente ya no
existen las teorías volterianas, a las que, como yo, creen que la
humanidad ha emprendido desde hace quince años una de sus más
atrevidas jornadas. Poseemos la ciencia del bien y del mal; y si el
mundo no se ha vuelto completamente bueno, al menos ha
mejorado en tercio y quinto.
Todos los hombres inteligentes dirígense al mismo fin, y todos los
grandes corazones se les adhieren; seamos buenos, seamos justos,
seamos veraces. El mal no es más que una vanidad; tengamos el
orgullo del bien, y sobre todo no desesperemos. No menospreciemos
a la mujer que no es madre, ni hija, ni esposa, ni hermana. No
reduzcamos el afecto al limitado círculo de la familia, ni vistamos el
egoísmo de indulgencia.
Una vez que el cielo gusta más del arrepentimiento de un pecador,
que de la oración de cien justos, procuremos que el cielo se regocije,
y busquemos en la satisfacción de hacer el bien, su propia
compensación.
Demos la limosna del perdón a las víctimas de los deseos terrenales,
a quienes salvará, tal vez, la esperanza de un más allá; y como dicen
las bondadosas ancianas cuando aconsejan un remedio casero, «si
no cura, tampoco hace daño».
Acaso alguien me tache de temerario, porque deseo obtener tan
grandes frutos del pequeño raigón que pretendo cultivar; pero yo
me cuento en el número de los que creen que lo máximo está en lo
mínimo. El niño es pequeño y encierra al hombre; el cerebro
estrecho, y abriga el pensamiento; el ojo es un punto y abarca
grandísimos espacios.
CAPÍTULO IV

A los dos días terminó la venta, que produjo ciento cincuenta mil
francos.
Dos terceras partes de la suma fueron para los acreedores, y la
familia, compuesta de una hermana y un sobrino, heredó el resto.
La hermana se quedó como quien ve visiones cuando el agente de
negocios le anunció que heredaba cincuenta mil francos. Hacía siete
años que la joven no había visto a su hermana mayor, la cual había
desaparecido de su casa un día sin que por nadie se averiguase el
menor detalle de su vida, desde el día en que se fué.
Faltóle tiempo para venir a París, y encontró su fortuna hecha y
derecha, sin querer averiguar el origen de tan inesperada riqueza.
Más tarde se me dijo que había vuelto a sus hogares con el corazón
lacerado por la muerte de la hermana, pero bastante consolada por
haber podido colocar la cantidad heredada al cuatro y medio por
ciento de interés.
Estas circunstancias, repetidas en París, población madre del
escándalo, empezaban a caer en el olvido, y ni yo mismo casi
recordaba la parte que tomé en tales sucesos, cuando otro incidente
casual me dió a conocer toda la historia de Margarita, enterándome
de tan interesantes pormenores, que me entraron deseos de
escribirla.
A los tres o cuatro días, estaban vendidos los muebles y la
habitación estaba por alquilar.
Una mañana llamaron a la puerta de mi casa. Mi portero, que hacía
las veces de criado, fué a abrir y me trajo una tarjeta, diciéndome
que la persona que se la había entregado quería hablarme.
Leí en la tarjeta estas palabras:
Armando Duval.
El nombre no me era desconocido, y en efecto, recordé el de la
primera página del volumen de Manón a Margarita.
¿Qué podía solicitar de mí la persona que había regalado el libro a
Margarita? Mandé que le hicieran pasar.
Era un joven rubio, alto, pálido, en traje de camino, que parecía no
habérselo quitado de encima desde algunos días; ni siquiera se lo
había cepillado a su llegada a París, pues venía cubierto de polvo.
El señor Duval, profundamente conmovido, no hizo ningún esfuerzo
para ocultar su emoción, y arrasados los ojos, me dijo con voz
entrecortada:
—Caballero, os suplico me perdonéis por veniros a visitar en
semejante traje. Entre jóvenes se suprimen fácilmente ciertas
formalidades. Y luego, era tan vivo el deseo por veros hoy mismo,
que ni siquiera me tomé tiempo para instalarme en la fonda, a
donde mandé mi equipaje, volando a vuestra casa, temeroso de no
encontraros a pesar de ser tan de mañana.
Rogué al señor Duval que se sirviese tomar asiento cerca de la
chimenea, lo que efectuó sacando un pañuelo con el cual ocultó su
rostro por unos momentos.
—No vais a adivinar—dijo sonriendo tristemente,—el por qué viene
este desconocido a visitaros, a tal hora con semejante traje y
llorando como un chiquillo. Me he permitido venir a pediros un gran
servicio.
—Hablad, caballero. Estoy a vuestras órdenes.
—¿Asististeis a la venta de los muebles de Margarita Gautier?
Al pronunciar este nombre, la emoción de que el joven parecía haber
triunfado, fué más poderosa que él, y tuvo que enjugar nuevas
lágrimas.
—Debo pareceros bastante ridículo—añadió;—perdonadme, amigo
mío, y creed que nunca olvidaré la paciencia con que tenéis la
bondad de atenderme.
—Caballero—repliqué,—si el servicio que según decís puedo
prestaros ha de mitigar algún tanto el dolor que hiere vuestra alma,
sepa yo en qué puedo complaceros y tened la seguridad de que me
consideraré dichoso si llego a satisfaceros.
La aflicción del señor Duval era simpática, y a pesar mío, hubiera
deseado poderle servir.
Entonces me interrogó diciendo:
—¿Habéis comprado algo en la venta de los objetos de la pobre
Margarita?
—Sí, señor; un libro.
—¿Manon Lescaut?
—Efectivamente.
—¿Lo tenéis aún?
—En mi cuarto.
La noticia pareció aliviarle de un gran peso, y me dió las gracias,
como si yo hubiese ya empezado a prestarle el servicio con tener a
mano aquel volumen.
Levantéme, entré en mi gabinete, tomé el libro y lo puse en su
mano.
—El mismo—exclamó mirando la dedicatoria de la primera página y
hojeándolo;—sí, éste es.
Dos grandes lágrimas rodaron por la superficie del libro.
—Caballero—dijo levantando la cabeza y sin tratar de ocultarme que
había llorado y estaba dispuesto a continuar:—¿os interesa mucho
este libro?
—¿Por qué, caballero?
—Porque vengo a suplicaros encarecidamente que me lo cedáis.
—Perdonad mi curiosidad—dije entonces;—pero, según eso, ¿sois
vos quién lo regaló a Margarita Gautier?
—Sí, señor.
—Recobradle, amigo mío, me alegro de ser yo quien os lo devuelva.
—Pero—prosiguió el señor Duval algo turbado,—es justo que al
menos os reembolséis lo que os costó.
—Permitidme que os lo ofrezca. El precio de un solo libro en
semejante venta es bien insignificante y ya ni siquiera lo recuerdo.
—Cien francos.
—Es verdad—dije turbándome a mi vez;—¿cómo lo sabéis?
—Es muy sencillo: yo creía estar a tiempo para la venta, y no he
podido llegar hasta hoy. Deseaba poseer un objeto cualquiera de
Margarita, y me dirigí a casa del tasador para pedirle que me dejara
ver la lista de los muebles vendidos y de los nombres de los
compradores. Vi que habíais comprado este libro y resolví suplicaros
que me lo cedieseis, aunque el precio a que lo pagasteis infundiese
en mí cierto recelo sobre la causa de vuestra adquisición.
Y así diciendo, parecía temer que yo hubiese conocido a Margarita
hasta el punto que él la conociera. Me apresuré a tranquilizarle.
—La conocí de vista—le dije;—su muerte me causó la impresión que
siempre causa a un joven la muerte de una mujer hermosa a quien
se alegraba de encontrar. Quise comprar alguna cosa al venderse
sus muebles; y me encapriché pujando sobre este libro, por el gusto
de hacer rabiar a un pobre diablo que se obstinaba en pagarlo más
caro que yo. Repítoos, pues, caballero, que el libro está a vuestra
disposición, y os ruego que lo aceptéis y no lo recibáis de mí como
yo lo recibí del tasador, pues de este modo puede ser el lazo de una
amistad que me complazco en ofreceros.
—Está bien, amigo mío—dijo Armando tendiéndome la mano y
apretando la mía.—Acepto, y creed que mi agradecimiento será
eterno.
Yo tenía grandes deseos de interrogar a Armando respecto de
Margarita, pues aquella dedicatoria del libro, su viaje y el deseo de
poseer aquel volumen aumentaban mi curiosidad; pero temí que de
mis preguntas pudiese colegir que rehusaba su dinero para tener el
derecho de inmiscuirme en sus asuntos, lo cual no entraba en mis
cálculos.
Hubiérase dicho que adivinó mi deseo, pues me dijo:
—¿Habéis leído este libro?
—Sí, señor.
—¿Qué pensasteis al ver las dos líneas que escribí en él?
—Supuse que, a vuestro modo de entender, la pobre joven a quien
regalasteis este volumen se separaba de la categoría ordinaria; pues
no quise ver en estas dos líneas un cumplimiento vulgar.
—Supusisteis bien, caballero. ¡Era un ángel! Tomad,—me dijo,—leed
esta carta.
Y me entregó un papel que parecía haber sido leído repetidas veces.
Lo abrí, y leí estas palabras:
«M¡ querido Armando: Recibí vuestra carta, gozáis de
buena salud, y doy gracias a Dios, porque os concede tal
beneficio.
«Sí, amigo mío, estoy enferma, y mi enfermedad no tiene
cura; pero el interés que os dignáis tomar por mí alivia
mucho mis sufrimientos. Sin duda no viviré el tiempo
indispensable para tener la dicha de estrechar la mano
que ha escrito la bondadosa carta que acabo de recibir, y
cuyas palabras me curarían si algo pudiese curarme. No
creo volveros a ver, pues me encuentro al borde de la
tumba, y me separa de vos una distancia incalculable.
«¡Pobre amigo mío! vuestra Margarita de otros tiempos ha
cambiado por completo, y me parece preferible que no
volváis a verla, si habéis de encontrarla tal como está.
¿Me preguntáis si os perdono? ¡oh! de todo corazón,
amigo mío, pues el mal que habéis querido hacerme no
era más que una prueba de verdadero amor. Hace un mes
que no he dejado el lecho, y me es tan cara vuestra
estimación, que, desde el instante en que nos separamos,
escribo el diario de mi vida y seguiré haciéndolo hasta que
mi mano se niegue a sostener la pluma. Si el interés que
por mí manifestáis es verdadero, Armando, os suplico que
cuando volváis, veáis a Julia Duprat, que os entregará
este diario. Por él sabréis la razón y la causa de cuanto ha
ocurrido entre nosotros. Julia es muy buena, y con
frecuencia me habla de vos. Se encontraba aquí cuando
recibí vuestra carta, y hemos llorado juntas leyéndola.
«Si hubieseis dejado de darme noticias, Julia quedaba
encargada de entregaros estos papeles a vuestra llegada
a Francia. No me lo agradezcáis. Este recuerdo diario de
los únicos momentos felices de mi vida me hace un gran
bien, y si en su lectura debéis vos hallar las excusas del
pasado, a mí me ofrece un bálsamo de consuelo
inagotable.
«Desearía dejaros algún recuerdo que os hiciese pensar
constantemente en mí, pero han embargado mis muebles,
y nada me pertenece ya.
«¿Comprendéis, amigo mío? Se acerca mi muerte, y desde
mi alcoba escucho los graves pasos del vigilante que mis
acreedores han puesto en el salón para evitar que nadie
se lleve nada. Con seguridad aguardan mi fallecimiento
para proceder a la venta de lo embargado.
«¡Oh! ¡los hombres no tienen piedad! Pero me engaño: el
justo, el inflexible, es Dios.
«Y bien, querido amigo, espero que cuando se realice la
venta, compraréis algo, pues si retirase cualquier objeto
para vos y lo supieran, serían capaces de acusaros de
sustractor de efectos embargados.
«¡Cuán triste es la vida que abandono!
«¡Qué bueno sería Dios si consintiese que nos viésemos
antes de yo expirar!
«Creo deber despedirme de vos según todas las
probabilidades, ¡adiós, pues, amigo mío! perdonadme si
no prolongo esta carta, porque los que se proponen
curarme me debilitan a fuerza de sangrías, y mi mano se
niega a seguir escribiendo.
«Margarita Gautier».
Las últimas palabras casi no podían leerse.
Devolví la carta a Armando, que sin duda acababa de leerla también
en su pensamiento, como yo en el papel, pues al tomarla exclamó:
—¡Quién diría que la que ha escrito estas líneas era una cortesana!
Y conmovido por los recuerdos, contempló por un momento el papel
y acabó por besarlo.
—¡Ah! cuando pienso—prosiguió,—que ha muerto sin que yo
pudiese volver a verla, que no la veré y que hizo por mí lo que no
hubiera hecho una hermana, no puedo perdonarme haberla dejado
morir de esta manera. ¡Muerta! ¡muerta! ¡pensando en mí,
escribiendo y pronunciando mi nombre! ¡desdichada Margarita!
Y Duval, dando rienda suelta a sus pensamientos y a sus lágrimas,
me tendió la mano y apretó la mía, continuando:
—Son muchos los que si me viesen lamentar así semejante muerte,
creeríanme un chiquillo; pero es porque ignorarían cuánto he hecho
sufrir a esta mujer, cuán cruel fuí y cuán buena y resignada fué ella.
Tuve la audacia de creer que a mí sólo me tocaba perdonar, y hoy
me considero indigno del perdón que ella me concede. ¡Oh! daría
diez años de mi vida por llorar a sus pies un solo momento.
Es casi imposible consolar un dolor que no se conoce, y sin
embargo, era tan viva la simpatía que me había inspirado aquel
joven, se me confiaba con tanta franqueza, que llegué a creer que
mis palabras no le serían indiferentes.
—¿No tenéis parientes o amigos?—le dije.—Vedles y os consolarán,
pues por mi parte sólo puedo compadeceros.
—Es cierto—dijo levantándose y paseándose agitado por la
habitación;—os molesto. Perdonadme, yo no reflexionaba que mis
penas deben importaros poco, y que os importuno por lo que no
puede o no debe inspiraros el menor interés.
—No me habéis comprendido; estoy a vuestra disposición, y sólo
deploro mi insuficiencia para calmar vuestra pena. Si mi compañía y
la de mis amigos puede distraeros, si necesitáis de mí, en cualquier
terreno que fuere, quiero que me dispenséis el placer de satisfacer
vuestros deseos.
—Perdonadme una y mil veces—me dijo;—el dolor exagera las
impresiones. Permitid que permanezca aquí algunos minutos más, el
tiempo de enjugarme los ojos, para que los bobos de la calle no
vean con curiosidad mis lágrimas. Me hacéis un gran bien dándome
este libro, y nunca sabré agradeceros tal favor. ¿Cómo pagároslo?
—Concediéndome vuestra amistad y explicándome el origen de
vuestro dolor—repuse.—¡Es tan consolador contar nuestros
sufrimientos!
—Es verdad, pero hoy no podría; siento necesidad de llorar, y mis
labios no podrían formular las palabras. Otro día os referiré tan triste
historia, y podréis apreciar cuán grandes son los motivos que tengo
para llorar su muerte. Por último—añadió pasando sus manos por los
ojos y mirándose en el espejo,—tened la bondad de decirme que no
me halláis demasiado simple, y permitidme que vuelva a visitaros.
—¡Valor, amigo mío, valor!—le dije.
Y haciendo esfuerzos inauditos para no llorar, mejor huyó que salió
de mi casa.
Desde el balcón le vi subir al carruaje que le esperaba: apenas entró
en él, se puso a llorar como un desesperado, tapándose la cara con
el pañuelo.
CAPÍTULO V

Transcurrieron muchos días sin que oyese hablar de Armando; en


cambio, se hablaba bastante de Margarita.
No sé si mis lectores se habrán fijado en ello, pero basta que se diga
una vez delante de nosotros el nombre de una persona que parecía
sernos desconocida o cuando menos indiferente, para que los
detalles vayan agrupándose lentamente en derredor del nombre; y
amigos, conocidos e indiferentes parece que no hablan entonces de
otra. Así es cómo descubrimos que esa persona había estado en
contacto con nosotros, nos damos cuenta de que la hemos visto
muchísimo sin fijarnos, y en lo que de ella se nos cuenta
encontramos coincidencias y afinidades con sucesos de nuestra
propia vida.
No es esto decir que me pasase lo mismo con respecto a Margarita,
pues yo la había visto infinitas veces, y la conocía personalmente
como conocía su modo de ser; pero había resonado tanto su nombre
en mis oídos desde aquella venta, y hallábase este nombre mezclado
con un dolor tan profundo, que mi admiración había crecido con el
aguijón de la curiosidad. Tanto era así, que desde entonces las
primeras palabras que dirigía a los amigos a quienes no había jamás
hablado de Margarita, eran siempre éstas o parecidas:
—¿Habéis conocido a una tal Margarita Gautier?
—¿La Dama de las Camelias?
—Sí.
—¡Mucho!
Estos muchos solían ir acompañados de sonrisas tan significativas
que parecían delaciones.
—Y bien, ¿qué era esa muchacha? ¿a qué...?
—Una buena muchacha.
—¿Y nada más?
—¡Puede! Aventajaba en talento, y tal vez también en corazón a
otras muchas.
—¿Sabéis alguna particularidad acerca de ella?
—Arruinó al barón de G...
—¿Qué más?
—Era la querida del viejo duque de...
—¿Estás cierto de que era su querida?
—Se dice. Por lo menos le costaba bastante dinero.
Siempre los mismos detalles a poca diferencia. No me satisfacía. Yo
hubiera querido saber algo sobre las relaciones de Margarita y
Armando.
Cierto día me encontré con uno de los que vivían en continua
intimidad con las meretrices, y le interrogué:
—¿Conocisteis a Margarita Gautier?
Contestó él mucho de costumbre.
—¿Qué clase de joven era?
—Linda y buena. Su muerte me entristeció de veras.
—¿Es verdad que tuvo un amante llamado Armando Duval?
—¿Un joven alto y rubio?
—Sí.
—Es cierto.
—¿Quién era ese Armando?
—Un buen chico, que, según parece, se comió con ella lo poco que
poseía, y tuvo necesidad de abandonarla; dícese que estaba loco por
ella.
—¿Y Margarita?
—También le amaba muchísimo, según aseguran, pero como aman
las mujeres de su clase. No se les puede pedir más de lo que
buenamente pueden dar.
—¿Qué se hizo de Armando?
—No lo sé; le conocía apenas. Vivieron cinco o seis meses juntos,
pero en el campo; y cuando ella volvió, él desapareció.
—¿No le habéis vuelto a ver desde entonces?
—No.
—Tampoco yo le había vuelto a ver; llegué al punto de presumir que
la noticia reciente del fallecimiento de Margarita había exagerado su
antiguo amor, y por lo tanto su pena al presentarse en mi casa, y
supuse que quizá se había ya olvidado de Margarita y de la promesa
que de venir a verme hiciera.
Mi suposición habría sido muy verosímil tratándose de otra persona;
pero la desesperación de Armando se había manifestado con tanta
sinceridad, que pasando de un extremo a otro, me figuré que el
dolor había degenerado en enfermedad, y que si carecía de noticias
suyas era porque estaba enfermo o quizá muerto.
¿Me interesaba espontáneamente por aquel joven? Tal vez. ¿Este
interés era hijo del egoísmo? ¡Puede! Bajo aquel dolor había
vislumbrado una tierna historia de corazón, y tal vez el anhelo de
conocerla era el único fundamento del cuidado en que el silencio de
Armando me había puesto. Viendo que Duval no venía a mi casa
resolví ir a la suya. El pretexto era bastante fácil de encontrar; pero
desgraciadamente yo no tenía su dirección, y por más que la
pregunté nadie supo dármela. Fuíme a la calle de Antín para ver al
portero de Margarita, el cual debía tener noticia de dónde vivía
Armando; pero el portero era otro, y lo ignoraba como yo. Entonces
me informé del cementerio en que fué enterrada Margarita. Averigüé
que era el de Montmartre.
El mes de abril había reaparecido con sus galas de esplendente sol y
frescas flores; las tumbas no ofrecían el aspecto doloroso y desolado
que les da el invierno; hacía ya calor bastante para que los vivos se
acordasen de los muertos y los visitasen. Fuíme, pues, al
cementerio, diciéndome: «A la simple vista de la tumba de Margarita
veré si aún vive el dolor de Armando, y quizá sabré qué se ha hecho
de él».
Cuando llegué a Montmartre pregunté al conserje si el día 22 de
febrero fué enterrado en aquel cementerio el cadáver de la que fué
Margarita Gautier.
El empleado hojeó un gran libro-registro en que están inscritos y
numerados los nombres de los que entran en aquel asilo,
contestándome que, efectivamente, el 22 de febrero se había dado
sepultura a una mujer llamada Margarita.
Roguéle que me hiciese acompañar, pues no hay medio de
orientarse sin cicerone en aquella ciudad de los muertos que tienen
sus calles como la de los vivos. El conserje llamó a uno de los
jardineros, le dió las instrucciones convenientes, y éste sin dejarle
concluir exclamó:
—¡Sí, sí, ya sé! ¡Es la tumba más fácil de distinguir!—continuó
dirigiéndose a mí.
—¿Por qué?
—Pues porque las flores que la adornan son diferentes de todas las
demás.
—¿Cuidáis vos de ellas?
—Sí, señor; y yo quisiera que todos los parientes cuidasen tanto de
los difuntos como el joven que me tiene recomendada aquélla.
Después de cruzar algunas calles, el jardinero se detuvo y dijo:
—Ésta
Y mis ojos se fijaron en un cuadro de flores que nadie hubiera
tomado por un sepulcro, a no descubrirlo una lápida de mármol
blanco grabada con el nombre de la difunta.
La piedra, colocada de pie, me recordó la Esperanza. Una verja de
hierro rodeaba el terreno comprado, y este terreno desaparecía bajo
una alfombra de camelias blancas.
—¿Qué os parece?—preguntó el jardinero.
—Precioso.
—Y cuando alguna camelia de éstas se marchita la substituyo por
otra inmediatamente. Es la orden que tengo.
—¿Y quién os la ha dado?
—Un joven que lloró mucho la primera vez que vino, un antiguo
amigo de la difunta, a lo que parece, o, mejor dicho, uno de los
amigos, pues, según se cuenta, tuvo varios. Dicen que era muy
linda. ¿La conocisteis?
—Sí.
—¿Como el otro?—dijo el jardinero, sonriendo maliciosamente.
—No, nunca le hablé.
—¿Y venís a visitarla en el cementerio? No deja de ser gracioso por
vuestra parte, pues no son muchos que digamos, los que vienen a
verla.
—Vienen muy pocos, ¿verdad?
—Nadie. A no ser el joven a que me he referido y que vino una sola
vez.
—¿Una vez nada más?
—Sí, señor.
—¿Y no ha vuelto por aquí?
—No, pero volverá a su regreso.
—¿Está, pues, viajando?
—Sí.
—¿Sabéis dónde se encuentra?
—A punto fijo, no; pero yo creo que en casa de la hermana de la
señorita Gautier.
—¿Y a qué ha ido?
—Supongo que para pedirle el permiso de exhumar el cadáver de la
difunta a fin de enterrarla en otra parte.
—¿Por qué no quiere dejarla aquí?
—Vos no ignoráis que hay gentes que tienen caprichos extraños
sobre los muertos. Lo estamos viendo diariamente. Este terreno fué
comprado por cinco años solamente, y ese joven quiere una
concesión perpetua y un terreno más vasto; esto tendrá que ser en
el cuartel nuevo.
—Y eso del cuartel nuevo, ¿qué es?
—Unos terrenos nuevos que están vendiéndose a la derecha. A ser
este cementerio dirigido siempre como ahora, no habría otro igual
en el mundo; pero aún le falta mucho para llegar a ser lo que
debiera. Y luego, abunda tanto la gente vana...
—¿Qué queréis decir?
—Es bien claro: quiero decir que hay personas que pasan por
orgullosas hasta después de muertas. Pero creo que la tal señorita
Gautier era una linda alhaja, permitidme la palabra. Ahora la pobre
ya no existe, y queda tanto de ella como de las que se dice que no
tienen por qué culparse. Pues bien; en cuanto los parientes de las
personas que están sepultadas cerca de ella han averiguado quién
era, han dado en la manía de decir que se opondrán a que se la
entierre aquí definitivamente, y que debieran destinarse terrenos
separados para esta clase de mujeres como para los pobres. ¿Dónde
se ha visto semejante extravagancia? Yo no sé qué temerán o qué
se habrán figurado esos señores acaudalados que no vienen cuatro
veces al año a visitar sus difuntos, que se traen ellos mismos las
flores ¡y ved qué flores! que consideran como un entretenimiento el
recuerdo de las personas por quienes lloran, según afirman
escribiendo en sus tumbas unas lágrimas que nunca han derramado,
y vienen a hacerse los exigentes por semejantes tonterías. En fin,
creedme, señor: yo no conocí a esta señorita ni sé lo que pudo
haber hecho, ¡pues bien! yo la quiero y cuido de ella, y la doy las
camelias tan baratas como puedo. ¡Es mi muerta favorita! ¡Qué
queréis! nosotros nos vemos obligados a querer a los muertos, pues
estamos tan ocupados con ellos que no tenemos tiempo para
acordarnos de los vivos.
Yo miraba y oía a aquel buen hombre y estoy cierto de que mis
lectores comprenderán, sin que tenga necesidad de explicárselo, la
emoción extraña que su gesto y palabras me producían.
No sé si se dió cuenta de ello, pues continuó:
—Se dice que había quienes se arruinaban por esa joven, y que tuvo
amantes que la adoraron. ¡Pues bien! cuando pienso que ninguno de
ellos viene a comprar una flor para su antigua querida, me digo que
el proceder es curioso y triste a la vez. Aunque bien mirado es de las
que no pueden quejarse: pues tiene su sepulcro, y si sólo queda un
amante que se acuerde de ella, ya lo hace por todos los demás.
Pues aquí enterramos diariamente jóvenes de la misma clase y de la
misma edad que son arrojadas a la fosa común, y creedme, señor:
se me va con sus cuerpos el corazón cuando los oigo y miro caer.
¡Pobrecitas! una vez enterradas, nadie se acuerda de ellas. No es del
todo divertido nuestro oficio, es decir, para los que tenemos un
pedazo de alma. ¿Qué queréis que os diga? A mí me hizo Dios así, y
no tiene remedio, no hay que darle vueltas, soy padre, tengo una
hija de veinte años, alta y bien formada, y cuando traen por aquí
una muerta de su edad, pienso en ella, y así sea una gran señora o
una vagabunda me entristezco y pongo malhumorado. Tal vez os
aburro con mis historias, y vos no habéis venido aquí para
escucharlas. Me han mandado que os acompañe a la tumba de la
señorita Gautier y ahí la tenéis. ¿Puedo seros útil en algo más?
—¿Las señas de la habitación de M. Armando Duval sabéis cuáles
son?—le pregunté sin contestar a sus filosofías.
—Sí, señor; vive en la calle de... o al menos, allí es donde fuí a
cobrar el valor de todas las flores que estáis viendo.
—Bien, muchas gracias, buen hombre.
Dirigí la última mirada a la florida tumba, cuyo fondo hubiera querido
penetrar a pesar mío para ver en qué se había trocado la
hermosísima criatura que del polvo había vuelto al polvo, y me alejé
triste y pensativo.
—¿Queréis ver a M. Duval?—prosiguió el jardinero que venía
siguiéndome.
—Sí.
—Es que casi aseguraría que no ha vuelto, pues de lo contrarío
hubiera ya venido aquí.
—¿Conque estáis convencido de que sigue pensando en Margarita?
—No solamente estoy convencido de ello, sino que apostaría
cualquier cosa a que su deseo por cambiarla de sepulcro es el deseo
de volverla a ver.
—¿Cómo? ¿qué decís?
—Lo que antes que nada me preguntó al venir al cementerio fué:
«¿Qué he de hacer para verla?». Esto no podía verificarse sino por
medio de un cambio de sepultura, y yo mismo le enteré de todas las
formalidades que debía llenar para conseguirlo, pues ya sabéis que
para trasladar los muertos de un sepulcro a otro es indispensable
reconocerlos y únicamente la familia puede autorizar este acto, que
debe ser presidido por un comisario, de modo que monsieur Duval
partió inmediatamente para pedir esa autorización a la hermana de
la señorita Gautier, y es de suponer que su primera visita sea para la
difunta.
Llegamos a la puerta del cementerio, di de nuevo las gracias y una
propina al jardinero, y me dirigí inmediatamente a casa de Armando.
Como aún no había vuelto, dejé mi tarjeta rogándole que viniese a
verme tan pronto como llegara, o me mandase a decir dónde y
cómo podría avistarme con él.
A la mañana siguiente recibí una carta de M. Duval en la que
anunciaba su regreso y me rogaba que pasase a su casa,
disculpándose de no venir a la mía por no permitírselo su estado.
CAPÍTULO VI

Me dirigí inmediatamente a su casa. Estaba en cama.


Alargóme una mano calenturienta.
—Parece que tenéis fiebre—le dije.
—Sí, pero no será nada; la fatiga de un viaje tan apresurado: he
aquí el origen.
—¿Acaso venís de casa de la hermana de Margarita?
—Sí; ¿quién os lo ha dicho?
—Yo lo sé; ¿y habéis obtenido lo que deseabais?
—Sí—y preguntó extrañado,—¿quién os ha informado tan bien?
—El jardinero del cementerio.
—¿Habéis visto su tumba?
Casi no me atrevía a contestarle, pues el tono con que hizo la
pregunta me revelaba que Armando seguía siendo víctima de la
emoción de que yo había sido testigo, y cuantas veces su
imaginación o las palabras de otro le recordaban tan triste pérdida,
recrudecía su pena, dejando entender que le faltaba luchar todavía
muchísimo para poder dominarla.
No contesté palabra; únicamente afirmé con un movimiento de
cabeza.
—¿Ha cuidado mucho de ella?—prosiguió Armando.
—Sí.
Dos grandes lágrimas saltaron de los ojos del enfermo que volvió la
cabeza casi ruborizado. Hice como que no había visto nada, y
procuré mudar de conversación.
—Si no me equivoco, se han pasado tres semanas desde que
partisteis—dije yo.
—Tres semanas, ni más ni menos.
—Largo ha sido el viaje.
—Bueno, es que no he viajado siempre; he estado quince días en la
cama, y esto me impidió regresar antes, pues al llegar allá, la fiebre
me dominó por completo.
—De suerte que en cuanto cedió un poco, os pusisteis otra vez en
camino.
—Si llego a seguir ocho días más en aquel país, me muero sin volver
a verla.
—Pues ahora, ya que habéis podido volver, es preciso que os
cuidéis: vuestros amigos vendrán a visitaros, y yo el primero, si no
me negáis esta satisfacción.
—Antes de dos horas pienso levantarme.
—¡Lo cual será una imprudencia!
—Es necesario.
—Pero no indispensable.
—Debo ir a ver al comisario de policía.
—¿Y por qué no confiáis a un amigo semejante diligencia, evitando
agravar vuestra enfermedad?
—Porque tal vez de esta visita depende mi curación. Es preciso que
la vea. Desde que tuve noticia de su muerte, y sobre todo desde que
vi su tumba, no vivo ni sosiego. No puedo persuadirme de que haya
muerto una mujer que dejé tan joven y tan bella. He de cerciorarme
por mis ojos. He de ver en qué ha trocado Dios un ser que he
amado tanto, y tal vez la horrible realidad desvanecerá el martirio
del recuerdo. Me acompañaréis, ¿no es verdad?
—¿Qué os dijo su hermana?
—Nada. Extrañó mucho que un particular quisiese comprar terreno
para sepultar a Margarita, y firmó desde luego la autorización que
solicitaba.
—Vamos a ver, ¿y no sería mejor aguardar vuestro completo
restablecimiento para verificar esa traslación?
—¡Oh! no me faltarán fuerzas, perded cuidado. Yo creo que me
volvería loco si cuanto antes no llevara a cabo esta resolución cuyo
cumplimiento es ya una exigencia de mi dolor. Creed que no volverá
la calma a mi corazón sino después de haber visto a Margarita. Será
el agua que apagará la sed de la fiebre que me devora, el delirio de
mis insomnios, el resultado de este delirio, todo lo que queráis; pero
aunque debiese hacerme cartujo, como M. de Rancé, después de
haber visto, veré.
—Entendido—dije a Armando,—estoy a vuestras órdenes. ¿Habéis
visto a Julia Duprat?
—¡Oh! sí, el mismo día de mi primer regreso.
—¿Y os entregó los manuscritos que Margarita le encargó para vos?
—Los tengo aquí.
Diciendo esto, Armando me indicó un rollo de papeles que guardaba
debajo de su almohada, el cual volvió a guardar en seguida.
—¡Ah!—dijo,—sé de memoria su contenido; los he estado leyendo
diez veces diarias durante tres semanas. Deseo que también los
leáis, pero más tarde, cuando esté más sosegado y pueda haceros
comprender todo lo que vale el amor que semejante confesión
manifiesta. Permitidme ahora que os pida un favor.
—¿Cuál?
—¿Tenéis un coche abajo?
—Sí.
—Pues bien. Tened la bondad de tomar mi pasaporte e ir a ver si en
el correo hay cartas que vengan dirigidas a mi nombre. Mi padre y
mi hermana me habrán escrito a París; pero como partí con tanta
precipitación, no tuve tiempo para informarme antes de emprender
mi viaje. A vuestra vuelta iremos juntos a ponernos de acuerdo con
el comisario de policía para la ceremonia de mañana.
Armando me entregó su pasaporte, y me trasladé a la calle de J. J.
Rousseau.
Había dos cartas dirigidas a M. Duval, recogílas y volví. Encontré a
Armando vestido del todo y dispuesto a salir a la calle.
Cuando le entregué las cartas dijo:
—Gracias, amigo mío—y añadió después de ver los sobres,—sí, son
de mi padre y de mi hermana. Nada de mi silencio habrán
comprendido.
Abrió las cartas, y mejor las adivinó que leyó, pues ambas estaban
escritas por sus cuatro caras, y a poco las había vuelto a doblar.
—Vámonos—dijo.—Contestaré mañana.
Fuimos a ver al comisario, a quien entregó Armando la autorización
de la hermana de Margarita.
El comisario se quedó con la carta, dando otra para el guardián del
cementerio; acordóse que el traslado tendría lugar el día siguiente, a
las diez de la mañana; y quedamos en que iríamos a buscarle unos
minutos antes, para luego dirigirnos al cementerio juntos.
Cosa rara. Yo también sentía cierta curiosidad por presenciar aquel
triste espectáculo, y confieso que pasé la noche sin dormir pensando
en ello.
A juzgar por mi impaciencia, la noche debió de ser muy larga para
Armando.
Al llegar a las nueve de la mañana a su casa, estaba horriblemente
demudado, aunque parecía tranquilo.
Recibióme sonriendo, y me tendió la mano.
Las bujías estaban gastadas hasta el cabo. Antes de salir, tomó
Armando una carta larguísima dirigida a su padre, y en la que sin
duda había consignado sus impresiones de aquella triste noche.
Treinta minutos después llegábamos a Montmartre.
Nos esperaba ya el comisario.
El brazo con que Armando se apoyaba en el mío, me comunicaba
con sus convulsiones la excitación que le dominaba. Yo le miraba de
cuando en cuando, y él, comprendiendo mis miradas, se sonreía
tristemente; pero desde que habíamos salido de su casa, no
cruzamos una sola palabra.
Antes de llegar delante del sepulcro, Armando se detuvo para
enjugar su rostro inundado en sudor.
Aproveché aquel instante para respirar, pues también tenía el
corazón comprimido.
¡Cuál será el origen del doloroso placer que nos producen
semejantes espectáculos!
Cuando llegamos, el jardinero había retirado las macetas de flores, y
la verja que cercaba la tumba había desaparecido. Dos hombres
cavaban la tierra.
El pobre Armando se apoyó contra un árbol y fijó su vidriosa mirada
allí donde los azadones abrían la tierra.
En sus ojos se hallaba concentrada toda su vida.
De pronto la punta de un azadón rechinó contra una piedra.
Armando se estremeció, retrocedió como herido por una descarga
eléctrica, y estrechó mi mano con tanta fuerza, que me hizo daño.
En seguida uno de los sepultureros tomó una ancha paleta y fué
vaciando la fosa poco a poco; después, cuando ya no quedaban más
que las piedras con que se cubre el ataúd las fué separando una por
una.
Yo seguía observando con gran cuidado todas las impresiones de mi
amigo, pues tenía el temor de que sus visibles esfuerzos para
concentrarlas precipitaran un terrible fin. Él por su parte seguía
mirando, fijos y abiertos los ojos, como si estuviese loco, y el
precipitado temblor de sus mejillas y labios demostraba lo violento
de la crisis.
En cuanto a mí, sólo puedo decir que casi me arrepentía de haber
ido al cementerio.
Cuando el ataúd quedó enteramente descubierto, el comisario dijo a
los sepultureros:
—Abrid.
Obedecieron aquellos hombres como si se tratase de la cosa más
sencilla del mundo.
La caja era de roble. Principiaron por introducir una palanqueta en la
juntura. La humedad había enmohecido los tornillos, y después de
muchos esfuerzos saltó la tapa: exhalóse un olor fétido, a pesar de
las plantas aromáticas de que estábamos rodeados.
Hasta los sepultureros apartaron la cabeza.
—¡Dios mío! ¡Dios mío!—dijo Armando y palideció más.
Un lienzo blanco cubría el cadáver, dibujando vagos contornos. El
sudario estaba carcomido en uno de sus extremos, y dejaba ver un
pie descarnado.
Confieso que sentí frío y desfallecimiento, y a la hora en que escribo
estas líneas aún me parece ver aquella escena en su imponente
realidad.
—Concluyamos—dijo el comisario.
En seguida uno de aquellos hombres alargó la mano, descosió parte
del sudario, y agarrándolo por la punta, pegó un tirón y descubrió el
rostro de la difunta.
Horrorizaba el verlo; horroriza el contarlo.
Los ojos no eran más que dos cavidades negras; los labios habían
desaparecido, y los dientes blancos estaban como unidos unos a
otros. Los largos cabellos negros y secos estaban como amasados y
pegados a las sienes velando en parte las verdosas cavidades de las
mejillas, y, sin embargo, en aquella enmohecida calavera reconocí el
rostro blanco, rosado y alegre que tantas veces había admirado.
Armando, con los ojos clavados en aquella figura, se había tapado la
boca con el pañuelo, que apretaba con sus dientes.
Yo estaba como soñando que un círculo de hierro oprimía mi cabeza,
nubláronse mis ojos, oí mil extraños zumbidos, abrí maquinalmente
un frasco que había traído a propósito y aspiré fuertemente las
esencias que contenía.
Embargado por aquella especie de sopor, creí oir al comisario que
decía al señor Duval.
—¿La reconocéis?
—Sí—contestó sordamente mi compañero.
—Pues cerrad, y trasladad—dijo el comisario.
Los sepultureros echaron otra vez el lienzo sobre el rostro de la
difunta, cerraron la caja, y tomándola cada uno por un extremo, se
dirigieron al lugar del cementerio a donde debía ser trasladada.
Armando permanecía inmóvil, clavados los ojos en aquella huesa
vacía: estaba pálido como el cadáver que acabábamos de ver... y
parecía petrificado.
En previsión de lo que iba a suceder cuando el dolor amenguase por
la ausencia del espectáculo, ya que por su violencia le sostenía como
galvanizado, me acerqué al comisario:
—¿Es indispensable la presencia de este caballero?—le pregunté
indicando a Duval.
—No, señor—contestó,—y aun os aconsejo que os le llevéis, pues
me parece que está malo.
—Vámonos—dije entonces a Armando tomándole del brazo.
—¡Cómo!—exclamó mirándome con extrañeza.
—Ya no necesitan de vos—-añadí;—debéis retiraros, amigo mío;
estáis afectado, y estas emociones os son perjudiciales.
—Tenéis razón, vámonos—contestó sin moverse.
Le cogí del brazo y me lo llevé.
Dejábase conducir como un niño, murmurando tan sólo de vez en
cuando:
—¿Habéis visto los ojos?—y lo decía volviendo la cabeza, como si
aquella visión le estuviese llamando.
Sus pasos eran irregulares; parecía que no avanzaba sino a
sacudidas; sus dientes castañeteaban, sus manos estaban heladas, y
una violenta agitación nerviosa que iba en aumento, se apoderó por
completo de su persona.
Él me respondía cuando le hablaba.
Todo lo que podía hacer se reducía a dejarse llevar.
Condújele, pues, hasta el carruaje.
Apenas entramos en él, aumentó su estremecimiento. Entonces tuvo
un verdadero ataque nervioso en medio del cual, por miedo de
asustarme, murmuraba apretándome la mano con violencia:
—No es nada, no es nada; tengo necesidad de llorar.
Y se hinchaba su pecho, y la sangre refluía en sus ojos sin que una
sola lágrima anunciase el desbordamiento de su dolor.
Le hice respirar el frasco de que me había servido. Cuando llegamos
a su casa, aún duraba su temblor convulsivo.
Le acosté, ayudado de su criado, mandé encender lumbre en su
cuarto, y fuí corriendo a buscar un médico, a quien enteré de todo
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

ebookname.com

You might also like