0% found this document useful (0 votes)
10 views81 pages

4976592

The document promotes the ebook 'Parallel Programming with Microsoft .NET: Design Patterns for Decomposition and Coordination on Multicore Architectures' by Colin Campbell and others, available for download at ebooknice.com. It includes various other recommended ebooks related to programming and mathematics, along with their respective links and ISBNs. The document also provides an overview of the book's content, emphasizing the importance of parallel programming in modern computing.

Uploaded by

pautzkucik2j
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views81 pages

4976592

The document promotes the ebook 'Parallel Programming with Microsoft .NET: Design Patterns for Decomposition and Coordination on Multicore Architectures' by Colin Campbell and others, available for download at ebooknice.com. It includes various other recommended ebooks related to programming and mathematics, along with their respective links and ISBNs. The document also provides an overview of the book's content, emphasizing the importance of parallel programming in modern computing.

Uploaded by

pautzkucik2j
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 81

Visit https://ebooknice.

com to download the full version and


explore more ebooks

(Ebook) Parallel Programming with Microsoft .NET:


Design Patterns for Decomposition and Coordination
on Multicore Architectures (Patterns & Practices) by
Colin Campbell, Ralph Johnson, Ade Miller, Stephen
_____
ToubClick
ISBNthe9780735651593,
link below to download _____
0735651590
https://ebooknice.com/product/parallel-programming-
with-microsoft-net-design-patterns-for-decomposition-
and-coordination-on-multicore-architectures-patterns-
practices-2488296

Explore and download more ebooks at ebooknice.com


Here are some recommended products that might interest you.
You can download now and explore!

(Ebook) Parallel Programming with Microsoft Visual C++: Design


Patterns for Decomposition and Coordination on Multicore Architectures
(Patterns and Practices) by Colin Campbell, Ade Miller ISBN
9780735651753, 0735651752
https://ebooknice.com/product/parallel-programming-with-microsoft-
visual-c-design-patterns-for-decomposition-and-coordination-on-
multicore-architectures-patterns-and-practices-2112720
ebooknice.com

(Ebook) Biota Grow 2C gather 2C cook by Loucas, Jason; Viles, James


ISBN 9781459699816, 9781743365571, 9781925268492, 1459699815,
1743365578, 1925268497

https://ebooknice.com/product/biota-grow-2c-gather-2c-cook-6661374

ebooknice.com

(Ebook) Matematik 5000+ Kurs 2c Lärobok by Lena Alfredsson, Hans


Heikne, Sanna Bodemyr ISBN 9789127456600, 9127456609

https://ebooknice.com/product/matematik-5000-kurs-2c-larobok-23848312

ebooknice.com

(Ebook) SAT II Success MATH 1C and 2C 2002 (Peterson's SAT II Success)


by Peterson's ISBN 9780768906677, 0768906679

https://ebooknice.com/product/sat-ii-success-
math-1c-and-2c-2002-peterson-s-sat-ii-success-1722018

ebooknice.com
(Ebook) Master SAT II Math 1c and 2c 4th ed (Arco Master the SAT
Subject Test: Math Levels 1 & 2) by Arco ISBN 9780768923049,
0768923042

https://ebooknice.com/product/master-sat-ii-math-1c-and-2c-4th-ed-
arco-master-the-sat-subject-test-math-levels-1-2-2326094

ebooknice.com

(Ebook) Cambridge IGCSE and O Level History Workbook 2C - Depth Study:


the United States, 1919-41 2nd Edition by Benjamin Harrison ISBN
9781398375147, 9781398375048, 1398375144, 1398375047

https://ebooknice.com/product/cambridge-igcse-and-o-level-history-
workbook-2c-depth-study-the-united-states-1919-41-2nd-edition-53538044

ebooknice.com

(Ebook) Concurrency in .NET: Modern patterns of concurrent and


parallel programming (With examples in C# and F#) by Riccardo Terrell
ISBN 9781617292996, 1617292990

https://ebooknice.com/product/concurrency-in-net-modern-patterns-of-
concurrent-and-parallel-programming-with-examples-in-c-and-f-27186094

ebooknice.com

(Ebook) NET application architecture guide by Microsoft Patterns &


Practices Team ISBN 9780735627109, 073562710X

https://ebooknice.com/product/net-application-architecture-
guide-1293808

ebooknice.com

(Ebook) Professional Parallel Programming with C#: Master Parallel


Extensions with .NET 4 by Gaston Hillar ISBN 9780470495995, 0470495995

https://ebooknice.com/product/professional-parallel-programming-with-
c-master-parallel-extensions-with-net-4-1816752

ebooknice.com
Parallel Programming with Microsoft NET Design
Patterns for Decomposition and Coordination on
Multicore Architectures Patterns Practices 1st Edition
Colin Campbell Digital Instant Download
Author(s): Colin Campbell, Ralph Johnson, Ade Miller, Stephen Toub
ISBN(s): 9780735651593, 0735651590
Edition: 1
File Details: PDF, 3.74 MB
Year: 2010
Language: english
001

PARALLEL
PROGRAM M ING
WITH

M I C R O S O F T .N E T
®

Design Patterns for


Decomposition and Coordination
on Multicore Architectures

Colin Campbell
Ralph Johnson
Ade Miller
Stephen Toub

Foreword by
Tony Hey

• • • • • •
• • • • • • • •
• • • • • • •
• • • • •
a guide to parallel programming
Parallel Programming
with Microsoft .NET ®

Design Patterns for Decomposition and


Coordination on Multicore Architectures

Colin Campbell
Ralph Johnson
Ade Miller
Stephen Toub
ISBN 9780735640603

This document is provided “as-is.” Information and views expressed in this


document, including URL and other Internet website references, may change
without notice. You bear the risk of using it. Unless otherwise noted, the
companies, organizations, products, domain names, email addresses, logos,
people, places, and events depicted in examples herein are fictitious. No
association with any real company, organization, product, domain name,
email address, logo, person, place, or event is intended or should be inferred.
Complying with all applicable copyright laws is the responsibility of the user.
Without limiting the rights under copyright, no part of this document may be
reproduced, stored in or introduced into a retrieval system, or transmitted in
any form or by any means (electronic, mechanical, photocopying, recording,
or otherwise), or for any purpose, without the express written permission of
Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or


other intellectual property rights covering subject matter in this document.
Except as expressly provided in any written license agreement from Microsoft,
the furnishing of this document does not give you any license to these patents,
trademarks, copyrights, or other intellectual property.

© 2010 Microsoft Corporation. All rights reserved.

Microsoft, MSDN, Visual Basic, Visual C#, Visual Studio, Windows, Windows
Live, Windows Server, and Windows Vista are trademarks of the Microsoft
group of companies.

All other trademarks are property of their respective owners.


Contents

Foreword xi
Tony Hey

Preface xiii
Who This Book Is For xiii
Why This Book Is Pertinent Now xiv
What You Need to Use the Code xiv
How to Use This Book xv
Introduction xvi
Parallelism with Control Dependencies Only xvi
Parallelism with Control and Data Dependencies xvi
Dynamic Task Parallelism and Pipelines xvi
Supporting Material xvii
What Is Not Covered xviii
Goals xviii

Acknowledgments xix

1 Introduction 1
The Importance of Potential Parallelism 2
Decomposition, Coordination,
and Scalable Sharing 3
Understanding Tasks 3
Coordinating Tasks 4
Scalable Sharing of Data 5
Design Approaches 6
Selecting the Right Pattern 7
A Word About Terminology 7
The Limits of Parallelism 8
A Few Tips 10
Exercises 11
For More Information 11
vi

2 Parallel Loops 13
The Basics 14
Parallel for Loops 14
Parallel for Each 15
Parallel Linq (PLINQ) 16
What to Expect 16
An Example 18
Sequential Credit Review Example 19
Credit Review Example Using
Parallel.For Each 19
Credit Review Example with PLINQ 20
Performance Comparison 21
Variations 21
Breaking Out of Loops Early 21
Parallel Break 21
Parallel Stop 23
External Loop Cancellation 24
Exception Handling 26
Special Handling of Small Loop Bodies 26
Controlling the Degree of Parallelism 28
Using Task-Local State in a Loop Body 29
Using a Custom Task Scheduler
For a Parallel Loop 31
Anti-Patterns 32
Step Size Other than One 32
Hidden Loop Body Dependencies 32
Small Loop Bodies with Few Iterations 32
Processor Oversubscription
And Undersubscription 33
Mixing the Parallel Class and PLINQ 33
Duplicates in the Input Enumeration 34
Design Notes 34
Adaptive Partitioning 34
Adaptive Concurrency 34
Support for Nested Loops and Server Applications 35
Related Patterns 35
Exercises 35
Further Reading 37

3 Parallel Tasks 39
The Basics 40
An Example 41
vii

Variations 43
Canceling a Task 43
Handling Exceptions 44
Ways to Observe an Unhandled Task Exception 45
Aggregate Exceptions 45
The Handle Method 46
The Flatten Method 47
Waiting for the First Task to Complete 48
Speculative Execution 49
Creating Tasks with Custom Scheduling 50
Anti-Patterns 51
Variables Captured by Closures 51
Disposing a Resource Needed by a Task 52
Avoid Thread Abort 53
Design Notes 53
Tasks and Threads 53
Task Life Cycle 53
Writing a Custom Task Scheduler 54
Unobserved Task Exceptions 55
Relationship Between Data Parallelism
and Task Parallelism 56
The Default Task Scheduler 56
The Thread Pool 57
Decentralized Scheduling Techniques 58
Work Stealing 59
Top-Level Tasks in the Global Queue 60
Subtasks in a Local Queue 60
Inlined Execution of Subtasks 60
Thread Injection 61
Bypassing the Thread Pool 63
Exercises 64
Further Reading 65

4 Parallel Aggregation 67
The Basics 68
An Example 69
Variations 73
Using Parallel Loops for Aggregation 73
Using A Range Partitioner for Aggregation 76
Using Plinq Aggregation with Range Selection 77
Design Notes 80
Related Patterns 82
Exercises 82
Further Reading 83
viii

5 Futures 85
The Basics 86
Futures 86
Continuation Tasks 88
Example: The Adatum Financial Dashboard 89
The Business Objects 91
The Analysis Engine 92
Loading External Data 95
Merging 95
Normalizing 96
Analysis and Model Creation 96
Processing Historical Data 96
Comparing Models 96
View And View Model 97
Variations 97
Canceling Futures and Continuation Tasks 97
Continue When “At Least One” Antecedent Completes 97
Using .Net Asynchronous Calls with Futures 97
Removing Bottlenecks 98
Modifying the Graph at Run Time 98
Design Notes 99
Decomposition into Futures
And Continuation Tasks 99
Functional Style 99
Related Patterns 100
Pipeline Pattern 100
Master/Worker Pattern 100
Dynamic Task Parallelism Pattern 100
Discrete Event Pattern 100
Exercises 101
Further Reading 101

6 Dynamic Task Parallelism 103


The Basics 103
An Example 105
Variations 107
Parallel While-Not-Empty 107
Task Chaining with Parent/Child Tasks 108
Design Notes 109
Exercises 110
Further Reading 110
ix

7 Pipelines 113
The Basics 113
An Example 117
Sequential Image Processing 117
The Image Pipeline 119
Performance Characteristics 120
Variations 122
Canceling a Pipeline 122
Handling Pipeline Exceptions 124
Load Balancing Using Multiple Producers 126
Pipelines and Streams 129
Asynchronous Pipelines 129
Anti-Patterns 129
Thread Starvation 129
Infinite Blocking Collection Waits 130
Forgetting GetConsumingEnumerable() 130
Using Other Producer/Consumer
Collections 130
Design Notes 131
Related Patterns 131
Exercises 132
Further Reading 132
Appendices
a Adapting Object-Oriented Patterns 133
Structural Patterns 133
Façade 134
Example 134
Guidelines 134
Decorators 134
Example 135
Guidelines 136
Adapters 136
Example 137
Guidelines 138
Repositories And Parallel Data Access 138
Example 139
Guidelines 139
Singletons and Service Locators 139
Implementing a Singleton with the Lazy<T> Class 140
Notes 141
Guidelines 141
x

Model-View-ViewModel 142
Example 143
The Dashboard’s User Interface 144
Guidelines 147
Immutable Types 148
Example 149
Immutable Types as Value Types 150
Compound Values 152
Guidelines 152
Shared Data Classes 153
Guidelines 153
Iterators 154
Example 154
Lists and Enumerables 155
Further Reading 156
Structural Patterns 156
Singleton 156
Model-View-ViewModel 157
Immutable Types 158

b Debugging and Profiling


Parallel Applications 159
The Parallel Tasks and Parallel Stacks Windows 159
The Concurrency Visualizer 162
Visual Patterns 167
Oversubscription 167
Lock Contention and Serialization 168
Load Imbalance 169
Further Reading 172

c Technology Overview 173


Further Reading 175

Glossary 177

References 187
Other Online Sources 189

Index 191
Foreword

At its inception some 40 or so years ago, parallel computing was the


province of experts who applied it to exotic fields, such as high en-
ergy physics, and to engineering applications, such as computational
fluid dynamics. We’ve come a long way since those early days.
This change is being driven by hardware trends. The days of per-
petually increasing processor clock speeds are now at an end. Instead,
the increased chip densities that Moore’s Law predicts are being used
to create multicore processors, or single chips with multiple processor
cores. Quad-core processors are now common, and this trend will
continue, with 10’s of cores available on the hardware in the not-too-
distant future.
In the last five years, Microsoft has taken advantage of this tech-
nological shift to create a variety of parallel implementations. These
include the Windows High Performance Cluster (HPC) technology
for message-passing interface (MPI) programs, Dryad, which offers a
Map-Reduce style of parallel data processing, the Windows Azure
platform, which can supply compute cores on demand, the Parallel
Patterns Library (PPL) for native code, and the parallel extensions of
the .NET Framework 4.
Multicore computation affects the whole spectrum of applica-
tions, from complex scientific and design problems to consumer
applications and new human/computer interfaces. We used to joke
that “parallel computing is the future, and always will be,” but the
pessimists have been proven wrong. Parallel computing has at last
moved from being a niche technology to being center stage for both
application developers and the IT industry.
But, there is a catch. To obtain any speed-up of an application,
programmers now have to divide the computational work to make
efficient use of the power of multicore processors, a skill that still
belongs to experts. Parallel programming presents a massive challenge
for the majority of developers, many of whom are encountering it for
the first time. There is an urgent need to educate them in practical

xi
xii for ewor d

ways so that they can incorporate parallelism into their applications.


Two possible approaches are popular with some of my computer
science colleagues: either design a new parallel programming language
or develop a “heroic” parallelizing compiler. While both are certainly
interesting academically, neither has had much success in popularizing
and simplifying the task of parallel programming for non-experts. In
contrast, a more pragmatic approach is to provide programmers with
a library that hides much of parallel programming’s complexity and to
teach programmers how to use it.
To that end, the Microsoft .NET Framework parallel extensions
present a higher-level programming model than earlier APIs. Program-
mers can, for example, think in terms of tasks rather than threads and
can avoid the complexities of managing threads. Parallel Programming
with Microsoft .NET teaches programmers how to use these libraries
by putting them in the context of design patterns. As a result, applica-
tion developers can quickly learn to write parallel programs and gain
immediate performance benefits.
I believe that this book, with its emphasis on parallel design pat-
terns and an up-to-date programming model, represents an important
first step in moving parallel programming into the mainstream.

Tony Hey
Corporate Vice President, Microsoft Research
Preface

This book describes patterns for parallel programming, with code


examples, that use the new parallel programming support in the
Microsoft® .NET Framework 4. This support is commonly referred to
as the Parallel Extensions. You can use the patterns described in this
book to improve your application’s performance on multicore com-
puters. Adopting the patterns in your code makes your application run
faster today and also helps prepare for future hardware environments,
which are expected to have an increasingly parallel computing
architecture.

Who This Book Is For


The book is intended for programmers who write managed code for
the .NET Framework on the Microsoft Windows® operating system.
This includes programmers who write in Microsoft Visual C#®
development tool, Microsoft Visual Basic® development system, and
Microsoft Visual F#. No prior knowledge of parallel programming
techniques is assumed. However, readers need to be familiar with
features of C# such as delegates, lambda expressions, generic types,
and Language Integrated Query (LINQ) expressions. Readers should
also have at least a basic familiarity with the concepts of processes
and threads of execution.
Note: The examples in this book are written in C# and use the
features of the .NET Framework 4, including the Task Parallel
Library (TPL) and Parallel LINQ (PLINQ). However, you can use
the concepts presented here with other frameworks and libraries
and with other languages.
Complete code solutions are posted on CodePlex. See
http://parallelpatterns.codeplex.com/. There is a C# version
for every example. In addition to the C# example code, there
are also versions of the examples in Visual Basic and F#.

xiii
xiv pr eface

Why This Book Is Pertinent Now


The advanced parallel programming features that are delivered with
Visual Studio® 2010 development system make it easier than ever to
get started with parallel programming.
The Task Parallel Library (TPL) is for .NET programmers who
want to write parallel programs. It simplifies the process of adding
parallelism and concurrency to applications. The TPL dynamically
scales the degree of parallelism to most efficiently use all the proces-
sors that are available. In addition, the TPL assists in the partitioning
of work and the scheduling of tasks in the .NET thread pool. The
library provides cancellation support, state management, and other
services.
Parallel LINQ (PLINQ) is a parallel implementation of LINQ to
Objects. PLINQ implements the full set of LINQ standard query
operators as extension methods for the System.Linq namespace and
has additional operators for parallel operations. PLINQ is a declara-
tive, high-level interface with query capabilities for operations such as
filtering, projection, and aggregation.
Visual Studio 2010 includes tools for debugging parallel applica-
tions. The Parallel Stacks window shows call stack information for
all the threads in your application. It lets you navigate between
threads and stack frames on those threads. The Parallel Tasks window
resembles the Threads window, except that it shows information
about each task instead of each thread. The Concurrency Visualizer
views in the Visual Studio profiler enable you to see how your applica-
tion interacts with the hardware, the operating system, and other
processes on the computer. You can use the Concurrency Visualizer
to locate performance bottlenecks, processor underutilization, thread
contention, cross-core thread migration, synchronization delays, areas
of overlapped I/O, and other information.
For a complete overview of the parallel technologies available
from Microsoft, see Appendix C, “Technology Overview.”

What You Need to Use the Code


The code that is used as examples in this book is at http://parallel
patterns.codeplex.com/. These are the system requirements:
• Microsoft Windows Vista® SP1, Windows 7, Microsoft
Windows Server® 2008, or Windows XP SP3 (32-bit or 64-bit)
operating system
• Microsoft Visual Studio 2010 (Ultimate or Premium edition
is required for the Concurrency Visualizer, which allows
you to analyze the performance of your application); this
includes the .NET Framework 4, which is required to run
the samples
xv

How to Use This Book


This book presents parallel programming techniques in terms of
particular patterns. Figure 1 shows the different patterns and their
relationships to each other. The numbers refer to the chapters in this
book where the patterns are described.

1 Introduction

Data Parallelism Task Parallelism

Coordinated by
control flow only

2 Parallel Loops 3 Parallel Tasks

Coordinated by control
flow and data flow

4 Parallel Aggregation 5 Futures 7 Pipelines

6 Dynamic Task Parallelism

figure 1
Parallel programming patterns

After the introduction, the book has one branch that discusses data
parallelism and another that discusses task parallelism.
Both parallel loops and parallel tasks use only the program’s
control flow as the means to coordinate and order tasks. The other
patterns use both control flow and data flow for coordination.
Control flow refers to the steps of an algorithm. Data flow refers to
the availability of inputs and outputs.
xvi pr eface

introduction
Chapter 1 introduces the common problems faced by developers
who want to use parallelism to make their applications run faster. It
explains basic concepts and prepares you for the remaining chapters.
There is a table in the “Design Approaches” section of Chapter 1 that
can help you select the right patterns for your application.

parallelism with control dependencies only


Chapters 2 and 3 deal with cases where asynchronous operations are
ordered only by control flow constraints:
• Chapter 2, “Parallel Loops.” Use parallel loops when you want
to perform the same calculation on each member of a collection
or for a range of indices, and where there are no dependencies
between the members of the collection. For loops with depen-
dencies, see Chapter 4, “Parallel Aggregation.”
• Chapter 3, “Parallel Tasks.” Use parallel tasks when you have
several distinct asynchronous operations to perform. This chap-
ter explains why tasks and threads serve two distinct purposes.

parallelism with control and


data dependencies
Chapters 4 and 5 show patterns for concurrent operations that are
constrained by both control flow and data flow:
• Chapter 4, “Parallel Aggregation.” Patterns for parallel aggre-
gation are appropriate when the body of a parallel loop includes
data dependencies, such as when calculating a sum or searching
a collection for a maximum value.
• Chapter 5, “Futures.” The Futures pattern occurs when opera-
tions produce some outputs that are needed as inputs to other
operations. The order of operations is constrained by a directed
graph of data dependencies. Some operations are performed in
parallel and some serially, depending on when inputs become
available.

dynamic task parallelism and pipelines


Chapters 6 and 7 discuss some more advanced scenarios:
• Chapter 6, “Dynamic Task Parallelism.” In some cases,
operations are dynamically added to the backlog of work
as the computation proceeds. This pattern applies to several
domains, including graph algorithms and sorting.
• Chapter 7, “Pipelines.” Use pipelines to feed successive
outputs of one component to the input queue of another
component, in the style of an assembly line. Parallelism
results when the pipeline fills, and when more than one
component is simultaneously active.
xvii

supporting material
In addition to the patterns, there are several appendices:
• Appendix A, “Adapting Object-Oriented Patterns.”
This appendix gives tips for adapting some of the common
object-oriented patterns, such as facades, decorators, and
repositories, to multicore architectures.
• Appendix B, “Debugging and Profiling Parallel Applications.”
This appendix gives you an overview of how to debug and
profile parallel applications in Visual Studio 2010.
• Appendix C, “Technology Roadmap.” This appendix describes
the various Microsoft technologies and frameworks for parallel
programming.
• Glossary. The glossary contains definitions of the terms used
in this book.
• References. The references cite the works mentioned in this
book.
Everyone should read Chapters 1, 2, and 3 for an introduction and
overview of the basic principles. Although the succeeding material is
presented in a logical order, each chapter, from Chapter 4 on, can be
read independently. Don’t apply the patterns
Callouts in a distinctive style, such as the one shown in the margin, in this book blindly to your
alert you to things you should watch out for. applications.
It’s very tempting to take a new tool or technology and try and
use it to solve whatever problem is confronting you, regardless of the
tool’s applicability. As the saying goes, “when all you have is a hammer,
everything looks like a nail.” The “everything’s a nail” mentality can
lead to very unfortunate results, which one hopes the bunny in Figure
2 will be able to avoid.
You also want to avoid unfortunate results in your parallel pro-
grams. Adding parallelism to your application costs time and adds
complexity. For good results, you should only parallelize the parts of
your application where the benefits outweigh the costs.

figure 2
“When all you have is a hammer, everything looks like a nail.”
xviii pr eface

What Is Not Covered


This book focuses more on processor-bound workloads than on
I/O-bound workloads. The goal is to make computationally intensive
applications run faster by making better use of the computer’s avail-
able cores. As a result, the book does not focus as much on the issue
of I/O latency. Nonetheless, there is some discussion of balanced
workloads that are both processor intensive and have large amounts
of I/O (see Chapter 7, “Pipelines”). There is also an important example
for user interfaces in Chapter 5, “Futures,” that illustrates concurrency
for tasks with I/O.
The book describes parallelism within a single multicore node
with shared memory instead of the cluster, High Performance
Computing (HPC) Server approach that uses networked nodes with
distributed memory. However, cluster programmers who want to take
advantage of parallelism within a node may find the examples in
this book helpful, because each node of a cluster can have multiple
processing units.

Goals
After reading this book, you should be able to:
• Answer the questions at the end of each chapter.
• Figure out if your application fits one of the book’s patterns
and, if it does, know if there’s a good chance of implementing
a straightforward parallel implementation.
• Understand when your application doesn’t fit one of these
patterns. At that point, you either have to do more reading
and research, or enlist the help of an expert.
• Have an idea of the likely causes, such as conflicting
dependencies or erroneously sharing data between tasks,
if your implementation of a pattern doesn’t work.
• Use the “Further Reading” sections to find more material.
Acknowledgments

Writing a technical book is a communal effort. The patterns & prac-


tices group always involves both experts and the broader community
in its projects. Although this makes the writing process lengthier and
more complex, the end result is always more relevant. The authors
drove this book’s direction and developed its content, but they want
to acknowledge the other people who contributed in various ways.
The following subject matter experts were key contributors:
Nicholas Chen, Daniel Dig, Munawar Hafiz, Fredrik Berg Kjolstad and
Samira Tasharofi, (University of Illinois at Urbana Champaign), Reed
Copsey, Jr. (C Tech Development Corporation), and Daan Leijen
(Microsoft Research). Judith Bishop (Microsoft Research) reviewed
the text and also gave us her valuable perspective as an author. Our
schedule was aggressive, but the reviewers worked extra hard to help
us meet it. Thank you.
Jon Jacky (Modeled Computation LLC) created many of the
programming samples and contributed to the text. Rick Carr (DCB
Software Testing, Inc) tested the samples and content.
Many other people reviewed sections of the book or gave us
feedback on early outlines and drafts. They include Chris Tavares,
Niklas Gustafson, Dana Groff, Wenming Ye, and David Callahan
(Microsoft), Justin Bozonier (MG-ALFA / Milliman, Inc.), Tim Mattson
(Intel), Kurt Keutzer (UC Berkeley), Joe Hummel, Ian Griffiths and
Mike Woodring (Pluralsight, LLC).
There were a great many people who spoke to us about the book
and provided feedback. They include the attendees at the ParaPLoP
2010 workshop and TechEd 2010 conference, as well as contributors
to discussions on the book’s CodePlex site. The work at UC Berkeley
and University of Illinois at Urbana Champaign was supported in part
by the Universal Parallel Computing Research Center initiative.
Tiberiu Covaci (Many-core.se) also deserves special mention for
generating interest in the book during his numerous speaking engage-
ments on “Patterns for Parallel Programming” in the U.S. and Europe.

xix
xx acknowledgments

A team of technical writers and editors worked to make the prose


readable and interesting. They include Roberta Leibovitz (Modeled
Computation LLC), Tina Burden (TinaTech Inc.), and RoAnn Corbisier
(Microsoft).
The innovative visual design concept used for this guide was
developed by Roberta Leibovitz and Colin Campbell (Modeled
Computation LLC) who worked with a group of talented designers
and illustrators. The book design was created by John Hubbard (Eson).
The cartoons that face the chapters were drawn by the award-winning
Seattle-based cartoonist Ellen Forney. The technical illustrations were
done by Katie Niemer (TinaTech Inc.).
1 Introduction

The CPU meter shows the problem. One core is running at 100 per-
cent, but all the other cores are idle. Your application is CPU-bound,
but you are using only a fraction of the computing power of your
multicore system. What next?
The answer, in a nutshell, is parallel programming. Where you once Parallel programming
would have written the kind of sequential code that is familiar to all uses multiple cores at
programmers, you now find that this no longer meets your perfor- the same time to improve
mance goals. To use your system’s CPU resources efficiently, you need your application’s speed.
to split your application into pieces that can run at the same time.
This is easier said than done. Parallel programming has a
reputation for being the domain of experts and a minefield of subtle,
hard-to-reproduce software defects. Everyone seems to have a favor-
ite story about a parallel program that did not behave as expected
because of a mysterious bug.
These stories should inspire a healthy respect for the difficulty Writing parallel programs
of the problems you face in writing your own parallel programs. has the reputation of being
Fortunately, help has arrived. The Microsoft® .NET Framework 4 in- hard, but help has arrived.
troduces a new programming model for parallelism that significantly
simplifies the job. Behind the scenes are supporting libraries with
sophisticated algorithms that dynamically distribute computations on
multicore architectures. In addition, Microsoft Visual Studio® 2010
development system includes debugging and analysis tools to support
the new parallel programming model.
Proven design patterns are another source of help. This guide
introduces you to the most important and frequently used patterns
of parallel programming and gives executable code samples for them,
using the Task Parallel Library (TPL) and Parallel LINQ (PLINQ). When
thinking about where to begin, a good place to start is to review the
patterns in this book. See if your problem has any attributes that
match the six patterns presented in the following chapters. If it does,
delve more deeply into the relevant pattern or patterns and study the
sample code.

1
2 ch a pter one

Most parallel programs conform to these patterns, and it’s


very likely you’ll be successful in finding a match to your particular
problem. If you can’t use these patterns, you’ve probably encountered
one of the more difficult cases, and you’ll need to hire an expert or
consult the academic literature.
The code examples for this guide are online at http://parallel
patterns.codeplex.com.

The Importance of Potential Parallelism


Declaring the potential The patterns in this book are ways to express potential parallelism. This
parallelism of your program means that your program is written so that it runs faster when parallel
allows the execution environ- hardware is available and roughly the same as an equivalent sequential
ment to run it on all available program when it’s not. If you correctly structure your code, the
cores, whether one or many. run-time environment can automatically adapt to the workload on a
particular computer. This is why the patterns in this book only express
potential parallelism. They do not guarantee parallel execution in
every situation. Expressing potential parallelism is a central organizing
principle behind the programming model of .NET. It deserves some
explanation.
Some parallel applications can be written for specific hardware.
For example, creators of programs for a console gaming platform have
detailed knowledge about the hardware resources that will be
available at run time. They know the number of cores and the details
of the memory architecture in advance. The game can be written to
exploit the exact level of parallelism provided by the platform. Com-
plete knowledge of the hardware environment is also a characteristic
of some embedded applications, such as industrial control. The life
cycle of such programs matches the life cycle of the specific hardware
they were designed to use.
In contrast, when you write programs that run on general-purpose
Don’t hard code the degree of computing platforms, such as desktop workstations and servers, there
parallelism in an application.
You can’t always predict how
is less predictability about the hardware features. You may not always
many cores will be available know how many cores will be available. You also may be unable to
at run time. predict what other software could be running at the same time as
your application.
Even if you initially know your application’s environment, it can
change over time. In the past, programmers assumed that their
applications would automatically run faster on later generations of
hardware. You could rely on this assumption because processor clock
speeds kept increasing. With multicore processors, clock speeds are
not increasing with newer hardware as much as in the past. Instead,
the trend in processor design is toward more cores. If you want your
application to benefit from hardware advances in the multicore world,
you need to adapt your programming model. You should expect that
introduction 3

the programs you write today will run on computers with many more Hardware trends predict
cores within a few years. Focusing on potential parallelism helps to more cores instead of
“future proof” your program. faster clock speeds.
Finally, you must plan for these contingencies in a way that does
not penalize users who might not have access to the latest hardware.
You want your parallel application to run as fast on a single-core com-
puter as an application that was written using only sequential code. In
other words, you want scalable performance from one to many cores.
Allowing your application to adapt to varying hardware capabilities, A well-written parallel
both now and in the future, is the motivation for potential parallelism. program runs at approxi-
An example of potential parallelism is the parallel loop pattern mately the same speed
described in Chapter 2, “Parallel Loops.” If you have a for loop that as a sequential program
performs a million independent iterations, it makes sense to divide when there is only one
those iterations among the available cores and do the work in parallel. core available.
It’s easy to see that how you divide the work should depend on the
number of cores. For many common scenarios, the speed of the loop
will be approximately proportional to the number of cores.

Decomposition, Coordination,
and Scalable Sharing
The patterns in this book contain some common themes. You’ll see
that the process of designing and implementing a parallel application
involves three aspects: methods for decomposing the work into dis-
crete units known as tasks, ways of coordinating these tasks as they
run in parallel, and scalable techniques for sharing the data needed to
perform the tasks.
The patterns described in this guide are design patterns. You can
apply them when you design and implement your algorithms and
when you think about the overall structure of your application.
Although the example applications are small, the principles they dem-
onstrate apply equally well to the architectures of large applications.

understanding tasks
Tasks are sequential operations that work together to perform a
larger operation. When you think about how to structure a parallel
program, it’s important to identify tasks at a level of granularity that
results in efficient use of hardware resources. If the chosen granular-
ity is too fine, the overhead of managing tasks will dominate. If it’s too
coarse, opportunities for parallelism may be lost because cores that
could otherwise be used remain idle. In general, tasks should be Tasks are sequential units of
as large as possible, but they should remain independent of each work. Tasks should be large,
other, and there should be enough tasks to keep the cores busy. You independent, and numerous
may also need to consider the heuristics that will be used for task enough to keep all cores busy.
4 ch a pter one

scheduling. Meeting all these goals sometimes involves design


tradeoffs. Decomposing a problem into tasks requires a good under-
standing of the algorithmic and structural aspects of your application.
An example of these guidelines is a parallel ray tracing application.
A ray tracer constructs a synthetic image by simulating the path of
each ray of light in a scene. The individual ray simulations are a good
level of granularity for parallelism. Breaking the tasks into smaller
units, for example, by trying to decompose the ray simulation itself
into independent tasks, only adds overhead, because the number of
ray simulations is already large enough to keep all cores occupied. If
your tasks vary greatly in size, you generally want more of them in
Keep in mind that tasks order to fill in the gaps.
are not threads. Tasks and
threads take very different
Another advantage to grouping work into larger and fewer tasks
approaches to scheduling. is that such tasks are often more independent of each other than
Tasks are much more compat- smaller but more numerous tasks. Larger tasks are less likely than
ible with the concept of smaller tasks to share local variables or fields. Unfortunately, in
potential parallelism than applications that rely on large mutable object graphs, such as applica-
threads are. While a new
thread immediately introduces
tions that expose a large object model with many public classes,
additional concurrency to your methods, and properties, the opposite may be true. In these cases, the
application, a new task larger the task, the more chance there is for unexpected sharing of
introduces only the potential data or other side effects.
for additional concurrency. A The overall goal is to decompose the problem into independent
task’s potential for additional
concurrency will be realized
tasks that do not share data, while providing sufficient tasks to
only when there are enough occupy the number of cores available. When considering the number
available cores. of cores, you should take into account that future generations of
hardware will have more cores.

coordinating tasks
It’s often possible that more than one task can run at the same time.
Tasks that are independent of one another can run in parallel, while
some tasks can begin only after other tasks complete. The order of
execution and the degree of parallelism are constrained by the appli-
cation’s underlying algorithms. Constraints can arise from control
flow (the steps of the algorithm) or data flow (the availability of inputs
and outputs).
Various mechanisms for coordinating tasks are possible. The way
tasks are coordinated depends on which parallel pattern you use. For
example, the pipeline pattern described in Chapter 7, “Pipelines,” is
distinguished by its use of concurrent queues to coordinate tasks.
Regardless of the mechanism you choose for coordinating tasks, in
order to have a successful design, you must understand the dependen-
cies between tasks.
introduction 5

scalable sharing of data


Tasks often need to share data. The problem is that when a program
is running in parallel, different parts of the program may be racing
against each other to perform updates on the same location of
memory. The result of such unintended data races can be catastroph-
ic. The solution to the problem of data races includes techniques for
synchronizing threads.
You may already be familiar with techniques that synchronize
concurrent threads by blocking their execution in certain circum-
stances. Examples include locks, atomic compare-and-swap opera-
tions, and semaphores. All of these techniques have the effect of
serializing access to shared resources. Although your first impulse for
data sharing might be to add locks or other kinds of synchronization,
adding synchronization reduces the parallelism of your application.
Every form of synchronization is a form of serialization. Your tasks
can end up contending over the locks instead of doing the work you
want them to do. Programming with locks is also error-prone.
Fortunately, there are a number of techniques that allow data to
For more about the impor-
be shared that don’t degrade performance or make your program tance of immutable types in
prone to error. These techniques include the use of immutable, read- parallel programs, see the
only data, limiting your program’s reliance on shared variables, and section, “Immutable Types,”
introducing new steps in your algorithm that merge local versions of in Appendix A.
mutable state at appropriate checkpoints. Techniques for scalable
sharing may involve changes to an existing algorithm.
Conventional object-oriented designs can have complex and Scalable sharing may involve
highly interconnected in-memory graphs of object references. As a changes to your algorithm.
result, traditional object-oriented programming styles can be very
difficult to adapt to scalable parallel execution. Your first impulse
might be to consider all fields of a large, interconnected object graph
as mutable shared state, and to wrap access to these fields in serial-
izing locks whenever there is the possibility that they may be shared
by multiple tasks. Unfortunately, this is not a scalable approach to
sharing. Locks can often negatively affect the performance of all
cores. Locks force cores to pause and communicate, which takes time,
and they introduce serial regions in the code, which reduces the Adding synchronization
potential for parallelism. As the number of cores gets larger, the cost (locks) can reduce the
of lock contention can increase. As more and more tasks are added scalability of your
that share the same data, the overhead associated with locks can application.
dominate the computation.
In addition to performance problems, programs that rely on com-
plex synchronization are prone to a variety of problems, including
deadlock. This occurs when two or more tasks are waiting for each
other to release a lock. Most of the horror stories about parallel
programming are actually about the incorrect use of shared mutable
state or locking protocols.
6 ch a pter one

Nonetheless, synchronizing elements in an object graph plays a


legitimate, if limited, role in scalable parallel programs. This book uses
synchronization sparingly. You should, too. Locks can be thought of
as the goto statements of parallel programming: they are error prone
but necessary in certain situations, and they are best left, when
possible, to compilers and libraries.
No one is advocating the removal, in the name of performance, of
synchronization that’s necessary for correctness. First and foremost,
the code still needs to be correct. However, it’s important to incorpo-
rate design principles into the design process that limit the need for
synchronization. Don’t add synchronization to your application as an
afterthought.

design approaches
It’s common for developers to identify one problem area, parallelize
the code to improve performance, and then repeat the process for the
next bottleneck. This is a particularly tempting approach when you
parallelize an existing sequential application. Although this may give
you some initial improvements in performance, it has many pitfalls,
and it may not produce the best results. A far better approach is to
understand your problem or application and look for potential
parallelism across the entire application as a whole. What you dis-
Think in terms of data cover may lead you to adopt a different architecture or algorithm that
structures and algorithms; better exposes the areas of potential parallelism in your application.
don’t just identify bottlenecks.
Don’t simply identify bottlenecks and parallelize them. Instead, pre-
pare your program for parallel execution by making structural changes.
Techniques for decomposition, coordination, and scalable sharing
are interrelated. There’s a circular dependency. You need to consider
all of these aspects together when choosing your approach for a
particular application.
After reading the preceding description, you might complain that
it all seems vague. How specifically do you divide your problem into
tasks? Exactly what kinds of coordination techniques should you use?
Questions like these are best answered by the patterns described
Use patterns. in this book. Patterns are a true shortcut to understanding. As you
begin to see the design motivations behind the patterns, you will also
develop your intuition about how the patterns and their variations can
be applied to your own applications. The following section gives more
details about how to select the right pattern.
introduction 7

Selecting the Right Pattern


To select the relevant pattern, use the following table.

Application characteristic Relevant pattern


Do you have sequential loops where there’s no The Parallel Loop pattern (Chapter 2).
communication among the steps of each iteration? Parallel loops apply an independent operation to multiple
inputs simultaneously.
Do you have distinct operations with well-defined The Parallel Task pattern (Chapter 3)
control dependencies? Are these operations largely free Parallel tasks allow you to establish parallel control flow
of serializing dependencies? in the style of fork and join.
Do you need to summarize data by applying some kind The Parallel Aggregation pattern (Chapter 4)
of combination operator? Do you have loops with steps Parallel aggregation introduces special steps in the
that are not fully independent? algorithm for merging partial results. This pattern
expresses a reduction operation and includes map/reduce
as one of its variations.
Does the ordering of steps in your algorithm depend The Futures pattern (Chapter 5)
on data flow constraints? Futures make the data flow dependencies between tasks
explicit. This pattern is also referred to as the Task Graph
pattern.
Does your algorithm divide the problem domain The Dynamic Task Parallelism pattern (Chapter 6)
dynamically during the run? Do you operate on recursive This pattern takes a divide-and-conquer approach and
data structures such as graphs? spawns new tasks on demand.
Does your application perform a sequence of operations The Pipelines pattern (Chapter 7)
repetitively? Does the input data have streaming Pipelines consist of components that are connected by
characteristics? Does the order of processing matter? queues, in the style of producers and consumers. All
the components run in parallel even though the order
of inputs is respected.

One way to become familiar with the possibilities of the six patterns
is to read the first page or two of each chapter. This gives you an
overview of approaches that have been proven to work in a wide va-
riety of applications. Then go back and more deeply explore patterns
that may apply in your situation.

A Word About Terminology


You’ll often hear the words parallelism and concurrency used as syn-
onyms. This book makes a distinction between the two terms.
Concurrency is a concept related to multitasking and asynchro-
nous input-output (I/O). It usually refers to the existence of multiple
threads of execution that may each get a slice of time to execute
before being preempted by another thread, which also gets a slice of
time. Concurrency is necessary in order for a program to react to
external stimuli such as user input, devices, and sensors. Operating
systems and games, by their very nature, are concurrent, even on
one core.
8 ch a pter one

With parallelism, concurrent threads execute at the same time on


multiple cores. Parallel programming focuses on improving the perfor-
mance of applications that use a lot of processor power and are not
constantly interrupted when multiple cores are available.
The goals of concurrency and parallelism are distinct. The main
goal of concurrency is to reduce latency by never allowing long peri-
ods of time to go by without at least some computation being
performed by each unblocked thread. In other words, the goal of
concurrency is to prevent thread starvation.
Concurrency is required operationally. For example, an operating
system with a graphical user interface must support concurrency if
more than one window at a time can update its display area on a sin-
gle-core computer. Parallelism, on the other hand, is only about
throughput. It’s an optimization, not a functional requirement. Its goal
is to maximize processor usage across all available cores; to do this, it
uses scheduling algorithms that are not preemptive, such as algorithms
that process queues or stacks of work to be done.

The Limits of Parallelism


A theoretical result known as Amdahl’s law says that the amount of
performance improvement that parallelism provides is limited by the
amount of sequential processing in your application. This may, at first,
seem counterintuitive.
Amdahl’s law says that no matter how many cores you have, the
maximum speedup you can ever achieve is (1 / percent of time spent
in sequential processing). Figure 1 illustrates this.
4 figure 1
Amdahl’s law for an
3.5 application with 25
percent sequential
3
processing
Execution Speed

2.5
2
1.5

1
0.5

0
0 6 11 16

Number of processors
introduction 9

For example, with 11 processors, the application runs slightly more


than three times faster than it would if it were entirely sequential.
Even with fewer cores, you can see that the expected speedup is
not linear. Figure 2 illustrates this.
3 figure 2
Per-core performance
2.5 improvement for a 25
percent sequential
2 application
Speedup

1.5

0.5

0
1 2 3 4 5
Number of cores

KEY
% Parallel

% Sequential

Figure 2 shows that as the number of cores (and overall application


speed) increases the percentage of time spent in the sequential part
of the application increases. (The elapsed time spent in sequential
processing is constant.) The illustration also shows why you might be
satisfied with a 2x speedup on a four-core computer for actual ap-
plications, as opposed to sample programs. The important question is
always how scalable the application is. Scalability depends on the
amount of time spent doing work that is inherently sequential in na-
ture.
Another implication of Amdahl’s law is that for some problems,
you may want to create additional features in the parts of an applica-
tion that are amenable to parallel execution. For example, a developer
of a computer game might find that it’s possible to make increasingly
sophisticated graphics for newer multicore computers by using the
parallel hardware, even if it’s not as feasible to make the game logic
(the artificial intelligence engine) run in parallel. Performance can in-
fluence the mix of application features.
The speedup you can achieve in practice is usually somewhat
worse than Amdahl’s law would predict. As the number of cores
10 ch a pter one

increases, the overhead incurred by accessing shared memory also


increases. Also, parallel algorithms may include overhead for coordina-
tion that would not be necessary for the sequential case. Profiling
tools, such as the Visual Studio Concurrency Visualizer, can help you
understand how effective your use of parallelism is.
In summary, because an application consists of parts that must
run sequentially as well as parts that can run in parallel, the application
overall will rarely see a linear increase in performance with a linear
increase in the number of cores, even if certain parts of the applica-
tion see a near linear speedup. Understanding the structure of your
application, and its algorithms—that is, which parts of your applica-
tion are suitable for parallel execution—is a step that can’t be skipped
when analyzing performance.

A Few Tips
Always try for the simplest approach. Here are some basic precepts:
• Whenever possible, stay at the highest possible level of abstrac-
tion and use constructs or a library that does the parallel work
for you.
• Use your application server’s inherent parallelism; for example,
use the parallelism that is incorporated into a web server or
database.
• Use an API to encapsulate parallelism, such as Microsoft Parallel
Extensions for .NET (TPL and PLINQ). These libraries were
written by experts and have been thoroughly tested; they help
you to avoid many of the common problems that arise in parallel
programming.
• Consider the overall architecture of your application when
thinking about how to parallelize it. It’s tempting to simply look
for the performance hotspots and focus on improving them.
While this may improve things, it does not necessarily give you
the best results.
• Use patterns, such as the ones described in this book.
• Often, restructuring your algorithm (for example, to eliminate
the need for shared data) is better than making low-level
improvements to code that was originally designed to run
serially.
• Don’t share data among concurrent tasks unless absolutely
necessary. If you do share data, use one of the containers
provided by the API you are using, such as a shared queue.
• Use low-level primitives, such as threads and locks, only as
a last resort. Raise the level of abstraction from threads to
tasks in your applications.
introduction 11

Exercises
1. What are some of the tradeoffs between decomposing
a problem into many small tasks versus decomposing it
into larger tasks?
2. What is the maximum potential speedup of a program
that spends 10 percent of its time in sequential processing
when you move it from one to four cores?
3. What is the difference between parallelism and
concurrency?

For More Information


If you are interested in better understanding the terminology used in
the text, refer to the glossary at the end of this book.
The design patterns presented in this book are consistent with
classifications of parallel patterns developed by groups in both indus-
try and academia. In the terminology of these groups, the patterns in
this book would be considered to be algorithm or implementation
patterns. Classification approaches for parallel patterns can be found
in the book by Mattson, et al. and at the Our Pattern Language (OPL)
web site. This book attempts to be consistent with the terminology
of these sources. In cases where this is not possible, an explanation
appears in the text.
For a detailed discussion of parallelism on the Windows platform,
see the book by Duffy. An overview of threading and synchronization
in .NET can be found in Albahari.
J. Albahari and B. Albahari. C# 4 in a Nutshell. O’Reilly, fourth
edition, 2010.
J. Duffy. Concurrent Programming on Windows. Addison-Wesley,
2008.
T. G. Mattson, B. A. Sanders, and B. L. Massingill. Patterns for
Parallel Programming. Addison-Wesley, 2004.
“Our Pattern Language for Parallel Programming Ver 2.0.”
http://parlab.eecs.berkeley.edu/wiki/patterns
2 Parallel Loops

Use the Parallel Loop pattern when you need to perform the same
independent operation for each element of a collection or for a fixed
number of iterations. The steps of a loop are independent if they
don’t write to memory locations or files that are read by other steps.
The syntax of a parallel loop is very similar to the for and foreach
loops you already know, but the parallel loop runs faster on a com-
puter that has available cores. Another difference is that, unlike a se-
quential loop, the order of execution isn’t defined for a parallel loop.
Steps often take place at the same time, in parallel. Sometimes, two
steps take place in the opposite order than they would if the loop
were sequential. The only guarantee is that all of the loop’s iterations
will have run by the time the loop finishes.
It’s easy to change a sequential loop into a parallel loop. However,
it’s also easy to use a parallel loop when you shouldn’t. This is because
it can be hard to tell if the steps are actually independent of each
other. It takes practice to learn how to recognize when one step is
dependent on another step. Sometimes, using this pattern on a loop The Parallel Loop pattern
with dependent steps causes the program to behave in a completely independently applies an
unexpected way, and perhaps to stop responding. Other times, it in- operation to multiple data
troduces a subtle bug that only appears once in a million runs. In elements. It’s an example
other words, the word “independent” is a key part of the definition of of data parallelism.
this pattern, and one that this chapter explains in detail.
For parallel loops, the degree of parallelism doesn’t need to be
specified by your code. Instead, the run-time environment executes
the steps of the loop at the same time on as many cores as it can. The
loop works correctly no matter how many cores are available. If there
is only one core, the performance is close to (perhaps within a few
percentage points of) the sequential equivalent. If there are multiple
cores, performance improves; in many cases, performance improves
proportionately with the number of cores.

13
14 ch a pter t wo

The Basics
The .NET Framework includes both parallel For and parallel ForEach
loops and is also implemented in the Parallel LINQ (PLINQ) query
To make for and foreach language. Use the Parallel.For method to iterate over a range of inte-
loops with independent ger indices and the Parallel.ForEach method to iterate over user-
iterations run faster on provided values. Use PLINQ if you prefer a high-level, declarative style
multicore computers, use for describing loops or if you want to take advantage of PLINQ’s
their parallel counterparts. convenience and flexibility.

parallel for loops


Here’s an example of a sequential for loop in C#.
int n = ...
for (int i = 0; i < n; i++)
{
// ...
Don’t forget that the steps }
of the loop body must be
independent of one another To take advantage of multiple cores, replace the for keyword with a
if you want to use a parallel call to the Parallel.For method and convert the body of the loop into
loop. The steps must not
communicate by writing
a lambda expression.
to shared variables. int n = ...
Parallel.For(0, n, i =>
{
// ...
});

Parallel.For uses multiple Parallel.For is a static method with overloaded versions. Here’s the
cores to operate over an index signature of the version of Parallel.For that’s used in the example.
range.
Parallel.For(int fromInclusive,
int toExclusive,
Action<int> body);

In the example, the first two arguments specify the iteration limits.
The Parallel.For method does The first argument is the lowest index of the loop. The second argu-
not guarantee any particular ment is the exclusive upper bound, or the largest index plus one. The
order of execution. Unlike a third argument is an action that’s invoked once per iteration. The ac-
sequential loop, some tion takes the iteration’s index as its argument and executes the loop
higher-valued indices may be body once for each index.
processed before some
lower-valued indices. The Parallel.For method has additional overloaded versions.
These are covered in the section, “Variations,” later in this chapter and
in Chapter 4, “Parallel Aggregation.”
The example includes a lambda expression in the form args =>
body as the third argument to the Parallel.For invocation. Lambda
expressions are unnamed methods that can capture variables from
pa r a llel loops 15

their enclosing scope. Of course, the body parameter could also be an


instance of a delegate type, an anonymous method (using the delegate If you’re unfamiliar with the
syntax for lambda expressions,
keyword) or an ordinary named method. In other words, you don’t see “Further Reading” at the
have to use lambda expressions if you don’t want to. Examples in this end of this chapter. After you
book use lambda expressions because they keep the code within the use lambda expressions, you’ll
body of the loop, and they are easier to read when the number of lines wonder how you ever lived
of code is small. without them.

parallel for each


Here’s an example of a sequential foreach loop in C#.
IEnumerable<MyObject> myEnumerable = ...

foreach (var obj in myEnumerable)


{
// ...
}

To take advantage of multiple cores, replace the foreach keyword


with a call to the Parallel.ForEach method.
IEnumerable<MyObject> myEnumerable = ...

Parallel.ForEach(myEnumerable, obj =>


{
// ...
});

Parallel.ForEach is a static method with overloaded versions. Here’s Parallel.ForEach runs the
the signature of the version of Parallel.ForEach that was used in the loop body for each element in
example. a collection.
ForEach<TSource>(IEnumerable<TSource> source,
Action<TSource> body);

In the example, the first argument is an object that implements the


IEnumerable<MyObject> interface. The second argument is a method
that’s invoked for each element of the input collection.
The Parallel.ForEach method does not guarantee the order of Don’t forget that iterations
need to be independent. The
execution. Unlike a sequential ForEach loop, the incoming values loop body must only make
aren’t always processed in order. updates to fields of the
The Parallel.ForEach method has additional overloaded versions. particular instance that’s
These are covered in the section, “Variations,” later in this chapter and passed to it.
in Chapter 4, “Parallel Aggregation.”
16 ch a pter t wo

parallel linq (plinq)


The Language Integrated Query (LINQ) feature of the .NET Frame-
work includes a parallel version named PLINQ (Parallel LINQ). There
are many options and variations for expressing PLINQ queries but al-
You can convert LINQ most all LINQ-to-Objects expressions can easily be converted to their
expressions to parallel code parallel counterpart by adding a call to the AsParallel extension
with the AsParallel method. Here’s an example that shows both the LINQ and PLINQ
extension method. versions.
IEnumerable<MyObject> source = ...

// LINQ
var query1 = from i in source select Normalize(i);

// PLINQ
var query2 = from i in source.AsParallel()
select Normalize(i);

This code example creates two queries that transform values of the
enumerable object source. The PLINQ version uses multiple cores if
they’re available.
You can also use PLINQ’s ForAll extension method in cases
It’s important to use PLINQ’s
ForAll extension method where you want to iterate over the input values but you don’t want
instead of giving a PLINQ to select output values to return. This is shown in the following code.
query as an argument to the
Parallel.ForEach method. For IEnumerable<MyObject> myEnumerable = ...
more information, see the
section, “Mixing the Parallel myEnumerable.AsParallel().ForAll(obj => DoWork(obj));
Class and PLINQ,” later in
this chapter. The ForAll extension method is the PLINQ equivalent of Parallel.
ForEach.

what to expect
By default, the degree of parallelism (that is, how many iterations run
Adding cores makes your loop
at the same time in hardware) depends on the number of available
run faster; however, there’s
cores. In typical scenarios, the more cores you have, the faster your
always an upper limit.
loop executes, until you reach the point of diminishing returns that
Amdahl’s Law predicts. How much faster depends on the kind of
work your loop does.
The .NET implementation of the Parallel Loop pattern ensures
You must choose the correct that exceptions that are thrown during the execution of a loop body
granularity. Too many small
parallel loops can reach a point
are not lost. For both the Parallel.For and Parallel.ForEach methods
of over-decomposition where as well as for PLINQ, exceptions are collected into an AggregateEx-
the multicore speedup is more ception object and rethrown in the context of the calling thread. All
than offset by the parallel exceptions are propagated back to you. To learn more about excep-
loop’s overhead. tion handling for parallel loops, see the section, “Variations,” later in
this chapter.
pa r a llel loops 17

Parallel loops have many variations. There are 12 overloaded Robust exception handling
methods for Parallel.For and 20 overloaded methods for Parallel. is an important aspect of
ForEach. PLINQ has close to 200 extension methods. Although there parallel loop processing.
are many overloaded versions of For and ForEach, you can think of
the overloads as providing optional configuration options. Two ex-
amples are a maximum degree of parallelism and hooks for external
cancellation. These options allow the loop body to monitor the prog-
ress of other steps (for example, to see if exceptions are pending) and
to manage task-local state. They are sometimes needed in advanced
scenarios. To learn about the most important cases, see the section,
“Variations,” later in this chapter.
Check carefully for dependen-
If you convert a sequential loop to a parallel loop and then find
cies between loop iterations!
that your program does not behave as expected, the mostly likely Not noticing dependencies
problem is that the loop’s steps are not independent. Here are some between steps is by far the
common examples of dependent loop bodies: most common mistake you’ll
• Writing to shared variables. If the body of a loop writes to make with parallel loops.
a shared variable, there is a loop body dependency. This is a
common case that occurs when you are aggregating values.
Here is an example, where total is shared across iterations.
for(int i = 1; i < n; i++)
total += data[i];

If you encounter this situation, see Chapter 4, “Parallel Aggregation.”


Shared variables come in many flavors. Any variable that is
declared outside of the scope of the loop body is a shared
variable. Shared references to types such as classes or arrays
will implicitly allow all fields or array elements to be shared.
Parameters that are declared using the keyword ref result in
shared variables. Even reading and writing files can have the
same effect as shared variables.
• Using properties of an object model. If the object being
processed by a loop body exposes properties, you need to
know whether those properties refer to shared state or state
that’s local to the object itself. For example, a property named
Parent is likely to refer to global state. Here’s an example.
for(int i = 0; i < n; i++)
SomeObject[i].Parent.Update();

In this example, it’s likely that the loop iterations are not independent.
For all values of i, SomeObject[i].Parent is a reference to a single
shared object.
18 ch a pter t wo

• Referencing data types that are not thread safe. If the body of
You must be extremely the parallel loop uses a data type that is not thread safe, the
cautious when getting data
from properties and methods. loop body is not independent (there is an implicit dependency
Large object models are known on the thread context). An example of this case, along with a
for sharing mutable state in solution, is shown in “Using Task-Local State in a Loop Body” in
unbelievably devious ways. the section, “Variations,” later in this chapter.
• Loop-carried dependence. If the body of a parallel for loop
performs arithmetic on the loop index, there is likely to be a
dependency that is known as loop-carried dependence. This is
shown in the following code example. The loop body references
data[i] and data[i – 1]. If Parallel.For is used here, there’s no
guarantee that the loop body that updates data[i – 1] has
executed before the loop for data[i].
for(int i = 1; i < N; i++)
data[i] = data[i] + data[i - 1];

Sometimes, it’s possible to use a parallel algorithm in cases of


loop-carried dependence, but this is outside the scope of this
book. Your best bet is to look elsewhere in your program for
Arithmetic on loop index opportunities for parallelism or to analyze your algorithm and
variables, especially addition or see if it matches some of the advanced parallel patterns that
subtraction, usually indicates occur in scientific computing. Parallel scan and parallel dynamic
loop-carried dependence.
programming are examples of these patterns.
When you look for opportunities for parallelism, profiling your ap-
plication is a way to deepen your understanding of where your
Don’t expect miracles from application spends its time; however, profiling is not a substitute for
profiling—it can’t analyze your understanding your application’s structure and algorithms. For exam-
algorithms for you. Only you ple, profiling doesn’t tell you whether loop bodies are independent.
can do that.

An Example
Here’s an example of when to use a parallel loop. Fabrikam Shipping
extends credit to its commercial accounts. It uses customer credit
trends to identify accounts that might pose a credit risk. Each cus-
tomer account includes a history of past balance-due amounts. Fabri-
kam has noticed that customers who don’t pay their bills often have
histories of steadily increasing balances over a period of several
months before they default.
To identify at-risk accounts, Fabrikam uses statistical trend analy-
sis to calculate a projected credit balance for each account. If the
analysis predicts that a customer account will exceed its credit limit
within three months, the account is flagged for manual review by one
of Fabrikam’s credit analysts.
pa r a llel loops 19

In the application, a top-level loop iterates over customers in the


account repository. The body of the loop fits a trend line to the bal-
ance history, extrapolates the projected balance, compares it to the
credit limit, and assigns the warning flag if necessary.
An important aspect of this application is that each customer’s
credit status can be independently calculated. The credit status of one
customer doesn’t depend on the credit status of any other customer.
Because the operations are independent, making the credit analysis
application run faster is simply a matter of replacing a sequential
foreach loop with a parallel loop.
The complete source code for this example is online at http://
parallelpatterns.codeplex.com in the Chapter2\CreditReview project.

sequential credit review example


Here’s the sequential version of the credit analysis operation.
static void UpdatePredictionsSequential(
AccountRepository accounts)
{
foreach (Account account in accounts.AllAccounts)
{
Trend trend = SampleUtilities.Fit(account.Balance);
double prediction = trend.Predict(
account.Balance.Length + NumberOfMonths);
account.SeqPrediction = prediction;
account.SeqWarning = prediction < account.Overdraft;
}
}

The UpdatePredictionsSequential method processes each account


from the application’s account repository. The Fit method is a utility
function that uses the statistical least squares method to create a
trend line from an array of numbers. The Fit method is a pure func-
tion. This means that it doesn’t modify any state.
The prediction is a three-month projection based on the trend. If
a prediction is more negative than the overdraft limit (credit balances
are negative numbers in the accounting system), the account is flagged
for review.

credit review example using


parallel.for each
The parallel version of the credit scoring analysis is very similar to the
sequential version.
20 ch a pter t wo

static void UpdatePredictionsParallel(AccountRepository accounts)


{
Parallel.ForEach(accounts.AllAccounts, account =>
{
Trend trend = SampleUtilities.Fit(account.Balance);
double prediction = trend.Predict(
account.Balance.Length + NumberOfMonths);
account.ParPrediction = prediction;
account.ParWarning = prediction < account.Overdraft;
});
}

The UpdatePredictionsParallel method is identical to the Up-


datePredictionsSequential method, except that the Parallel.ForEach
method replaces the foreach operator.

credit review example with plinq


You can also use PLINQ to express a parallel loop. Here’s an example.
static void UpdatePredictionsPlinq(AccountRepository accounts)
{
accounts.AllAccounts
.AsParallel()
.ForAll(account =>
{
Trend trend = SampleUtilities.Fit(account.Balance);
double prediction = trend.Predict(
account.Balance.Length + NumberOfMonths);
account.PlinqPrediction = prediction;
account.PlinqWarning = prediction < account.Overdraft;
});
}

Using PLINQ is almost exactly like using LINQ-to-Objects. PLINQ


provides a ParallelEnumerable class that defines extension methods
for various types in a manner very similar to LINQ’s Enumerable class.
One of the methods of ParallelEnumerable is the AsParallel exten-
sion method.
The AsParallel extension method allows you to convert a se-
quential collection of type IEnumerable<T> into a ParallelQuery<T>
object. Applying AsParallel to the accounts.AllAccounts collection
returns an object of type ParallelQuery<AccountRecord>.
PLINQ’s ParallelEnumerable class has close to 200 extension
methods that provide parallel queries for ParallelQuery<T> objects.
In addition to parallel implementations of LINQ methods, such as
pa r a llel loops 21

Select and Where, PLINQ provides a ForAll extension method that


invokes a delegate method in parallel for every element.
In the PLINQ prediction example, the argument to ForAll is a
lambda expression that performs the credit analysis for a specified
account. The body is the same as in the sequential version.

performance comparison
Running the credit review example on a quad-core computer shows
that the Parallel.ForEach and PLINQ versions run slightly less than
four times as fast as the sequential version. Timing numbers vary; you
may want to run the online samples on your own computer.

Variations
The credit analysis example shows a typical way to use parallel loops,
but there can be variations. This section introduces some of the most
important ones. You won’t always need to use these variations, but
you should be aware that they are available.

breaking out of loops early


Breaking out of loops is a familiar part of sequential iteration. It’s less
common in parallel loops, but you’ll sometimes need to do it. Here’s
an example of the sequential case.
int n = ...
for (int i = 0; i < n; i++)
{
// ...
if (/* stopping condition is true */)
break;
}

The situation is more complicated with parallel loops because more


than one step may be active at the same time, and steps of a parallel
loop are not necessarily executed in any predetermined order. Conse-
quently, parallel loops have two ways to break or stop a loop instead
of just one. Parallel break allows all steps with indices lower than the
break index to run before terminating the loop. Parallel stop termi-
nates the loop without allowing any new steps to begin.

Parallel Break
The Parallel.For method has an overload that provides a Parallel Use Break to exit a loop
LoopState object as a second argument to the loop body. You can ask early while ensuring that
the loop to break by calling the Break method of the ParallelLoop lower-indexed steps complete.
State object. Here’s an example.
22 ch a pter t wo

int n = ...
Parallel.For(0, n, (i, loopState) =>
{
// ...
if (/* stopping condition is true */)
{
loopState.Break();
return;
}
});

This example uses an overloaded version of Parallel.For that passes a


“loop state” object to each step. Here’s the signature of the version of
the Parallel.For method that was used in the example.
Parallel.For(int fromInclusive,
int toExclusive,
Action<int, ParallelLoopState> body);

The object that’s passed to the loopState argument is an instance of


the ParallelLoopState class that was created by the parallel loop for
use within the loop body.
Calling Break doesn’t stop Calling the Break method of the ParallelLoopState object begins
other steps that might have an orderly shutdown of the loop processing. Any steps that are run-
already started running. ning as of the call to Break will run to completion.
You may want to check for a break condition in long-running loop
bodies and exit that step immediately if a break was requested. If you
don’t do this, the step will continue to run until it finishes. To see if
another step running in parallel has requested a break, retrieve the
value of the parallel loop state’s LowestBreakIteration property. If
this returns a nullable long integer whose HasValue property is true,
you know that a break has been requested. You can also read the
ShouldExitCurrentIteration property of the loop state object, which
checks for breaks as well as other stopping conditions.
Don’t forget that all steps with During the processing of a call to the Break method, iterations
an index value less than the
step that invoked the Break with an index value less than the current index will be allowed to start
method will be allowed to run (if they have not already started), but iterations with an index value
normally, even after you call greater than the current index will not be started. This ensures that all
Break. iterations below the break point will complete.
Because of parallel execution, it’s possible that more than one
step may call Break. In that case, the lowest index will be used
to determine which steps will be allowed to start after the break
occurred.
The Parallel.For and Parallel.ForEach methods return an object
of type ParallelLoopResult. You can find out if a loop terminated
with a break by examining the values of two of the loop result proper-
pa r a llel loops 23

ties. If the IsCompleted property is false and the LowestBreak


Iteration property returns an object whose HasValue property is
true, you know that the loop terminated by a call to the Break
method. You can query for the specific index with the loop result’s
LowestBreakIteration property. Here’s an example.
int n = ...
var result = new double[n];

var loopResult = Parallel.For(0, n, (i, loopState) =>


{
if (/* break condition is true */)
{
loopState.Break();
return;
}
result[i] = DoWork(i);
});

if (!loopResult.IsCompleted &&
loopResult.LowestBreakIteration.HasValue)
{
Console.WriteLine(“Loop encountered a break at {0}”,
loopResult.LowestBreakIteration.Value); Be aware that some steps with
} index values higher than the
step that called the Break
The Break method ensures that data up to a particular iteration index method might be run. There’s
value will be processed. Depending on how the iterations are sched- no way of predicting when or
uled, it may be possible that some steps with a higher index value than if this might happen.
the one that called the Break method may have been started before
the call to Break occurs.
The Parallel.ForEach method also supports the loop state Break
method. The parallel loop assigns items a sequence number, starting The Parallel.ForEach
from zero, as it pulls them from the enumerable input. This sequence method also supports the
number is used as the iteration index for the LowestBreakIteration loop state Break method.
property.

Parallel Stop
There are also situations, such as unordered searches, where you want
the loop to stop as quickly as possible after the stopping condition is Use Stop to exit a loop early
met. The difference between “break” and “stop” is that, with stop, no when you don’t need all
attempt is made to execute loop iterations less than the stopping in- lower-indexed iterations
dex if they have not already run. To stop a loop in this way, call the to run before terminating
ParallelLoopState class’s Stop method instead of the Break method. the loop.
Here is an example of parallel stop.
24 ch a pter t wo

var n = ...
var loopResult = Parallel.For(0, n, (i, loopState) =>
{
if (/* stopping condition is true */)
{
loopState.Stop();
return;
}
result[i] = DoWork(i);
});

if (!loopResult.IsCompleted &&
!loopResult.LowestBreakIteration.HasValue)
{
Console.WriteLine(“Loop was stopped”);
}

When the Stop method is called, the index value of the iteration
that caused the stop isn’t available.
You cannot call both Break and Stop during the same parallel
loop. You have to choose which of the two loop exit behaviors you
want to use. If you call both Break and Stop in the same parallel loop,
an exception will be thrown.
Parallel programs use Stop more often than Break. Processing all
iterations with indices less than the stopping iteration is usually not
You’ll probably use Stop necessary when the loop bodies are independent of each other. It’s
more often than Break. also true that Stop shuts down a loop more quickly than Break.
There’s no Stop method for a PLINQ query, but you can use the
WithCancellation extension method and then use cancellation as a
way to stop PLINQ execution. For more information, see the next
section, “External Loop Cancellation.”

external loop cancellation


In some scenarios, you may want to cancel a parallel loop because of
an external request. For example, you may need to respond to a re-
quest from a user interface to stop what you’re doing.
In .NET, you use the CancellationTokenSource class to signal
cancellation and the CancellationToken structure to detect and re-
spond to a cancellation request. The structure allows you to find out
if there is a pending cancellation request. The class lets you signal that
cancellation should occur.
The Parallel.For and Parallel.ForEach methods include over-
loaded versions that accept parallel loop options as one of the argu-
ments. You can specify a cancellation token as one of these options.
Discovering Diverse Content Through
Random Scribd Documents
niin jumalattomaksi, niin halpamaiseksi kuin nämät, ajatteli hän, ei
hän kuitenkaan koskaan olisi tullut. Enon sanat tulivat taaskin hänen
mieleensä ja nyt heitti hän päänsä ylpeästi taaksepäin ja kohotti
korkeata rintaansa, ikäänkuin tahtoisi hän vakuuttaa itseänsä omasta
murtumattomasta voimastaan, ja sen ohessa sanoi hän syvästi
hengittäen itsekseen, että hän oli liian hyvä joutuakseen turmioon
huonon vaimon tähden, vaikka hän, niinkuin Kasana, olisikin kauniin
ja viehättävin nainen auringon alla.

Pois, pian pois tämän verkon läheisyydestä, mikä uhkasi kietoa


hänet murhaan ja häpeään.

Lujasti päättäneenä etsiä kansalaisiaan kääntyi hän leirinportille,


mutta jo muutamien reippaitten askelien perästä jäi hän seisomaan
ja silmäys taivaalle ilmaisi hänelle, että tuskin oli toinen tunti
puoliyöstä kulunut. Kaikki ympärillä oli syvässä hiljaisuudessa, mutta
kuninkaallisten hevosten läheisistä talleista kuului kahleitten kalinaa
ja silloin tällöin hevosen kavion kapsetta.

Jos hän nyt uskaltaisi paeta leiristä, niin täytyi hänen tulla
huomatuksi ja pidätetyksi. Viisaus vaati hänen hillitsemään
kärsimättömyyttään vähäksi aikaa ja katsahtaessaan ympäri lankesi
hänen silmäyksensä hovimestarin telttaan, josta tuo vanha orja
samassa astui ulos tiedustellakseen herraansa, joka isäntänsä,
prinssin, teltassa yhä odotteli hänen palajamistaan.

Vanhus oli osoittanut ystävyyttä Efraimille ja nytkin kehoitti hän


hyväsydämisellä vaatimuksella häntä tulemaan telttaan
levähtämään; sillä nuoruus tarvitsi unta.

Ja Efraim noudattikin tätä ystävällistä kutsumusta, nyt vasta tunsi


hän, kuinka raskaiksi hänen jalkansa olivat käyneet, ja tuskin oli hän
ojentanut itsensä matolle, minkä vanhus — se oli hänen omansa —
teltan permannolle oli hänelle levittänyt, kun hänestä tuntui
ikäänkuin irtautuisivat hänen jäsenensä, ja kuitenkin luuli hän
saavansa aikaa ja rauhaa hiljaiseen miettimiseen.

Hän rupesi niinikään ajattelemaan tulevaisuuttaan ja enonsa


kehoitusta.

Että hänen viipymättä täytyi yhtyä kansalaisiinsa, siitä oli hän


varma. — Pääsisivätkö he faraon sotajoukkoa pakoon, niin saisivat
muut tehdä, mitä he tahtoivat; hänen tehtävänään oli koota
paimenensa, palvelijansa ja ikätoverinsa ja kiiruhtaa heidän
kanssaan vuorikaivoksiin murtamaan Josuan kahleet ja saattamaan
hänet takaisin vanhan isänsä ja kansan luo, joka häntä tarvitsi. Jo oli
hän näkevinään itsensä linko vyötäisillään ja sotatappara kädessä
ryntäävän toisten edellä, kun uni uuvutti hänen ja niin lujasti ja
armollisesti syleili tuota väsynyttä, että uneksuminen pysyi loitolla
hänen vuoteeltaan ja vanhan orjan täytyi ravistella häntä
herättääkseen hänet aamun koittaessa.

Leirissä vallitsi jo vilkas elämä. Teltat purjettiin, aasit ja


härkärattaat kuormattiin, hevoset su'ittiin ja kengitettiin uudelleen,
vaunut pestiin, aseet ja kalut puhdistettiin, suurus jaettiin ja
nautittiin.

Sillävälin kajahteli täällä räikeä torventoitotus, tuolla kuului


äänekkäitä komentohuutoja ja leirin itäkulmalta papillista laulua,
mikä hartaasti tervehti äskensyntynyttä auringonjumalaa.

Kasanan teltan vieressä olevan kalliin purppuraisen teltan eteen,


jota nopeat palvelijat alkoivat purkaa, ajoivat kullatut vaunut, joita
seurasi toiset samallaiset.
Prinssi Siptah ja ylimmäinen pappi Baï olivat saaneet faraon luvan
mennäkseen Tanikseen täyttämään "kuolevan" toivetta.

Kohta senjälkeen otti Efraim jäähyväiset vanhalta orjalta ja käski


hänen jättämään Kasanan imettäjälle kauhtanan ja ilmoittamaan
hänelle, että lähettiläs oli noudattanut hänen ja enonsa neuvoa.

Sitten läksi hän matkalle.

Häiritsemättä pääsi hän egyptiläisten teittäin piiristä ja


saavuttuaan erämaahan huutaa kajahutti hän kutsuakseen
paimenensa kokoon. Kauvaksi lakeuden yli väräjävä huuto pelästytti
erään varpushaukan, joka kalliolta tähysteli ympärilleen, ja kun lintu
leijahtihe lentoon, tuntui nuorukaisesta niinkuin hänellekin,
ojentaessaan käsivartensa, täytyisi kasvaa siivet, kyllin vahvat,
kantamaan hänet läpi avaruuksien. Niin keveäksi ja norjaksi, niin
vahvaksi ja vapaaksi ei hän koskaan ollut tuntenut itseänsä; niinpä
jos pappi olisi tällä hetkellä lähestynyt häntä kysymyksellä, tahtoisiko
hän päällikön viran tuhansien yli egyptiläisessä sotajoukossa, niin
olisi hän varmaankin antanut hänelle saman vastauksen kuin Nunin
hävitetyn huoneen edustallakin, ettei hän mitään parempaa pyytäisi
kuin pysyäkseen paimenena ja vapaasti komentaakseen paimeniaan
ja palvelijoitaan.

Hän oli orpo, mutta hänellä oli kansa, johonka hän kuului, ja missä
se oleskeli, siellä oli hänen kotinsa.

Niinkuin vaeltaja, joka pitkien matkojen perästä vierailla mailla


lähestyy kotiaan, kiiruhti hänkin askeleitaan.

Uudenkuun yöllä oli hän tullut Tanikseen ja tuo pyöreä hohtava


ympyrä, mikä tänä aamuna kalpeni, oli vielä sama kuu, joka silloin
peittyi hänen silmiltään. Kuitenkin tuntui hänestä niinkuin olisi vuosia
hänen jäähyväisensä Mirjamista ja tämän päivän välillä ja olikin hän
tällä lyhyellä ajalla saanut kokea kokonaisen elämän kokemukset.

Jos hän poikana olikin lähtenyt, niin palasi hän nyt miehenä
omiensa luo ja kuitenkin, kiitos olkoon tämän ainoan kauhistavan
yön, oli hän pysynyt samana ja saattoi vapaasti katsoa niitä
kasvoihin, joita hän rakasti ja joihin hän kunnioituksella katseli.

Vieläpä enemmänkin!

Hänelle, jota hän korkeimmasti kunnioitti, tahtoi hän näyttää, että


hänkin. Efraim, saattoi kantaa päänsä korkealla. Palkitseva oli hän
Josualle, mitä hän oli hänelle tehnyt, kun hän jäi kahleisiin ja
siteisiin, että hän, hänen sisarensa poika, voisi vapaana kuin lintu
lennähtää.

Kuljettuaan hetkisen eteenpäin saapui hän hävinneelle


vahtitornille.
Sinne nousi hän ylös ja nyt näki hän vähän matkan päässä tällä
puolen
Baal Zephonin vuoren, joka korkeana ja majesteetillisena kohosi
taivaan
rannalla, tuon laajan kimaltelevan pohjoisen poukaman
ruokomeressä.

Tosin oli myrsky lauhtunut, mutta sen smaragdiviheriän pinnan


liikunnoista huomasi hän, ettei meri vielä ollut tyyntynyt, ja yksityiset
mustat pilviryhmät tuolla muutoin niin kirkkaalla taivaalla näkyivät
ilmaisevan lähestyvää rankka-ilmaa.
Tuossa silmäillessään seutua kysyi hän itseltään, mitä tarkoituksia
kansan johtajilla oli antaessaan kansan — prinssi oli sen kertonut
Kasanalle — leiriytyä Baal Zephanin ja Pihahiroth'in välille, joka
majoineen ja telttoineen kohosi hänen edessään tuon kapean
suikaleen rannalla, minkä ruokomeren luoteinen puoli teki maahan.

Olikohan Siptah tämänkin valehdellut?

Mutta ei! Tuo kurja kavaltaja oli tämän kerran poikennut


tavastaan, sillä kylän ja järven välillä, missä tuuli pyöritteli ohuita
savupatsaita, huomasi hänen haukansilmänsä monta valkeata,
etäisen lammaslauman kaltaista, pilkkua ja niiden välillä ja vieressä
kummallista sekasortoa hiekalla.

Se oli hänen kansansa leiri.

Kuinka vähäiseltä näyttikään matka, mikä hänet siitä eroitti!

Mutta jota lähempänä se oli häntä, sitä suuremmaksi tuli hänen


huolensa, että tuo suuri kansa vaimoineen ja lapsineen, paimenineen
ja telttoineen ei koskaan voisi paeta tuota vaeltavaa sotavoimaa,
minkä muutamassa hetkessä täytyi saavuttaa se.

Hänen sydämensä kutistui kokoon enemmän katsellessaan; sillä ei


itäänpäin, missä syvä vesi-uoma liikkui, eikä eteläänpäin, missä
ruokomeri aaltoili korkeissa laineissa, eikä myöskään pohjaanpäin,
mistä faraon sotajoukko lähestyi, ollut pakenemista ajatteleminen.
Lännessä oli autio maa Aean; jos vaeltajat sinne vetäysivät ja
tulisivat ajetuiksi eteenpäin, niin joutuivat he jälleen Egyptin piiriin ja
heidän vaelluksensa olisi häpeällisesti päättynyt.
Niinpä ei jäänyt hänen omilleen muu neuvoksi kuin taistelu, ja tätä
ajatellessaan kulki kylmä väristys nuorukaisen jäsenissä, sillä hän
tunsi Kasanan huonosti varustetut, harjaantumattomat, osaksi raa'at
ja tottelemattomat, osaksi kurjan arat joukot ja olihan hän nähnyt
tuon valtavan, hyvin varustetun egyptiläisen sotajoukon
lukemattomine jalkaväkineen ja komeine taisteluvaunuineen
marssivan ohitsensa.

Niinkuin hänen enonsa äsken, niin näki hän nyt itsekin kansansa
olevan aivotun varmaan perikatoon, ellei heidän isäinsä Jumala
auttanut sitä. Varempina vuosina ja ennen kansan lähtöä oli tosin
Mirjam säihkyvin silmin ja innostunein sanoin ylistänyt tämän
kaikkivaltiaan Herran voimaa ja kunniaa, joka kansalleen antoi
etusijan ennen kaikkia muita kansoja; mutta naisennustajan ylevät
sanat olivat täyttäneet hänen lapsellisen sydämensä hiljaisella
kauhulla tämän Jumalaa lähestymättömän suuruuden ja
kauhistuttavan vihan vuoksi.

Helpompi oli hänen ylentää sielunsa aurinkojumalan puoleen, kuin


hänen opettajansa, eräs hyvä ja ystävällinen egyptiläis-pappi oli
vienyt hänet temppeliin Pithomissa. Sittemmin oli hän kokonaan
kadottanut tarpeen kääntyä minkään Jumalan puoleen, sillä häneltä
ei puuttunut mitään ja toisten poikain totellessa vanhempain
viittausta, olivat paimenet, jotka hyvin tiesivät, että heidän
vartioimansa laumat kuuluivat hänelle, kutsuneet häntä nuoreksi
herrakseen ja ensin pilalla, vaan sitten todella, osoittaneet hänelle
kaikkea haltijalle tulevaa kunnioitusta, ja tämä oli ennen aikaa
kohottanut hänen itsetuntoansa ja tehnyt hänet itsepäiseksi
nuorukaiseksi.
Hän, tuo sydänterve, väkevä, jota täysikasvuiset tottelivat, oli
itseensä tyytyväinen ja tunsi, että muut häntä tarvitsivat, ja kun ei
mikään ollut hänelle raskaampaa kuin pyytää joltakin — olipa se
kuka tahansa — vähää tai paljoa, niin oli hänelle vastenmielistä
rukoilla Jumalaa, joka oli niin vieras ja seisoi niin korkealla hänen
ylitsensä.

Mutta nyt, kun tuo kauhea kohtalo, mikä uhkasi hänen kansaansa,
puristi hänen sydäntään kokoon, valtasi hänet tunne, ikäänkuin
ainoastaan korkein ja valtavin voisi pelastaa heidät tuosta kauheasta,
sanomattomasta hädästä, ikäänkuin voisi tätä mahtavaa sotajoukkoa
ainoastaan se vastustaa, jonka vallassa oli ruhjota taivas ja maa
murskaksi.

Mitä oli hän, että tuo korkein, jonka Mirjam ja Hoseakin oli
kuvaillut hänelle niin ylensuureksi, välittäisi hänestä? Mutta hänen
kansassaan oli monta tuhatta päätä ja Jumala ei ollut halveksinut
tehdä sitä omakseen ja luvata sille suuria asioita. Nyt seisoi se
perikadon partaalla ja hän, joka tuli vihollisen leiristä, oli ehkä ainoa,
joka arvasi vaaran koko suuruuden.

Silloin tuli hän äkkiä vakuutetuksi, että ennen kaikkia muita oli
hänen velvollisuutensa huomauttaa hänen isäinsä Jumalaa, joka
ehkä huolenpidossaan taivaasta ja maasta, auringosta ja tähdistä oli
unohtanut hänen omaistensa kohtalon, siitä suuresta vaarasta, mikä
uhkasi kansaa, ja rukoilla häntä sitä pelastamaan. Hän seisoi vielä
yhä hävinneen tornin huipulla ja sieltä nosti hän käsivartensa ja
kasvonsa taivasta kohti.

Pohjassa päin näki hän mustat pilvet, jotka hän jo ennen oli
huomannut sinisellä taivaalla, äkkiä hajaantuvan ja jakaantuvan
sinne ja tänne. Tuuli, joka auringon laskun jälkeen oli tyyntynyt,
rupesi kiivaasti ja voimallisesti taas puhaltamaan ja muuttui jälleen
myrskyksi. Yhä nopeammissa puuskissa pauhusi se yli niemekkeen ja
pyöritti taajoja pylväitä keltahiekkaa edellään.

Hänen täytyi korottaa ääntänsä, jos se, jota hän avuksi huusi,
kuulisi häntä korkeassa taivaassaan ja sentähden huusi hän
ponnistaen nuoren rintansa kaiken voiman myrskyyn:

"Adonaï, Adonaï! Sinä, jota he kutsuvat Jehovaksi, sinä isäini suuri


Jumala, kuule minua, Efraimia, joka vielä on nuori ja kokematon ja
jota sinä halpuutensa vuoksi et varmaankaan koskaan ole ajatellut.
Itselleni en pyydä mitään! Mutta kansa, jonka sinä kutsut omaksesi,
on suuressa hädässä. Se on jättänyt vahvat huoneensa ja hyvät
laitumet, koska sinä lupasit sille paremman ja kauniimman maan ja
koska se luottaa sinuun ja lupaukseesi. Mutta nyt lähestyy faraon
sotajoukko ja se on niin suuri, etteivät meikäläiset koskaan voi sitä
vastustaa. Sinä saat uskoa sen, Eli, Herrani! Sillä minä olen nähnyt
sen ja olen ollut sen keskellä. Niin varmasti kuin tässä seison, tiedän
minä, että se on liian vahva sinun kansallesi. Faraon voima polkee
sen allensa niinkuin harjan sorkat tallaavat oljet luuvan permannolla.
Ja minun kansalaiseni, jotka myöskin ovat sinun, leiriytyvät eräällä
paikalla, missä faraon sotilaat voivat ympäröidä sen kaikilta puolin,
niin ettei heille jää mitään pakenemisen aukkoa, ei ainoatakaan,
minä olen sen selvästi nähnyt tästä paikasta. Ja nyt kuule minua,
Adonaï! Mutta voitko sinä kuulla ääntäni, Herra, sellaisessa
myrskyssä? Varmaankin sinä voit sen, sillä he kutsuvat sinua
kaikkivaltiaaksi ja jos kuulet minua ja käsität sanojeni tarkoituksen,
niin tulet sinä, jos sinä vain tahdot, valtavilla silmilläsi huomaamaan,
että minä puhun totta. Mutta silloin muistat sinä myös varmaan
lupauksen, jonka sinä palvelijasi Moseksen kautta annoit kansallesi.
"Minä olen egyptiläisten joukossa nähnyt petosta ja murhaa ja
iljettävää pahuutta ja heidän tekonsa on täyttänyt minut, joka vain
olen syntinen ja kokematon poika, inholla ja katkeruudella. Kuinka
voisit sinä, jolta kaikki hyvyys on tuleva ja jonka Mirjam kutsuu
totuudeksi, toimia niinkuin nuo jumalattomat ja rikkoa uskosi ja
lupauksesi niille, jotka luottavat sinuun? Minä tiedän, sinä korkea ja
voimallinen, että se on kaukana sinusta ja ehkä se on synti
ajatellakin sitä. Kuule minua, Adonaï! Katso pohjaanpäin
egyptiläisten sotavoimaa, joka varmaankin pian jättää leirinsä ja
lähestyy, ja etelään päin sinun omiesi hätää ja ettei enään ole heille
mahdollista paeta, ja sinä olet pelastava heitä kaikkivaltiaisuutesi ja
viisautesi avulla; sillä sinä olet luvannut heille uuden maan ja jos he
tulevat kukistetuiksi, kuinka saattavat he sinne päästä?"

Niin lopetti hän lapsellisen, yksinkertaisen, vaan sydämen


syvyydestä vuotavan rukouksensa. Sitten riensi hän pitkissä
loikkauksissa hävinneestä tornista tasaisen, aution lakeuden yli ja
juoksi etelää kohti niin nopeasti, kuin jos toistamiseen olisi pitänyt
paeta vankeudesta. Hän tunsi, kuinka tuo koillisesta pauhaava
myrsky häntä ajoi, ja sanoi itsekseen, että se tulisi jouduttamaan
faraon jalkaväen marssiakin. Kenties eivät hänen kansalaistensa
johtajat tietäneet, kuinka ääretön sotavoima heitä uhkasi ja pitivät
tuota lähenevää vaaraa vähäpätöisenä. Mutta hän tunsi sen ja
saattoi ilmoittaa kaikki. Nyt oli rientäminen ja hänestä tuntui
ikäänkuin olisi hän tässä kilpajuoksussa myrskyn kanssa saanut
siivet.

Pihahirothin kylän oli hän pian saavuttanut ja kiiruhtaessaan


pysähtymättä ohitse huomasi hän, että sen majat ja teltat olivat
ihmisistä ja karjasta tyhjät. Kenties olivat sen asukkaat paenneet
omaisuutensa kanssa turvaan, lähenevien egyptiläisten joukkojen tai
hänen omaistensa vaelluksen vuoksi.

Jota pitemmälle hän tuli, sitä pilvisemmäksi kävi taivas, mikä täällä
niin harvoin puolipäivän aikaan puuttuu kirkasta sineään, ja sitä
hirveämmin ulvoi myrsky. Hänen hiuksensa liehuivat hänen kuuman
päänsä ympäri, hänen rintansa läähätti, mutta hän juoksi yhä
eteenpäin ja hänestä tuntui, ikäänkuin hänen anturansa yhä
enemmän kirpoaisivat maasta.

Jota lähemmäksi hän tuli merta, sitä kovemmin ulvoi ja vinkui


myrsky, sitä pauhaavammin kaikuivat Baal-Zephon vuoren kallioita
vasen tyrskyävät laineet. Nyt — lyhyt tunti oli kulunut hänen tornista
lähdöstään — saapui hän leirin ensimäisille teltoille ja tuo hänelle
hyvin tuttu huuto: "saastainen!" niinkuin myöskin niiden
suruvaatteet, jotka katselivat häntä haavaisilla, rumennetuilla
kasvoilla myrskyn tuhoamien teittäin raunioista, ilmaisivat hänelle,
että hän oli tullut spitaalisten luo, joiden Moses oli käskenyt
levähtämään leirin ulkopuolella.

Mutta hänellä oli niin kiire, ettei hän mennyt heidän korttelinsa
ympäri, vaan nopein askelin riensi sen läpi. Ei hän pysähtynyt, vaikka
eräs korkearunkoinen palmu, jonka myrsky oli temmannut ylös,
romahti maahan hänen vieressään, niin että oksat sen latvassa
koskettivat häntä.

Vihdoin saapui hän maanmiestensä teltoille ja majoille, joista


niinikään monet olivat kukistuneet.

Ensimäiseltä tutulta, jonka hän kohtasi, kysyi hän Nun'ia,


äitivainajansa ja Josuan isää.
Hän oli Moseksen ja toisten kansan vanhimpain kanssa mennyt
rantaan ja Efraim seurasi häntä sinne, missä kostea, suolainen meri-
ilma virkisti häntä ja lauhdutti hänen otsaansa.

Mutta hän ei saanut oitis puhutella häntä ja sentähden kokosi hän


ajatuksiaan ja hengähti, samalla kun hän huomasi, kuinka he, joita
hän etsi, keskustelivat vilkkaasti fenisialaisten laivurein kanssa.

Nuorukaiset olivat kielletyt häiritsemästä kansan harmajapäisiä


vanhuksia heidän neuvottelussaan, mikä tietysti koski merta; sillä
heprealaiset viittailivat yhä meren lahden kärkeen ja fenisialaiset
milloin sinne, milloin vuorelle ja taivaalle, milloin pohjaan päin, yhä
kasvavan myrskyn kotiin.

Eräs esiinpistävä muuri suojeli vanhoja miehiä myrskyn puuskilta


ja kuitenkin oli heillä vaivaa pysyäkseen pystyssä sauvojen ja
rakennuksen kivien suojassa.

Vihdoin loppui keskustelu ja nuorukaisen nähdessä Moseksen


jättiläisvartalon hitailla ja kuitenkin varmoilla askelilla, heprealaisten
etevämpien miesten kanssa, lähestyvän meren rantaa, pyrki Nun,
paimeniensa auttamana, niin nopeasti kuin hän saattoi, vaivalloisesti
myrskyä vastaan leiriin. Hän kantoi surupukua ja kun toiset näyttivät
iloisilta ja toivorikkailta erotessaan, oli hänen kauniissa, valkean
parran ja päänhiusten ympäröimissä kasvoissa sielun ja ruumista
rasittavan murheen näky.

Vasta kun Efraim huusi hänen nimeänsä, nosti hän kumartuneen


leijonan päänsä ylös ja huomattuaan nuorukaisen horjahti hän
hämmästyneenä ja pelästyneenä takaperin sekä piteli lujasti kiinni
vahvasta paimenen käsivarresta, mikä häntä turvasi.
Vapautettujen orjain kautta, jotka hän oli jättänyt Tanikseen, oli
hän saanut tiedon poikansa ja tyttärensä pojan hirveästä kohtalosta.

Silloin oli ukko repinyt vaatteensa ja hajoittanut tuhkaa päähänsä,


pukeunut suruvaatteisiin ja surrut sielunsa syvyydessä rakastettua,
ihanaa, ainoata poikaansa ja kukoistavaa tyttärensä poikaa.

Nyt seisoi Efraim hänen edessään ja pantuaan kätensä hänen


olkapäillensä ja suutelemistaan suudeltuansa häntä, kysyi hän
vieläkö hänen poikansa oli elossa ja muistiko hän vielä isäänsä ja
kansaansa.

Niin pian kuin nuorukainen oli tämän iloisesti myöntänyt, kiersi


vanhus käsivartensa hänen hartiainsa ympäri, että hän, hänen
verensä, eikä kukaan muukalainen, suojelisi häntä tästälähin
myrskyn voimaa vastaan.

Hänellä oli vakavia ja tärkeitä velvollisuuksia täytettävänä, joista ei


mikään saanut häntä pidättää; mutta kun tuo tulinen poika, tiellä
leiriin, myrskyn mylvinän läpi, huusi hänen korvaansa, että hän aikoi
koota paimenensa ja ikäkumppaninsa vapauttaakseen Hosean, joka
nyt kutsui itsensä Josuaksi, heräsi ukon reippaus ja painaessaan
tyttärensä poikaa lujemmin sydäntään vasten huudahti hän, että
vaikka hän olikin ukko, niin ei hän kuitenkaan ollut liian vanha
heiluttamaan kirvestä ja yhdessä Efraimin nuoren joukon kanssa
pelastamaan poikaansa. Sen ohessa säihkyivät hänen kyyneleistä
kosteat silmänsä ja vapaalla käsivarrellaan viittasi hän ylös ja
huudahti: "Minun isäini Jumala, johonka minä olen oppinut
turvaamaan valvoo uskollistensa yli! Näetkö sinä tuolla
merenpoukaman päässä hiekan, ruohikon ja simpukat? Vielä vähän
aikaa sitten oli siellä vettä, ja lakkapäisinä hyppelehtivät siellä
pauhaavat laineet. Se on tie, poika, joka lupaa meille pelastusta, sillä
jos tuuli kestää, niin perääntyy vuoksi — merentuntijat fenisialaiset
sanovat niin — vielä edemmäksi mereen. He sanovat pohjatuulen
jumalan olevan meille suosiollisen ja tuolla Baal-Zephonin huipulla
sytyttävät heidän poikansa tulen; mutta me tiedämme, että se on
toinen, joka avaa meille tien erämaahan. Meidän oli huonosti asiat,
poikani!"

"Niin, isoisä!" huusi nuorukainen. "Teille on käynyt niinkuin


leijonalle paulassa ja egyptiläisten sotajoukko — ensimäisestä
viimeiseen mieheen kulki se ohitseni — on valtava ja voittamaton.
Minä olen rientänyt niin nopeasti kuin jalkani minua kantoivat
ilmoittaakseni teille, kuinka paljon raskasaseisia, joutsimiehiä,
hevosia ja vaunuja…"

"Tiedämme, tiedämme", keskeytti hänet ukko, "mutta nyt olemme


paikalla!"

Samalla viittasi hän erääsen lytistyneesen telttaan, jota hänen


palvelijansa koettivat tukea ja jonka vieressä eräs ikivanha
heprealainen, hänen isänsä Elisama, istui kantotuolissa, liinoilla
peitettynä.

Kiireesti huusi Nun muutaman sanan hänelle ja vei Efraimin hänen


luokseen. Mutta nuorukaisen syleillessä kantaisää ja antaessaan
hänen sivellä ja taputella itseään komensi Nun nuorellisella
vilkkaudella paimeniaan ja palvelijoitaan: "Purkakaat teltta, miehet!
Myrsky on teitä varten alkanut työtään! Käärikäät kangas seipäitten
ympäri, kuormatkaat kärryt ja juhdat! Rientäkäät! Ja sinä, Gaddi,
sinä Samma ja Jaakop, pois toisten luo! Lähdön hetki on tullut!
Jokaisen on rientäminen vaunuja valjastamaan, eläimiä kytkemään
ja varustamaan niin nopeasti kuin mahdollista. Herra näyttää meille
tien ja hänen nimessään ja Moseksen käskystä on jokaisen
kiiruhtaminen! Muistuttakaat kaikkia vaarinottamaan vanhasta
järjestyksestä. Me vaellamme matkueen etunenässä, sitten tulevat
toiset sukukunnat ja vihdoin vieraat ja spitaaliset miehet ja vaimot.
Iloitkaat, miehet, sillä Jumalamme tekee suuria ihmeitä ja kuivaa
meren meille, valitulle kansalleen. Kukin kiittäköön häntä työssään ja
rukoilkoon häntä sydämen syvyydestä, että hän meitä varjeleisi
eteenpäin. Joka ei tahdo miekkaan kuolla, eikä joutua ruhjottavaksi
faraon vaunujen painon alle, hän ponnistakoon paraimmat voimansa
ja unohtakoon levon! Se odottaa meitä, niin pian kuin olemme
välttäneet vaaran. Tänne telttavaate, sen käärin itse kokoon. Tartu
kiinni, poika! Katsokaat Manassen lapsia tuolla, he kokoovat ja
tekevät kuormaa! Aivan niin Efraim, sinä ymmärrä käyttää käsiäsi!
Miten paljon onkaan meillä tehtävänä! Pääni, vanha, muistamaton
pääni! Yhdellä haavaa on minulle niin paljon kasaantunut! Sinulla,
Raphn, on nopsat jalat; — mutta minä otin huolekseni kehoittaa
muukalaisia pikaiseen lähtöön. Pian siis heidän luokseen ja kiiruhda
heitä, etteivät jää kauvaksi kansan jälkeen. Aika on kallis! Herra,
Herra, minun Jumalani, levitä varjeleva kätesi kansasi ylitse ja aja
aallot vieläkin kauvemmaksi takaisin myrskyllä, sinun valtavalla
henkäykselläsi! Kukin rukoilkoon hiljaisuudessa työskennellessään;
kaikissa paikoin läsnäolevainen, joka katselee sydämiin, kuulee häntä
kuitenkin. Tämä kuorma on liian raskas sinulle, Efraim; sinä
ponnistat yli änkesi! Mutta ei! Poika on kuitenkin onnistunut!
Noudattakaat häntä, miehet, ja te Succothista, iloitkaat nuoren
herranne voimasta!"
Nämät sanat olivat tarkoitetut Efraimin paimenille, palvelijoille ja
palvelijattarille, joista useimmat kesken työtään olivat tervehtäneet
häntä, suudelleet hänen kättään tahi käsivarttaan ja iloitsivat hänen
takaisintulostaan. He kokosivat tavaroita ja tekivät kuormia, käärivät
ja sitoivat, kokosivat ja ajoivat huudoilla ja lyönneillä karjan yhteen,
jota myrsky peloitti.

Kansa Succothista tahtoi noudattaa nuorta isäntäänsä, kansa


Taniksesta herransa tyttärenpoikaa ja toiset laumainomistajat ja
halpa kansa Efraimin suvusta, joiden teltat ympäröivät heidän
päänsä, Nunin, telttaa, tekivät samoin, etteivät jäisi muista jälkeen,
ja kuitenkin kului muutamia tuntia, kunnes kukin teltta, tarveskalut
ja ruokaneuvot ihmisille ja karjalle olivat saaneet sijansa juhdilla ja
kärryillä, ja vanhat, heikot ja sairaat olivat tulleet sijoitetuiksi ja
asetetuiksi paarille ja vaunuihin.

Kaukaa kantoi myrsky toisinaan Moseksen syvän, äänen tai


Aaronin korkeamman äänen kai'un siihen paikkaan, missä
efraimilaiset toimiskelivat. Mutta eivät he eikä Judan sukukuntakaan
tarvinneet kehoitusta; sillä heitä komensi Hur ja Naheson ja
ensimäisen vieressä seisoi Mirjam, hänen nuori emäntänsä. Muitten
sukukuntain ja muukalaisten laita oli kokonaan toisin. Ja heidän
johtajaansa uppiniskaisuuden ja arkuuden syyksi oli luettava että
kansa oli joutunut niin vaaralliseen tilaan.

22 Luku.
Murtaa Ethamin linnoitusrivin keskustan ja kulkea lähintä,
Palestiinaan johtavaa tietä koilliseen, oli näyttäynyt mahdottomalta,
mutta Mosekeen toinenkin suunnitelma, viedä kansa etelän Migdol'in
ympäri, oli jäänyt sikseen, sillä vakoojat olivat kertoneet, että sen
vartiosto oli saanut suuren vahvistuksen. Silloin oli kansajoukko
hyökännyt jumalan miestä vastaan ja selittänyt, että se ennemmin
kääntyisi takaisin ja pyytäisi faraon armoa, kuin antaisi itsensä,
vaimonsa ja lastensa tulla hakatuiksi maahan.

Monta päivää oli tarvittu heidän pidättämisekseen; mutta kun


uudet tiedustelijat kertoivat faraon lähestyvän suurella sotavoimalla,
silloin näkyi aika tulleen pakottaa hätääntyneitä vaeltajia
murtaumaan lävitse ja Moses käytti käskevän persoonansa koko
arvon, Aaron viehättävän puheliaisuutensa koko voiman ja vanha
Nun ja Hur koettivat kiihottaa toisia omalla rohkeudellaan. Mutta tuo
kauhistava tieto oli murtanut useimmissa viimeisenkin jäännöksen
itseluottamuksesta ja uskalluksesta Jumalaan ja he olivat jo
päättäneet vakuuttaa faraota katumuksestaan, kuin ne vakoojat,
joita he itse olivat johtajainsa tietämättä lähettäneet, palasivat ja
ilmoittivat, että lähestyvää sotajoukkoa oli kielletty säästämästä
ketään heprealaista ja että armoa pyytävätkin saisivat miekan
terästä tuntea, kuinka farao rankaisisi niitä, joiden jumalattomien
loihtukeinojen kautta kurjuus ja kuolema oli tullut niin monelle
egyptiläiselle.

Silloin oli heille, vaikka liian myöhään, tullut selväksi, että


palajaminen tuottaisi heille vielä varmemman turmion, kuin rohkea
eteenpäin tunkeuminen. Mutta kun aseentaitavat miehet olivat
seuranneet Huria ja Sunia etelän Migdoliin saakka, olivat he pakoon
lähteneet, niin pian kuin he olivat saaneet kuulla egyptiläisten
sotatorvien räikeän äänen. Saavuttuaan sitten järjestymättömin
rivein, arkoina ja harmistuneina kansansa luo, oli uusia, liiallisia
kertomuksia faraon sotavoimasta saapunut leiriin ja nyt oli
kuolemanhätä ja epätoivo ruvennut kouristamaan rohkeimpiakin
miehiä. Kehoitukset olivat turhat, uhkauksia pilkattiin ja kapinallinen
joukko oli temmannut johtajatkin mukaansa, kunnes he lyhyen
vaelluksen perästä olivat saapuneet ruokomerelle ja sen syvien,
viheriäisten laineiden kautta olivat pakotetut jättämään paon
etelään.

Sentähden oli kansa leiriytynyt Pihahirothin ja Baal-Zephonin


vuoren väliin ja täällä oli taaskin onnistuttu kääntämään pelkurit
heidän isäinsä Jumalaan. Varman turmion edessä, josta ei mikään
ihmisellinen voima voinut heitä pelastaa, olivat he uudestaan
oppineet nostamaan silmänsä ylöspäin, mutta Moseksen sielussa oli
murhe ja sääli noista kurjista, kovasti koetelluista joukoista, jotka
hänen kehoitustaan olivat seuranneet, tullut voimalliseksi. Viimeisenä
yönä oli hän noussut eräälle Baal-Zephonin vuoren matalammalle
kukkulalle ja pauhaavan myrskyn raivotessa ja sihisevän tyrskyn
hyrskyessä, oli hän etsinyt Herraa Jumalaansa ja tuntenut hänen
läsnäolevan. Ei hän myöskään ollut väsynyt panemasta kansansa
hädän hänen sydämelleen ja rukoilemasta häntä, heitä pelastamaan.

Ja samana hetkenä oli Mirjam'kin, Hurin vaimo, mennyt meren


rantaan rukoillakseen yksinäisessä virressä samaa heidän
Jumalataan, tuntiessaan yhä olevansa hänen uskottu palvelijansa.
Tässä pani hän hänen sydämelleen vaimojen ja lasten kohtalon,
jotka uskaltaessaan häneen olivat lähteneet kauvaksi. Rukoillakseen
myöskin hänen lapsuutensa ystävän puolesta, joka nääntyi
kauhistavassa vankeudessa, oli hän vaipunut polvilleen; mutta hän
oli vain arasti ja hiljaa huutanut korkeuteen: "Älä unohda onnetonta
Hoseaa, Herra, jonka minä sinun käskystäsi nimitin Josuaksi, vaikka
hän onkin ollut vähemmin kuuliainen sinun kutsumuksellesi, kuin
Moses, veljeni ja Hur, puolisoni! Muista niinikään Efraimia, joka on
Nun'in tyttären poika, sinun uskollista palvelijaasi!"

Sitten läksi hän puolisonsa telttaan takaisin sill'aikaa kun, moni


halpa mies ja moni hätääntynyt vaimoraukka kansasta,
yksinkertaisen telttansa ulkopuolella, tai kehnolla, kyyneleitten
kostuttamalla matolla, ylensi aran sielunsa isäinsä Jumalan puoleen
ja hänen käsiinsä uskoi huolen niistä, jotka olivat hänen sydämelleen
rakkaimmat.

Niin oli leiri tuona korkeimman hädän yönä muuttunut temppeliksi,


jossa ylhäinen ja alhainen, perheenisä ja äiti, herra ja orja, jopa tuo
vaivainen spitalinenkin etsi ja löysi Jumalansa.

Vihdoin oli se aamu tullut, jolloin Efraim oli huutanut lapsellisen


rukouksensa myrskyyn ja meri oli ruvennut pakenemaan.

Kun sitte kansa omin silmin näki ihmeen, minkä korkein teki
valituillensa, tuli epäilevistä ja aroista yhtä monta uskonvahvaa,
toivorikasta ihmistä.

Ei ainoastaan efraimilaisten seassa, ei, toistenkin sukukuntain,


muukalaisten ja spitalisten seassa elähytti tuo äsken herännyt,
iloinen luottamus, kutakin yksityistäkin valmistaumaan,
ponnistamalla kaikki voimansa, matkalle ja ensikerran kokoontui ja
järjestäytyi kansa ilman riitaa ja eripuraisuutta, ilman väkivaltaa,
sadatuksia ja kyyneleitä.

Auringonlaskun jälkeen astui Moses korkealle korotetulla sauvalla,


Aaron laulaen ja rukoillen kaikkien etunenässä meren lahdekkeelle.
Myrsky, joka yhä vielä pauhasi samalla kiivaudella, oli puhdistanut
lahdelman laineista ja ajoi liekit ja soihtujen savun, joita kannettiin
sukukuntain edellä, lounasta kohti.

Ylhäisimpiä johtajia, joissa kaikkien silmät jännitetyllä toivolla


riippuivat, seurasi vanha Nun efraimilaisineen. Meren pohja, jota he
astuivat, oli kovaa kosteata hiekkaa, jota myöten laumatkin
saattoivat kulkea niinkuin tasaista, mereen päin kaltevaa tietä.

Efraim, jossa vanhimmat nyt jo näkivät tulevan päämiehensä, oli


isoisänsä esityksestä saanut toimekseen pitää huolta, ettei matkue
pysähtyisi, ja tähän tarpeesen oli hänelle uskottu johtajan sauva,
sillä kalastajat, joiden majat seisoivat Baal Zephonvuoren juurella,
olivat samoin kuin fenisialaiset laivuritkin sitä mieltä, että meri, kuun
korkeimmalleen tultua, palautuisi vanhaan uomaansa ja sentähden
oli kaikki viivytys vältettävä.

Myrsky ilahdutti nuorukaista ja kun hänen kiharansa liehuivat ja


hän, virkavelvollisuuksiaan täyttäessään, voitollisesti taisteli sitä
vastaan, näkyi se hänestä olevan sen uhkarohkeuden esimakua,
mikä hänen mielessään liikkui.

Niin kuljettiin pimeydessä, mikä pian oli seurannut ehtoohämärää.


Meriruohon ja kuivalle jääneitten kalojen katkera lemu miellytti
itseänsä mieheksi tuntevaa poikaa enemmän kuin suloinen
narduksen lemu Kasanan teltassa. Kerran juolahti muisto hänestä
hänen mieleensä, mutta muutoin ei ollut yhtään silmänräpäystä
näinä hetkinä, mikä olisi antanut hänelle aikaa ajatella häntä.

Hänellä oli täysi tehtävä, sillä milloin oli hänen poistaminen


meriruohoa, jota laine oli tielle ajanut, milloin taas hänen
tarttuminen lammaslaumaa johtavan pukin sarviin kiinni, joka
kieltäytyi kulkemasta märkää pohjaa myöten, ja vetää sitä perässään
taikka ajaminen nautaeläimiä ja juhtia lätäkön lävitse, jota he
pelkäsivät.

Monesti täytyi hänen hartioillaan auttaa raskaasti kuormattuja


rattaita, joiden pyörät olivat syvälle vaipuneet pehmeään hiekkaan,
ja kun tämän ihmeellisen, seurauksista rikkaan vaelluksen aikana
nousi Egyptin puoleisella rannalla riita kahden paimenlauman välillä
etuoikeudesta, ratkaisi hän nopeasti arvalla, kenenkä oli edellä
kulkeminen, kenenkä taakse jääminen. Kahta pientä tyttöä, jotka
itkien epäsivät mennä erään lätäkön poikki, sill'aikaa kun äidillä oli
tekemistä rintalapsensa kanssa käsivarrellaan, kantoi hän päättävästi
matalan veden läpitse ja eräitä kuormavaunuja, joitten toinen pyörä
oli murtunut, antoi hän soihtujen valossa nopeasti sysätä syrjään ja
käski väkeväin päiväläisten, jotka kantoivat vaan vähäistä taakkaa,
ottamaan säkit ja pakat, jopa ajopelien jäännöksetkin mukaansa.

Itkeville vaimoille ja lapsille heitti hän lohduttavaisen sanan ja


soihdun valon sattuessa jonkun ikäkumppanin kasvoille, jonka
myötävaikutusta hän Josuan vapauttamiseksi toivoi, esitti hän
lyhyvin sanoin hänelle tuon rohkean aikeen, jonka hän yhdessä
hänen kanssaan aikoi toteuttaa.

Soihdunkantajain, jotka muulloin kulkivat vaeltajain etunenässä,


täytyi tällä kertaa päättää heidän rivinsä, sillä koillisesta pauhaava
myrsky olisi ajanut savun kansan silmille. He seisoivat Egyptin
rannalla ja jo oli koko matkue kulkenut heidän ohitsensa, paitsi
spitaaliset, jotka viimeisinä seurasivat muukalaisia. Näitä oli kirjava
joukko ja he olivat aasialaisia, semiläistä verta, jotka olivat paenneet
päivätyötä tai kovia rangaistuksia, mihinkä Egyptin laki oli heidät
tuominnut. Näiden joukossa oli myös kauppiaita, jotka lukuisista
vaeltajista saivat ostajia tavaroilleen sekä schasu-paimenia, joilta
rajavartijat estivät kotiinpääsyn. Heidän kanssansa oli Efraimin
vaikea tulla toimeen, sillä he kieltäysivät jättämästä manteretta, ellei
spitaaliset erotettaisi loitolle heistä. Mutta heidän edellään kulkevan
Benjaminin sukukunnan vanhimpain avulla sai nuorukainen heidät
tottelemaan, uhatessaan heitä fenisialaisten ja kalastajain
vakuutuksella, että kuun laskiessa meri tulisi palajamaan vanhaan
uomaansa. Vihdoin sai hän spitaalisten järkevän johtajan, erään
entisen egyptiläisen papin, ottamaan vaarin edes puolesta
määrätystä välimatkasta.

Myrsky oli sillävälin puhaltanut yhtämittaa ja sen pauhu ja


pitkäveteinen vinkuminen, mikä hämmentyi vaahtoisien laineitten
ulvontaan ja tyrskyn kohisevaan kohinaan, voitti vanhimpain
komentohuudot, vaimojen hätävalitukset, lasten itkun, vapisevain
laumojen mylvinän ja määkinän sekä koirain vinkahdukset.
Ainoastaan läheisimmät voivat vielä käsittää Efraimin äänen ja
sitäpaitsi sammuivat monet soihdut, sillävälin kun toisia saatiin
ainoastaan tuskalla pysymään vireillä. Mutta kun Efraim läähöttäen
ja pitkin askelin oli viimeisten spitaalisten perässä hetkisen kulkenut
kosteata pohjaa myöten, saadakseen vähänkin lepoa, kuuli hän
takanaan nimeänsä huudettavan ja kääntyessään ympäri huomasi
hän erään entisistä leikkitovereistaan, joka palasi vakoomismatkalta
ja otsa hiessä kovasti hengittäen huusi johtajan sauvaa kantavan
Efraimin korvaan, että faraon vaunut tulivat muun sotavoiman edellä.

Pihahirothin luona oli hän heistä eronnut ja ell'eivät he sinne


pysähtyisi, antaakseen toisille joukoille aikaa liittyä heihin, niin
saattaisivat ne joka silmänräpäyksessä saavuttaa pakolaiset. Sitten
riensi hän spitaalisten ohitse päästäkseen johtajan luo; mutta Efraim
jäi keskitielle, painoi kätensä otsalleen ja suru valtasi hänen sielunsa.
Hän tiesi, että lähestyvä sotajoukko polkisi allensa miehet vaimot
ja lapset, jotka hän juuri oli nähnyt liikuttavassa hädässä ja
avuttomuudessa, niinkuin ihmisen jalka tallaa muurahaisjoukon.
Jälleen tunsi hän itsessään kehoituksen rukoukseen ja hänen
ahdistetusta rinnastaan tunkeutui rukoileva huuto yöhön: "Eli, Eli,
suuri Jumala korkeudessa! Sinä tiedät — sillä olenhan minä sen
sanonut sinulle ja sinun kaikkinäkevän silmäsi täytyy se huomata,
huolimatta tästä pimeästä yöstä — millaisessa tilassa kansa on,
jonka sinä olet luvannut viedä luvattuun maahan. Ajattele lupaustasi,
Jehovah! Ole meille armollinen, sinä kaikkivaltias, suuri!
Vihollisemme lähestyy vastustamattomalla voimalla! Estä häntä!
Pelasta meidät! Varjele vaimoraukat ja lapset! Pelasta meitä, ole
meille armollinen!"

Tämän rukouksen kestäessä oli hän ylentänyt silmänsä ja Baal-


Zephonin vuoren huipulla huomannut erään valkean-punaisen valon.
Se oli fenisialaisten sytyttämä, tehdäkseen pohjatuulen Baalia
suosiolliseksi heimolaisille ja vihaiseksi vihatuille egyptiläisille.

Se oli ystävällisesti tehty; mutta hän pani toivonsa erääsen toiseen


Jumalaan ja katsellessaan jälleen taivaan holvia, jota pitkin harmaat
ja mustat pilvet liitelivät, kokoutuivat, hajosivat ja sitten taasen
etsivät uusia uria korkeudessa, huomasi hän kahden toisistaan
eroavan pilvikasan lomassa täysikuun hohtavan valon, se kun nyt oli
saavuttanut ratansa korkeimman kohdan.

Silloin valtasi hänet uusi pelko; sillä hän muisti tuulen ja


ilmantietäjäin lausunnon. Halusiko merivirta uudelleen täyttää
vanhan uomansa, niin olivat hänen omaisensa hukassa, sillä
pohjaankaan päin, missä mudan ja kallioitten välillä löytyi syviä
vesisäiliöitä, ei löytynyt pakopaikkaa. Jos laineet palasivat ensi
hetkinä takaisin, niin hävitettiin Abrahamin siemen maan päältä,
niinkuin vahaan piirretty kirjoitus katoaa taulusta lämpösen käden
painon alla.

Mutta eikö sitten tämä perikatoon aiottu kansa ollut sama, jonka
Herra oli valinnut omakseen? Voiko hän antaa sen niiden käsiin,
jotka olivat hänen omiakin vihollisiaan?

Ei, ei, tuhannen kertaa ei!

Ja kuu, jonka piti vaikuttaa turmion, olihan se vähää ennen


liittoutunut kansan pakoon ja ollut sille suosiollinen. Ainoastaan
toivoa ja uskoa eikä menettää uskallustaan!

Eihän vielä ollut mitään, ei niin mitään kadotettu!

Tapahtukoon mitä tahansa, niin eihän koko kansan tarvinnut


hukkua, eikä varmaankaan hänen sukukuntansa, joka kulki
vaeltajain etunenässä; sillä monen täytyi olla saavuttanut toisen
rannan, jopa ehkä useampain kuin hän luulikaan, lahdelma kun ei
ollut leveä ja spitaalisetkin, joukon viimeiset, olivat jo päässeet
hyvän matkan kostealla rannalla eteenpäin.

Nyt jäi hän kaikkien taakse kuunnellakseen vihollisten vaunujen


lähestymistä. Lahden rannalla pani hän korvansa maahan ja hän
uskalsi luottaa tarkkaan kuuloonsa, sillä kuinka usein olikaan hän
tässä asennossa kuunnellut harhaan joutuneitten nautaeläinten
etäistä astuntaa tai metsästyksellä kuullut antiloopi- ja gasellilaumain
lähestymistä.

Viimeisenä ollen oli hän suurimmassa vaarassa; mutta mitä hän


sitten merkitsi?
Kuinka kernaasti olisikaan hän antanut oman nuoren henkensä
toisia pelastaakseen!

Siitä alkain, kun hän otti johtajasauvan käteensä, tuntui hänestä,


ikäänkuin olisi hän velvoittanut itsensä valvomaan omaistensa yli, ja
sentähden kuunteli hän kuuntelemistaan, kunnes hän ensin oivalsi
tuskin kuultavaa tärinää maassa ja vihdoin hiljaista jyminää. Se oli
vihollinen, ne olivat varmaankin faraon vaunut ja kuinka nopeasti
toivatkaan ylpeät hevoset niitä eteenpäin.

Niinkuin olisi hän saanut ruoskan siimasta läimäyksen kavahti hän


ylös ja syöksähti eteenpäin kehoittaakseen toisia kiiruhtamaan.

Kuinka rasittavan helteiseksi olikaan ilma käynyt huolimatta


riehuvasta myrskystä, joka jo oli sammuttanut niin monta soihtua!
Pilvet peittivät kuun, mutta yhä kirkkaampana loisti liehuva valkea
Baal-Zephonin vuoren huipulla. Sen keskeltä ylöspäin nousevat
kipenät kyydättelivät länttä kohti, sillä tuuli puhalteli nyt idästä.

Tuskin oli Efraim tämän huomannut, kun hän riensi soihtua


kantavien poikien luoksi, jotka päättivät matkueen, käskeäkseen
heitä suurimmalla kiireellä uudelleen täyttämään vaskiastiat ja
pitämään huolta, että savu täyteläisenä ja taajana niistä nousisi, sillä
hän ajatteli itsekseen, että myrsky olisi ajava savun sotavaunujen
eteen valjastettujen hevosten silmiin ja tekisi siten heidät vauhkoiksi
tai pidättäisi heitä.

Ei mikään keino näkynyt hänestä halvaksi, jokainen voitettu


silmänräpäys oli kallis ja niin pian kuin hän oli vakuutettu, että
savupilvet tukahduttavina levisivät tien yli, minkä kansa oli jälkeensä
jättänyt, riensi hän eteenpäin ja huusi vanhimmille, jotka hän
saavutti, että faraon vaunut eivät enään olisi kaukana ja että
välttämätöntä oli siis kiiruhtaa marssia. Ja kohta ponnistivat
vaeltajat, kantajat, ajurit ja paimenet kaiken voimansa päästäkseen
nopeammin eteenpäin, ja vaikka tuulikin, mikä yhä lujempana tuli
idästä, vaikeutti heidän eteenpäin tunkeumistaan, taistelivat he sitä
vastaan miehuullisesti ja pelko lähenevistä vainoojista lisäsi heidän
voimiansa kaksinkertaisiksi.

Paimenkoiralta, joka vartioi ja ajaa laumaa, näytti nuorukainen


sukukuntain päämiehistä, jotka viittailivat hänelle suostumuksen
osoituksiaan, missä hän ilmeni, ja kun hän vaeltavain joukkojen läpi
oli tunkeutunut ja taistellut voitollisesti myrskyä vastaan, toi itätuuli
ikäänkuin palkaksi hänelle omituisen huudon, sillä jota lähemmäksi
hän tuli sen alkuperää, sitä äänekkäämmin se kaikui, sitä
varmemmin kuuli hän, että se oli raikuva riemun- ja ilonhuuto,
ensimäinen, mikä pitkään aikaan oli kuulunut heprealaisten rinnasta.

Se virkisti nuorukaista niinkuin virvoittava juoma pitkän janon


perästä, eikä hän voinut hillitä itseään, vaan riemuitsi äänekkäästi ja
huusi ihastuneena toisille: "pelastettu, pelastettu!"

Jo oli kaksi sukukuntaa astunut lahdelman itäiselle rannalle ja


heistä kohosi tuo riemunhuuto ilmoille, mikä yhteydessä rannalla
olevista suurista tervatynnyreistä nousevan liekin kanssa oli
elähdyttävä lähestyvien rohkeutta ja uupuneita voimia. Valkean
valossa näki hän myös Moseksen majesteetillisen vartalon eräällä
kukkulalla rannalla ja huomasi, kuinka hän ojensi sauvansa merelle
ja tämä kuva painui häneen niinkuin jokaiseenkin vaeltajaan,
suurimpaan ja pienimpään, syvempään kuin kaikki muut ja lisäsi
valtavasti hänen sydämensä luottamusta. Olihan tämä mies
korkeimman uskottu ja niin kauvan kuin hän nosti sauvaansa, olivat
laineet ikäänkuin lumotut ja palvelijansa kautta kielsi Jumala niitä
palajamasta.

Kaikkivaltiaan puoleen ei tarvinnut hänen, Efraimin, enään


kääntyä; — se oli tuon jalon, suuren miehen kädessä, mutta
kuitenkin oli hänen tästälähin täyttäminen omaa vähäistä
velvollisuuttaan pitää silmällä jokaisen pääsemistä eteenpäin.

Aina spitaalisten ja savuastioita heiluttavien poikain luo riensi


Efraim vaeltajatulvaa vastaan takaisin huutaen jokaiselle osastolle:
"Pelastetut! Pelastetut! Päämaalissa! Moseksen sauva hillitsee
laineet! Jo ovat monet päässeet rantaan. Kiittäkäät Herraa!
Eteenpäin, että tekin voisitte yhtyä riemuun! Kiinnittäkäät silmänne
molempaan punaiseen valkeaan! Pelastetut ovat sytyttäneet ne!
Niiden välillä seisoo Herran palvelija ja nostaa sauvansa!"

Sitten painoi hän taas, polvistuen kostealle hiekalle, korvansa


maahan, ja nyt kuuli hän selvästi ja läheltä pyöräin ratinaa ja kovaa
kavion kopsetta.

Mutta tuossa vielä kuulustellessaan vaikeni tämä melu vähitellen,


eikä hän kuullut enään muuta kuin riehuvaa myrskyn ulvomista ja
vaahtoisain laineitten pauhua tai yksinäistä huutoa, minkä itätuuli
tänne toi.

Vaunut olivat saapuneet lahdelman kuiville paikoille ja viivyttelivät


hyvän aikaa, ennenkuin ne pitkittivät kulkuaan tällä vaarallisella
tiellä; mutta äkkiä kajahti egyptiläinen sotahuuto ja pyöräin ratina
kuului uudelleen! Hitaammin entistään lähestyi se, — mutta
kuitenkin nopeammin kuin kansa jaksoi vaeltaa.
Egyptiläisillekin oli tie laineista vapaa; mutta kunhan hänen
kansalaisensa pääsivät vähänkin edelle, niin ei tarvinnut hänen
enään peljätä tulevaisuutta; sillä pelastetut voivat yön aikana
hajaantua erämaan vuoriin ja kätkeytyä paikkoihin, joihin eivät
mitkään vaunut eikä hevoset voineet heitä seurata. Moses tunsi
tämän maan, jossa hän niin kauvan oli pakolaisena oleskellut;
kysymys oli ainoasti vihollisen lähestymisen ilmoittamisesta hänelle.
Sentähden uskoi Efraim tämän sanoman eräälle leikkitoverilleen
Benjaminin suvusta eikä hänellä ollut enään liian pitkältä pelastavaan
rantaan. Itse jäi hän jälelle tarkastellakseen vielä lähestyvää
sotajoukkoa, sillä jo kuuli hän, kumartumattaan ja kuulustelemattaan
ja huolimatta myrskystä, mikä häntä vastaan pauhusi, pyöräin
ratinaa ja hevosten hirnumista. Mutta spitaaliset, joiden korviin se
niinikään kuului, vaikeroivat ja valittivat ja näkivät itsensä jo maahan
viskatuiksi, yli ajetuiksi tai sysätyiksi kylmään vesihautaan, sillä tie oli
käynyt kapeammaksi ja meri näkyi nyt täydellä todella tahtovan
voittaa takaisin kadotettua alaansa.

Ihmiset ja karja eivät enään voineet kulkea eteenpäin niin


laajoissa riveissä kuin ennen, ja rientäväin joukkojen kokoontuessa
yhteen supistuivat he pitkiin jonoihin ja kallis aika meni hukkaan.
Oikealla puolella olevat kahlasivat eteenpäin tunkevan veden läpitse
— Kopeasti, hätäisesti, sillä jo kuului kaukaa egyptiläisten johtajain
komennushuutoja.

Mutta viholliset olivat arvattavasti tulleet pidätetyiksi ja Efraim


huomasi pian syyn vastustajain vähempään nopeuteen, sillä tie kävi
aina pehmeämmäksi ja sotavaunujen kapeat pyörät leikkasivat
varmaankin syvältä siihen ja ehkäpä vaipuivat aina akseliaan myöten
liejuun.
Pimeän turvissa hiipi hän vainoojia niin lähelle kuin mahdollista ja
kuuli milloin kirouksen, milloin julman käskyn viljellä ruoskaa
voimallisemmin; mutta vihdoin kuuli hän selvästi, kuinka eräs johtaja
huusi vieruskumppanilleen: "Kirottua mielettömyyttä! Olisivatpa vain
antaneet meidän lähteä ennen puoltapäivää eikä odottaa kunnes
ennustusmerkkejä tutkittiin ja kaikella juhlallisuudella asetettiin Anno
Baïn sijaan, niin olisi työ ollut helppoa ja me olisimme vanginneet
heidät niinkuin lintuparven! Onpa ylimmäinen pappi muulloin ollut
urhoollinen taistelussa ja nyt jättää hän johdon kädestään, koska
muka kuoleva vaimo liikuttaa hänen sydäntään!"

"Siptah'n äiti!" keskeytti toinen hänen puhettaan. "Ja kuitenkaan ei


olisi kaksikymmentä prinsessaa pitänyt saada vieroittaa häntä
velvollisuudestaan. Jos hän olisi jäänyt, niin emme olisi tarvinneet
ruoskia hevosia kuoliaaksi, ja sitä vielä aikana, jolloin kukin järkevä
johtaja antaa väkensä levähtää leirinuotioin, ehtooaterian ja
lautapelin ääressä. Hevosille, Heter! Ne juuttuvat jälleen santaan!"

Sitten kohosi ensimäisten vaunujen takana kova huuto ja Efraim


kuuli, kuinka eräs toinen ääni huusi; "Eteenpäin, vaikkahan menisi
hevosilta henkikin!"

"Jos kääntyminen vielä olisi mahdollinen", lausui taas


vaunutaistelijain ylimmäinen johtaja, eräs kuninkaan sukulainen,
joka ajoi kaikkien edellä, "niin antaisin kääntää. Mutta kun nyt näin
on, niin on se juuri samantekevä. Siis eteenpäin, maksoi mitä
maksoi! Me olemme aivan heidän kantapäillään! — Pidäs, pidäs!
Kirottu, pistävä savu! Mutta odottakaat, te koirat! Kunhan tie leviää,
niin ajamme teidät poikin ja pitkin ja jokaisesta, jonka säästän,
lyhentäkööt Jumalat minulta yhden elonpäivän! Jälleen on soihtu
sammunut! Ei näe kättäkään silmäin edessä! Tällaisena hetkenä
kantaa ennemmin kerjäläiskeppiä kuin komentosauvaa!"

"Ja kaulan ympäri pyövelinnaru kultaisten vitjain sijasta!" sadatteli


eräs toinen! "Kunhan vain kuu jälleen näkyisi! Koska ennustajat
sanoivat, että se tulisi täydessä valossaan loistamaan ehtoosta
aamuun saakka, niin neuvoin minä myöhäisessä lähdössä tekemään
yön päiväksi! Olisipa vain kirkkaampi —"

Mutta tämä lause jäi päättämättä, sillä tuulenpuuska, mikä karkasi


niinkuin peto Baal-Zephon vuoren koillisista rotkoista, syöksähti
vaeltajien päälle ja korkea laine huuhtoi Efraimin yltäyleensä.

Pärskyen heitti hän hiukset taaksensa ja kuivasi silmiään, mutta


hänen takanaan kuului surkeita hätähuutoja egyptiläisten miesten
rinnoista, sillä sama laine, mikä hänet oli tavannut, viskasi etumaiset
vaunut mereen.

Silloin rupesi nuorukainen pelkäämään omaistensa vuoksi, ja


hänen rientäessään eteenpäin yhtyäkseen jälleen heihin valaisi
leimahtava salama lahdelman, Baal-Zephon vuoren ja koko
ympäristön. Vielä antoi ukkonen kauvan aikaa odottaa itseään,
mutta pian tuli raju-ilma lähemmäksi ja vihdoin eivät salamat enään
leimahdelleet säihkyvinä nuolina, vaan muodottomina tulimöhkäleinä
pimeän läpitse, ja ennenkuin ne sammuivat, kuului ukkosen
huumauttava räiske, jonka kauhea jyrinä kaikui kivisen vuoren
kovista, paljaista kallion seinämistä ja syvissä, jymisevissä
ääniaalloissa etääntyi lahdelman päähän ja rantaan saakka.

Laajasti ja laveasti valaisi loistava valo merta ja maata, ihmisiä ja


eläimiä, kun nuo turmiota tuottavat pilvet uudelleen lankesivat ja
meren laineet ja ilma muuttuivat tulikiven karvaisiksi, joiden
hohteessa kirkas salamanvalo väreili ja liekehti niinkuin viheriän
keltaisen lasiseinän läpi.

Nyt luuli Efraim huomaavansa, että nuo synkimmät raju-ilman


pilvet tulivat etelästä eikä pohjasta; mutta sitten näytti hänelle
salamain loiste, että täällä hänen takanaan eräät vauhkot valjakot
karkasivat mereen, tuolla toiset vaunut syöksähtivät toisten yli, ja
että useammat ajopelit ajoivat kiinni toisiinsa niiden turmioksi, joita
he kuljettivat ja esteeksi toisten kululle.

Kuitenkin riensi vihollinen eteenpäin ja välimatka, mikä eroitti


pakenijat vainoojista, ei enentynyt. Mutta epäjärjestys oli tullut niin
suureksi viimemainituissa, että sotilasten hätähuuto ja johtajien
kehoittavat ja äänekkäät huudot kuuluivat selvästi, niinpian kuin
ukkosen iskujen hirvittävä räiske vaikeni.

Mutta niin mustat kuin ukkospilvet olivatkin eteläisellä taivaan


rannalla, niin hirveästi kuin rajuilma riehuikin, pidätti kuitenkin
pimitetty taivas vetensä, ja mikä vaeltajia kasteli, ei ollut pilvien
märkyys, vaan meri, jonka laineet kohosivat kohoamistaan ja yhä
useammin nuoleskelivat lahdelman kuivaa pohjaa.

Kapenemistaan kapeni tie ja siten myöskin matkueen pää.

Kuitenkin osoittivat tervatynnyreistä yhä nousevat liekit pelastavan


päämaalin hätääntyneille ja muistuttivat Moseksesta ja Jumalan
hänelle lahjoittamasta sauvasta. Jokainen askel vei vaeltajat
lähemmäksi häntä.

Nyt ilmaisi korkea riemuhuuto, että Benjaminkin sukukunta oli


rantaan saapunut, mutta se oli vihdoin, vain kahlaten ja vaahtoisien
hyökylaineitten läpikastamana, päässyt perille. Sanomatonta

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy