Download as pdf or txt
Download as pdf or txt
You are on page 1of 67

The Art of Multiprocessor

Programming. Second Edition Maurice


Herlihy
Visit to download the full and correct content document:
https://ebookmass.com/product/the-art-of-multiprocessor-programming-second-editio
n-maurice-herlihy/
The Art of Multiprocessor
Programming

SECOND EDITION

Maurice Herlihy

Nir Shavit

Victor Luchangco

Michael Spear
Table of Contents

Cover image

Title page

Copyright

Dedication

Preface

Organization

Prerequisites

Acknowledgments

Suggested ways to teach the art of multiprocessor programming

Practitioner track

Non-CS Major track

CS Major track
Chapter 1: Introduction

Abstract

1.1. Shared objects and synchronization

1.2. A fable

1.3. The producer–consumer problem

1.4. The readers–writers problem

1.5. The harsh realities of parallelization

1.6. Parallel programming

1.7. Chapter notes

1.8. Exercises

Bibliography

Part 1: Principles

Chapter 2: Mutual exclusion

Abstract

2.1. Time and events

2.2. Critical sections

2.3. Two-thread solutions

2.4. Notes on deadlock

2.5. The filter lock


2.6. Fairness

2.7. Lamport's Bakery algorithm

2.8. Bounded timestamps

2.9. Lower bounds on the number of locations

2.10. Chapter notes

2.11. Exercises

Bibliography

Chapter 3: Concurrent objects

Abstract

3.1. Concurrency and correctness

3.2. Sequential objects

3.3. Sequential consistency

3.4. Linearizability

3.5. Quiescent consistency

3.6. Formal definitions

3.7. Memory consistency models

3.8. Progress conditions

3.9. Remarks

3.10. Chapter notes


3.11. Exercises

Bibliography

Chapter 4: Foundations of shared memory

Abstract

4.1. The space of registers

4.2. Register constructions

4.3. Atomic snapshots

4.4. Chapter notes

4.5. Exercises

Bibliography

Chapter 5: The relative power of primitive synchronization


operations

Abstract

5.1. Consensus numbers

5.2. Atomic registers

5.3. Consensus protocols

5.4. FIFO queues

5.5. Multiple assignment objects

5.6. Read–modify–write operations


5.7. Common2 RMW operations

5.8. The compareAndSet operation

5.9. Chapter notes

5.10. Exercises

Bibliography

Chapter 6: Universality of consensus

Abstract

6.1. Introduction

6.2. Universality

6.3. A lock-free universal construction

6.4. A wait-free universal construction

6.5. Chapter notes

6.6. Exercises

Bibliography
Part 2: Practice

Chapter 7: Spin locks and contention

Abstract

7.1. Welcome to the real world

7.2. Volatile fields and atomic objects


7.3. Test-and-set locks

7.4. Exponential back-off

7.5. Queue locks

7.6. A queue lock with timeouts

7.7. Hierarchical locks

7.8. A composite lock

7.9. A fast path for threads running alone

7.10. One lock to rule them all

7.11. Chapter notes

7.12. Exercises

Bibliography

Chapter 8: Monitors and blocking synchronization

Abstract

8.1. Introduction

8.2. Monitor locks and conditions

8.3. Readers–writers locks

8.4. Our own reentrant lock

8.5. Semaphores

8.6. Chapter notes


8.7. Exercises

Bibliography

Chapter 9: Linked lists: The role of locking

Abstract

9.1. Introduction

9.2. List-based sets

9.3. Concurrent reasoning

9.4. Coarse-grained synchronization

9.5. Fine-grained synchronization

9.6. Optimistic synchronization

9.7. Lazy synchronization

9.8. Nonblocking synchronization

9.9. Discussion

9.10. Chapter notes

9.11. Exercises

Bibliography

Chapter 10: Queues, memory management, and the ABA problem

Abstract

10.1. Introduction
10.2. Queues

10.3. A bounded partial queue

10.4. An unbounded total queue

10.5. A lock-free unbounded queue

10.6. Memory reclamation and the ABA problem

10.7. Dual data structures

10.8. Chapter notes

10.9. Exercises

Bibliography

Chapter 11: Stacks and elimination

Abstract

11.1. Introduction

11.2. An unbounded lock-free stack

11.3. Elimination

11.4. The elimination back-off stack

11.5. Chapter notes

11.6. Exercises

Bibliography

Chapter 12: Counting, sorting, and distributed coordination


Abstract

12.1. Introduction

12.2. Shared counting

12.3. Software combining

12.4. Quiescently consistent pools and counters

12.5. Counting networks

12.6. Diffracting trees

12.7. Parallel sorting

12.8. Sorting networks

12.9. Sample sorting

12.10. Distributed coordination

12.11. Chapter notes

12.12. Exercises

Bibliography

Chapter 13: Concurrent hashing and natural parallelism

Abstract

13.1. Introduction

13.2. Closed-address hash sets

13.3. A lock-free hash set


13.4. An open-address hash set

13.5. Chapter notes

13.6. Exercises

Bibliography

Chapter 14: Skiplists and balanced search

Abstract

14.1. Introduction

14.2. Sequential skiplists

14.3. A lock-based concurrent skiplist

14.4. A lock-free concurrent skiplist

14.5. Concurrent skiplists

14.6. Chapter notes

14.7. Exercises

Bibliography

Chapter 15: Priority queues

Abstract

15.1. Introduction

15.2. An array-based bounded priority queue

15.3. A tree-based bounded priority queue


15.4. An unbounded heap-based priority queue

15.5. A skiplist-based unbounded priority queue

15.6. Chapter notes

15.7. Exercises

Bibliography

Chapter 16: Scheduling and work distribution

Abstract

16.1. Introduction

16.2. Analyzing parallelism

16.3. Realistic multiprocessor scheduling

16.4. Work distribution

16.5. Work-stealing deques

16.6. Chapter notes

16.7. Exercises

Bibliography

Chapter 17: Data parallelism

Abstract

17.1. MapReduce

17.2. Stream computing


17.3. Chapter notes

17.4. Exercises

Bibliography

Chapter 18: Barriers

Abstract

18.1. Introduction

18.2. Barrier implementations

18.3. Sense reversing barrier

18.4. Combining tree barrier

18.5. Static tree barrier

18.6. Termination detection barriers

18.7. Chapter notes

18.8. Exercises

Bibliography

Chapter 19: Optimism and manual memory management

Abstract

19.1. Transitioning from Java to C++

19.2. Optimism and explicit reclamation

19.3. Protecting pending operations


19.4. An object for managing memory

19.5. Traversing a list

19.6. Hazard pointers

19.7. Epoch-based reclamation

19.8. Chapter notes

19.9. Exercises

Bibliography

Chapter 20: Transactional programming

Abstract

20.1. Challenges in concurrent programming

20.2. Transactional programming

20.3. Hardware support for transactional programming

20.4. Transactional lock elision

20.5. Transactional memory

20.6. Software transactions

20.7. Combining hardware and software transactions

20.8. Transactional data structure design

20.9. Chapter notes

20.10. Exercises
Bibliography

Appendix A: Software basics

A.1. Introduction

A.2. Java

A.3. The Java memory model

A.4. C++

A.5. C#

A.6. Appendix notes

Bibliography

Appendix B: Hardware basics

B.1. Introduction (and a puzzle)

B.2. Processors and threads

B.3. Interconnect

B.4. Memory

B.5. Caches

B.6. Cache-conscious programming, or the puzzle solved

B.7. Multicore and multithreaded architectures

B.8. Hardware synchronization instructions

B.9. Appendix notes


B.10. Exercises

Bibliography

Bibliography

Bibliography

Index
Copyright
Morgan Kaufmann is an imprint of Elsevier
50 Hampshire Street, 5th Floor, Cambridge, MA 02139, United States

Copyright © 2021 Elsevier Inc. All rights reserved.

No part of this publication may be reproduced or transmitted in any


form or by any means, electronic or mechanical, including
photocopying, recording, or any information storage and retrieval
system, without permission in writing from the publisher. Details on
how to seek permission, further information about the Publisher's
permissions policies and our arrangements with organizations such
as the Copyright Clearance Center and the Copyright Licensing
Agency, can be found at our website:
www.elsevier.com/permissions.

This book and the individual contributions contained in it are


protected under copyright by the Publisher (other than as may be
noted herein).

Notices
Knowledge and best practice in this field are constantly changing.
As new research and experience broaden our understanding,
changes in research methods, professional practices, or medical
treatment may become necessary.

Practitioners and researchers must always rely on their own


experience and knowledge in evaluating and using any
information, methods, compounds, or experiments described
herein. In using such information or methods they should be
mindful of their own safety and the safety of others, including
parties for whom they have a professional responsibility.

To the fullest extent of the law, neither the Publisher nor the
authors, contributors, or editors, assume any liability for any injury
and/or damage to persons or property as a matter of products
liability, negligence or otherwise, or from any use or operation of
any methods, products, instructions, or ideas contained in the
material herein.

Library of Congress Cataloging-in-Publication Data


A catalog record for this book is available from the Library of
Congress

British Library Cataloguing-in-Publication Data


A catalogue record for this book is available from the British Library

ISBN: 978-0-12-415950-1

For information on all Morgan Kaufmann publications visit our


website at https://www.elsevier.com/books-and-journals

Publisher: Katey Birtcher


Acquisitions Editor: Stephen R. Merken
Editorial Project Manager: Beth LoGiudice
Production Project Manager: Beula Christopher
Designer: Renee Duenow

Typeset by VTeX
Dedication

For my parents, David and Patricia Herlihy, and for Liuba, David, and
Anna.

– M.H.

For Noun and Aliza, Shafi, Yonadav, and Lior, and for Luisa.

– N.S.

For my family, especially my parents, Guilly and Maloy Luchangco, and


for God, who makes all things possible.

– V.L.

For Emily, Theodore, Bernadette, Adelaide, Teresa, Veronica, Phoebe, Leo,


and Rosemary.
– M.S.
Preface
In the decade since the first edition, this book has become a staple
of undergraduate and graduate courses at universities around the
world. It has also found a home on the bookshelves of practitioners
at companies large and small. The audience for the book has, in turn,
advanced the state of the art in multiprocessor programming. In this
second edition, we aim to continue this “virtuous cycle” by
providing new and updated content. Our goal is the same as with
the first edition: to provide a textbook for a senior-level
undergraduate course and a reference for practitioners.

Organization
The first part of this book covers the principles of concurrent
programming, showing how to think as a concurrent programmer,
developing fundamental skills such as understanding when
operations “happen,” considering all possible interleavings, and
identifying impediments to progress. Like many skills—driving a
car, cooking a meal, or appreciating caviar—thinking concurrently
must be cultivated, and it can be learned with moderate effort.
Readers who want to start programming right away may skip most
of this section but should still read Chapters 2 and 3, which cover the
basic ideas necessary to understand the rest of the book.
We first look at the classic mutual exclusion problem (Chapter 2).
This chapter is essential for understanding why concurrent
programming is a challenge. It covers basic concepts such as fairness
and deadlock. We then ask what it means for a concurrent program
to be correct (Chapter 3). We consider several alternative conditions
and the circumstances under which one might want to use each one.
We examine the properties of shared memory essential to concurrent
computation (Chapter 4), and we look at the kinds of
synchronization primitives needed to implement highly concurrent
data structures (Chapters 5 and 6).
We think it is essential that anyone who wants to become truly
skilled in the art of multiprocessor programming spend time solving
the problems presented in the first part of this book. Although these
problems are idealized, they distill the kind of thinking necessary to
write effective multiprocessor programs. Most importantly, they
distill the style of thinking necessary to avoid the common mistakes
committed by nearly all novice programmers when they first
encounter concurrency.
The second part of the book describes the practice of concurrent
programming. For most of this part, we give examples in Java to
avoid getting mired in low-level details. However, we have
expanded this edition to include discussion of some low-level issues
that are essential to understanding multiprocessor systems and how
to program them effectively. We use examples in C++ to illustrate
these issues.
Each chapter has a secondary theme, illustrating either a particular
programming pattern or an algorithmic technique. Chapter 7 covers
spin locks and contention, and introduces the importance of the
underlying architecture: spin lock performance cannot be
understood without understanding the multiprocessor memory
hierarchy. Chapter 8 covers monitor locks and waiting, a common
synchronization idiom.
Several chapters cover concurrent data structures. Linked lists,
which illustrate different kinds of synchronization patterns, from
coarse-grained locking to fine-grained locking to lock-free structures,
are covered in Chapter 9. This chapter should be read before the
remaining chapters, which depend on it. First-in-first-out (FIFO)
queues illustrate the “ABA problem” that arises when using atomic
synchronization primitives (Chapter 10); stacks illustrate an
important synchronization pattern called elimination (Chapter 11);
hash maps show how an algorithm can exploit natural parallelism
(Chapter 13); skip lists illustrate efficient parallel search (Chapter 14);
p p p p
priority queues illustrate how one can sometimes relax correctness
guarantees to enhance performance (Chapter 15).
We also consider other fundamental problems in concurrent
computing. Chapter 12 describes counting and sorting, two classic
problems with nuanced concurrent solutions. Breaking a program
into parallelizable tasks and organizing their execution is an
essential skill for concurrent programming, and we consider several
ways to do this, including work stealing and distribution (Chapter
16), data parallelism (Chapter 17), barriers (Chapter 18), and
transactional programming (Chapter 20). Memory management is
another fundamental challenge for concurrent programs, and we
discuss how to manually reclaim memory in Chapter 19. Because
Java provides automatic memory management, we use C++ to
illustrate these issues.
Much of these latter chapters are new to this edition: Chapters 17
and 19 are completely new, and Chapters 16 and 20 have been
substantially updated from the first edition. In particular, Chapter 20
now covers hardware primitives for transactional programming as
well as software strategies, and the examples have been recast in C++
to allow us to focus on lower-level mechanisms.

In theory, there is no difference between theory and practice. In


practice, there is.

Although the origin of this quote is uncertain, it is relevant to the


subject of this book. For the greatest benefit, a reader must
supplement learning the conceptual material presented in this book
with actual experience programming real multiprocessor systems.

Prerequisites
The prerequisites for the second edition are largely the same as for
the first. To understand the algorithms and their properties, readers
will need some knowledge of discrete mathematics, especially “big-
O” notation and what it means for a problem to be NP-complete,
and data structures such as stacks, queues, lists, balanced trees, and
hash tables. It is also helpful to be familiar with elementary
computer architecture and system constructs such as processors,
threads, and caches. While a course on operating systems or
computer organization would suffice, neither is necessary; dozens of
universities have used this book successfully without either
prerequisite.
A basic understanding of Java or C++ is needed to follow the
examples. When we require advanced language features or
advanced understanding of hardware, we provide an explanation
first. More details about programming language constructs and
multiprocessor hardware architectures are covered in Appendix A
and Appendix B, respectively.
Acknowledgments
We would like to thank our colleagues, students, and friends, who
provided guidance, comments, and suggestions during the writing
of this book: Yehuda Afek, Shai Ber, Hans Boehm, Martin Buchholz,
Vladimir Budovsky, Christian Cachin, Cliff Click, Yoav Cohen, Tom
Cormen, Michael Coulombe, Dave Dice, Alexandra Fedorova, Pascal
Felber, Christof Fetzer, Rati Gelasvili, Mohsen Ghaffari, Brian Goetz,
Shafi Goldwasser, Rachid Guerraoui, Tim Harris, Will Hasenplaugh,
Steve Heller, Danny Hendler, Maor Hizkiev, Alex Kogan, Justin
Kopinsky, Hank Korth, Eric Koskinen, Christos Kozyrakis, Edya
Ladan, Doug Lea, Oren Lederman, Will Leiserson, Pierre Leone,
Yossi Lev, Wei Lu, Virendra Marathe, Kevin Marth, Alex Matveev,
John Mellor-Crummey, Mark Moir, Adam Morrison, Dan
Nussbaum, Roberto Palmieri, Kiran Pamnany, Ben Pere, Radia
Perlman, Torvald Riegel, Ron Rivest, Vijay Saraswat, Bill Scherer,
Warren Schudy, Michael Scott, Ori Shalev, Marc Shapiro, Michael
Sipser, Yotam Soen, Ralf Suckow, Seth Syberg, Joseph Tassarotti,
John Tristan, George Varghese, Alex Weiss, Kelly Zhang, and
Zhenyuan Zhao. We apologize for any names inadvertently omitted.
We also extend our appreciation to the many people who have
sent us errata to improve the book, including: Matthew Allen, Rajeev
Alur, Karolos Antoniadis, Liran Barsisa, Cristina Basescu, Igor
Berman, Konstantin Boudnik, Bjoern Brandenburg, Kyle Cackett,
Mario Calha, Michael Champigny, Neill Clift, Eran Cohen, Daniel B.
Curtis, Gil Danziger, Venkat Dhinakaran, Wan Fokkink, David Fort,
Robert P. Goddard, Enes Goktas, Bart Golsteijn, K. Gopinath, Jason
T. Greene, Dan Grossman, Tim Halloran, Muhammad Amber
Hassaan, Matt Hayes, Francis Hools, Ben Horowitz, Barak Itkin,
Paulo Janotti, Kyungho Jeon, Irena Karlinsky, Ahmed
Khademzadeh, Habib Khan, Omar Khan, Namhyung Kim, Guy
Korland, Sergey Kotov, Jonathan Lawrence, Adam MacBeth, Mike
Maloney, Tim McIver, Sergejs Melderis, Bartosz Milewski, Jose
Pedro Oliveira, Dale Parson, Jonathan Perry, Amir Pnueli, Pat
Quillen, Sudarshan Raghunathan, Binoy Ravindran, Roei Raviv,
Jean-Paul Rigault, Michael Rueppel, Mohamed M. Saad, Assaf
Schuster, Konrad Schwarz, Nathar Shah, Huang-Ti Shih, Joseph P.
Skudlarek, James Stout, Mark Summerfield, Deqing Sun, Fuad
Tabba, Binil Thomas, John A Trono, Menno Vermeulen, Thomas
Weibel, Adam Weinstock, Chong Xing, Jaeheon Yi, and Ruiwen Zuo.
We are also grateful to Beula Christopher, Beth LoGiudice, Steve
Merken, and the staff at Morgan Kaufmann for their patience and
assistance throughout the process of bringing this book to print.
Suggested ways to teach the art
of multiprocessor programming
Here are three alternative tracks for teaching a multiprocessor
programming course using the material in the book.
The first track is a short course for practitioners, focusing on
techniques that can be applied directly to problems at hand.
The second track is a longer course for students who are not
Computer Science majors but who want to learn the basics of
multiprocessor programming as well as techniques likely to be
useful in their own areas.
The third track is a semester-long course for Computer Science
majors, either upper-level undergraduates or graduate students.

Practitioner track
Cover Chapter 1, emphasizing Amdahl's law and its implications. In
Chapter 2, cover Sections 2.1 to 2.4 and Section 2.7. Mention the
implications of the impossibility proofs in Section 2.9. In Chapter 3,
skip Sections 3.5 and 3.6.
Cover Chapter 7, except for Sections 7.7, 7.8, and 7.9. Chapter 8,
which deals with monitors and reentrant locks, may be familiar to
some practitioners. Skip Section 8.5 on semaphores.
Cover Chapters 9 and 10, except for Section 10.7, and cover
Sections 11.1 and 11.2. Skip the material in Chapter 11 from Section
11.3 and onwards. Skip Chapter 12.
Cover Chapters 13 and 14. Skip Chapter 15. Cover Chapter 16,
except for Section 16.5. Chapter 17 is optional. In Chapter 18, teach
Sections 18.1 to 18.3. For practitioners who focus on C++, Chapter 19
is essential and can be covered at any point after Chapter 9 and
Section 10.6. Chapter 20 is optional.

Non-CS Major track


Cover Chapter 1, emphasizing Amdahl's law and its implications. In
Chapter 2, cover Sections 2.1 to 2.4, 2.6, and 2.7. Mention the
implications of the impossibility proofs in Section 2.9. In Chapter 3,
skip Section 3.6.
Cover the material in Sections 4.1 and 4.2 and Chapter 5. Mention
the universality of consensus, but skip Chapter 6.
Cover Chapter 7, except for Sections 7.7, 7.8, and 7.9. Cover
Chapter 8.
Cover Chapters 9 and 10, except for Section 10.7, and cover
Chapter 11. Skip Chapter 12.
Cover Chapters 13 and 14. Skip Chapter 15. Cover Chapters 16
and 17. In Chapter 18, teach Sections 18.1 to 18.3. For practitioners
who focus on C++, Chapter 19 is essential and can be covered at any
point after Chapter 9 and Section 10.6. Chapter 20 is optional. In
Chapter 20, cover up to Section 20.3.

CS Major track
The slides on the companion website were developed for a semester-
long course.
Cover Chapters 1 and 2 (Section 2.8 is optional) and Chapter 3
(Section 3.6 is optional). Cover Chapters 4, 5, and 6. Before starting
Chapter 7, it may be useful to review basic multiprocessor
architecture (Appendix B).
Cover Chapter 7 (Sections 7.7, 7.8, and 7.9 are optional). Cover
Chapter 8 if your students are unfamiliar with Java monitors and
have not taken a course on operating systems. Cover Chapters 9 and
10 (Section 10.7 is optional). Cover Chapters 11, 12 (Sections 12.7,
12.8, and 12.9 are optional), 13, and 14.
The remainder of the book should be covered as needed for degree
requirements. For Math or Computer Science majors, Chapter 15
should come next, followed by Chapters 16 and 17. For Data Science
majors, Chapter 15 can be skipped so that more emphasis can be
placed on Chapters 16, 17, and 18. For Computer Engineering
majors, emphasis should be placed on Chapters 18, 19, and 20. In the
end, the instructor should of course take into account students'
interests and backgrounds.
Chapter 1: Introduction
Abstract
This chapter introduces and motivates the study of shared-
memory multiprocessor programming, or concurrent
programming. It describes the overall plan of the book, and then
presents some basic concepts of concurrent computation, and
presents some of the fundamental problems—mutual exclusion,
the producer–consumer problem, and the readers–writers
problem—and some simple approaches to solve these problems.
It ends with a brief discussion of Amdahl's law.

Keywords
parallelism; concurrent programming; shared-memory
multiprocessors; safety; liveness; mutual exclusion; coordination
protocol; producer—consumer problem; readers–writers problem;
deadlock-freedom; starvation-freedom; Amdahl's law
At the dawn of the twenty-first century, the computer industry
underwent yet another revolution. The major chip manufacturers
had increasingly been unable to make processor chips both smaller
and faster. As Moore's law approached the end of its 50-year reign,
manufacturers turned to “multicore” architectures, in which
multiple processors (cores) on a single chip communicate directly
through shared hardware caches. Multicore chips make computing
more effective by exploiting parallelism: harnessing multiple circuits
to work on a single task.
The spread of multiprocessor architectures has had a pervasive
effect on how we develop software. During the twentieth century,
advances in technology brought regular increases in clock speed, so
software would effectively “speed up” by itself over time. In this
century, however, that “free ride” has come to an end. Today,
advances in technology bring regular increases in parallelism, but
only minor increases in clock speed. Exploiting that parallelism is
one of the outstanding challenges of modern computer science.
This book focuses on how to program multiprocessors that
communicate via a shared memory. Such systems are often called
shared-memory multiprocessors or, more recently, multicores.
Programming challenges arise at all scales of multiprocessor systems
—at a very small scale, processors within a single chip need to
coordinate access to a shared memory location, and on a large scale,
processors in a supercomputer need to coordinate the routing of
data. Multiprocessor programming is challenging because modern
computer systems are inherently asynchronous: activities can be
halted or delayed without warning by interrupts, preemption, cache
misses, failures, and other events. These delays are inherently
unpredictable, and can vary enormously in scale: a cache miss might
delay a processor for fewer than ten instructions, a page fault for a
few million instructions, and operating system preemption for
hundreds of millions of instructions.
We approach multiprocessor programming from two
complementary directions: principles and practice. In the principles
part of this book, we focus on computability: figuring out what can be
computed in an asynchronous concurrent environment. We use an
idealized model of computation in which multiple concurrent threads
manipulate a set of shared objects. The sequence of the thread
operations on the objects is called the concurrent program or
concurrent algorithm. This model is essentially the model presented
by threads in Java, C#, and C++.
Surprisingly, there are easy-to-specify shared objects that cannot
be implemented by any concurrent algorithm. It is therefore
important to understand what not to try, before proceeding to write
multiprocessor programs. Many of the issues that will land
multiprocessor programmers in trouble are consequences of
fundamental limitations of the computational model, so we view the
p
acquisition of a basic understanding of concurrent shared-memory
computability as a necessary step. The chapters dealing with
principles take the reader through a quick tour of asynchronous
computability, attempting to expose various computability issues,
and how they are addressed through the use of hardware and
software mechanisms.
An important step in the understanding of computability is the
specification and verification of what a given program actually does.
This is perhaps best described as program correctness. The correctness
of multiprocessor programs, by their very nature, is more complex
than that of their sequential counterparts, and requires a different set
of tools, even for the purpose of “informal reasoning” (which, of
course, is what most programmers actually do).
Sequential correctness is mostly concerned with safety properties.
A safety property states that some “bad thing” never happens. For
example, a traffic light never displays green in all directions, even if
the power fails. Naturally, concurrent correctness is also concerned
with safety, but the problem is much, much harder, because safety
must be ensured despite the vast number of ways that the steps of
concurrent threads can be interleaved. Equally important,
concurrent correctness encompasses a variety of liveness properties
that have no counterparts in the sequential world. A liveness
property states that a particular good thing will happen. For
example, a red traffic light will eventually turn green.
A final goal of the part of the book dealing with principles is to
introduce a variety of metrics and approaches for reasoning about
concurrent programs, which will later serve us when discussing the
correctness of real-world objects and programs.
The second part of the book deals with the practice of
multiprocessor programming, and focuses on performance.
Analyzing the performance of multiprocessor algorithms is also
different in flavor from analyzing the performance of sequential
programs. Sequential programming is based on a collection of well-
established and well-understood abstractions. When we write a
sequential program, we can often ignore that underneath it all, pages
are being swapped from disk to memory, and smaller units of
g pp y
memory are being moved in and out of a hierarchy of processor
caches. This complex memory hierarchy is essentially invisible,
hiding behind a simple programming abstraction.
In the multiprocessor context, this abstraction breaks down, at
least from a performance perspective. To achieve adequate
performance, programmers must sometimes “outwit” the
underlying memory system, writing programs that would seem
bizarre to someone unfamiliar with multiprocessor architectures.
Someday, perhaps, concurrent architectures will provide the same
degree of efficient abstraction as sequential architectures, but in the
meantime, programmers should beware.
The practice part of the book presents a progressive collection of
shared objects and programming tools. Every object and tool is
interesting in its own right, and we use each one to expose the reader
to higher-level issues: spin locks illustrate contention, linked lists
illustrate the role of locking in data structure design, and so on. Each
of these issues has important consequences for program
performance. We hope that readers will understand the issue in a
way that will later allow them to apply the lessons learned to specific
multiprocessor systems. We culminate with a discussion of state-of-
the-art technologies such as transactional memory.
For most of this book, we present code in the Java programming
language, which provides automatic memory management.
However, memory management is an important aspect of
programming, especially concurrent programming. So, in the last
two chapters, we switch to C++. In some cases, the code presented is
simplified by omitting nonessential details. Complete code for all the
examples is available on the book's companion website at
https://textbooks.elsevier.com/web/product_details.aspx?
isbn=978124159501.
There are, of course, other languages which would have worked
as well. In the appendix, we explain how the concepts expressed
here in Java or C++ can be expressed in some other popular
languages or libraries. We also provide a primer on multiprocessor
hardware.
Throughout the book, we avoid presenting specific performance
numbers for programs and algorithms, instead focusing on general
trends. There is a good reason why: multiprocessors vary greatly,
and what works well on one machine may work significantly less
well on another. We focus on general trends to ensure that
observations are not tied to specific platforms at specific times.
Each chapter has suggestions for further reading, along with
exercises suitable for Sunday morning entertainment.

1.1 Shared objects and synchronization


On the first day of your new job, your boss asks you to find all
primes between 1 and 1010 (never mind why) using a parallel
machine that supports ten concurrent threads. This machine is
rented by the minute, so the longer your program takes, the more it
costs. You want to make a good impression. What do you do?
As a first attempt, you might consider giving each thread an equal
share of the input domain. Each thread might check 109 numbers, as
shown in Fig. 1.1. This approach fails to distribute the work evenly
for an elementary but important reason: Equal ranges of inputs do
not produce equal amounts of work. Primes do not occur uniformly;
there are more primes between 1 and 109 than between and
1010. To make matters worse, the computation time per prime is not
the same in all ranges: it usually takes longer to test whether a large
number is prime than a small number. In short, there is no reason to
believe that the work will be divided equally among the threads, and
it is not clear even which threads will have the most work.
FIGURE 1.1 Balancing the work load by dividing up the
input domain. Each thread in {0..9} gets an equal subset of
the range.

A more promising way to split the work among the threads is to


assign each thread one integer at a time (Fig. 1.2). When a thread is
finished testing an integer, it asks for another. To this end, we
introduce a shared counter, an object that encapsulates an integer
value, and that provides a () method, which increments
the counter's value and returns the counter's prior value.

FIGURE 1.2 Balancing the work load using a shared


counter. Each thread is given a dynamically determined
number of numbers to test.

Fig. 1.3 shows a naïve implementation of in Java. This


counter implementation works well when used by a single thread,
but it fails when shared by multiple threads. The problem is that the
expression
FIGURE 1.3 An implementation of the shared counter.

is in effect an abbreviation of the following, more complex code:

In this code fragment, is a field of the object, and is


shared among all the threads. Each thread, however, has its own
copy of , which is a local variable to each thread.
Now imagine that two threads call the counter's ()
method at about the same time, so that they both read 1 from . In
this case, each thread would set its local variables to 1, set to
2, and return 1. This behavior is not what we intended: we expect
concurrent calls to the counter's () to return distinct
values. It could be worse: after one thread reads 1 from , but
before it sets to 2, another thread could go through the
increment loop several times, reading 1 and writing 2, then reading 2
and writing 3. When the first thread finally completes its operation
and sets to 2, it will actually be setting the counter back from 3 to
2.
The heart of the problem is that incrementing the counter's value
requires two distinct operations on the shared variable: reading the
field into a temporary variable and writing it back to the
object.
Something similar happens when you try to pass someone
approaching you head-on in a corridor. You may find yourself
veering right and then left several times to avoid the other person
doing exactly the same thing. Sometimes you manage to avoid
bumping into them and sometimes you do not. In fact, as we will see
in the later chapters, such collisions are provably unavoidable.1 On
an intuitive level, what is going on is that each of you is performing
two distinct steps: looking at (“reading”) the other's current position,
and moving (“writing”) to one side or the other. The problem is,
when you read the other's position, you have no way of knowing
whether they have decided to stay or move. In the same way that
you and the annoying stranger must decide on which side to pass
each other, threads accessing a shared must decide who goes
first and who goes second.
As we discuss in Chapter 5, modern multiprocessor hardware
provides special read–modify–write instructions that allow threads to
read, modify, and write a value to memory in one atomic (that is,
indivisible) hardware step. For the object, we can use such
hardware to increment the counter atomically.
We can also ensure atomic behavior by guaranteeing in software
(using only read and write instructions) that only one thread
executes the read-and-write sequence at a time. The problem of
ensuring that only one thread can execute a particular block of code
at a time, called the mutual exclusion problem, is one of the classic
coordination problems in multiprocessor programming.
As a practical matter, you are unlikely ever to find yourself having
to design your own mutual exclusion algorithm (you would
probably call on a library). Nevertheless, understanding how to
implement mutual exclusion from the basics is an essential condition
for understanding concurrent computation in general. There is no
more effective way to learn how to reason about essential and
ubiquitous issues such as mutual exclusion, deadlock, bounded
fairness, and blocking versus nonblocking synchronization.
1.2 A fable
Instead of treating coordination problems (such as mutual exclusion)
as programming exercises, we prefer to frame concurrent
coordination problems as interpersonal problems. In the next few
sections, we present a sequence of fables, illustrating some of the
basic problems. Like most authors of fables, we retell stories mostly
invented by others (see the chapter notes at the end of this chapter).
Alice and Bob are neighbors, and they share a yard. Alice owns a
cat and Bob owns a dog. Both pets like to run around in the yard, but
(naturally) they do not get along. After some unfortunate
experiences, Alice and Bob agree that they should coordinate to
make sure that both pets are never in the yard at the same time. Of
course, they rule out trivial solutions that do not allow either pet into
an empty yard, or that reserve the yard exclusively to one pet or the
other.
How should they do it? Alice and Bob need to agree on mutually
compatible procedures for deciding what to do. We call such an
agreement a coordination protocol (or just a protocol, for short).
The yard is large, so Alice cannot simply look out of the window
to check whether Bob's dog is present. She could perhaps walk over
to Bob's house and knock on the door, but that takes a long time, and
what if it rains? Alice might lean out the window and shout “Hey
Bob! Can I let the cat out?” The problem is that Bob might not hear
her. He could be watching TV, visiting his girlfriend, or out
shopping for dog food. They could try to coordinate by cell phone,
but the same difficulties arise if Bob is in the shower, driving
through a tunnel, or recharging his phone's batteries.
Alice has a clever idea. She sets up one or more empty beer cans
on Bob's windowsill (Fig. 1.4), ties a string around each one, and
runs the string back to her house. Bob does the same. When she
wants to send a signal to Bob, she yanks the string to knock over one
of the cans. When Bob notices a can has been knocked over, he resets
the can.
FIGURE 1.4 Communicating with cans.

Up-ending beer cans by remote control may seem like a creative


idea, but it does not solve this problem. The problem is that Alice can
place only a limited number of cans on Bob's windowsill, and sooner
or later, she is going to run out of cans to knock over. Granted, Bob
resets a can as soon as he notices it has been knocked over, but what
if he goes to Cancún for spring break? As long as Alice relies on Bob
to reset the beer cans, sooner or later, she might run out.
So Alice and Bob try a different approach. Each one sets up a
flagpole, easily visible to the other. When Alice wants to release her
cat, she does the following:

1. She raises her flag.


2. When Bob's flag is lowered, she releases her cat.
3. When her cat comes back, she lowers her flag.

When Bob wants to release his dog, his behavior is a little more
complicated:

1. He raises his flag.


2. While Alice's flag is raised
a. Bob lowers his flag,
b. Bob waits until Alice's flag is lowered,
c. Bob raises his flag.
3. As soon as his flag is raised and hers is down, he releases his
dog.
4. When his dog comes back, he lowers his flag.
This protocol rewards further study as a solution to Alice and
Bob's problem. On an intuitive level, it works because of the
following flag principle: If Alice and Bob each

1. raises his or her own flag, and then


2. looks at the other's flag,

then at least one will see the other's flag raised (clearly, the last one
to look will see the other's flag raised) and will not let his or her pet
enter the yard. However, this observation does not prove that the pets
will never be in the yard together. What if, for example, Alice lets her
cat in and out of the yard several times while Bob is looking?
To prove that the pets will never be in the yard together, assume
by way of contradiction that there is a way the pets could end up in
the yard together. Consider the last time Alice and Bob each raised
their flag and looked at the other's flag before sending the pet into
the yard. When Alice last looked, her flag was already fully raised.
She must have not seen Bob's flag, or she would not have released
the cat, so Bob must have not completed raising his flag before Alice
started looking. It follows that when Bob looked for the last time,
after raising his flag for the last time, it must have been after Alice
started looking, so he must have seen Alice's flag raised and would
not have released his dog, a contradiction.
This kind of argument by contradiction shows up over and over
again, and it is worth spending some time understanding why this
claim is true. It is important to note that we never assumed that
“raising my flag” or “looking at your flag” happens instantaneously,
nor did we make any assumptions about how long such activities
take. All we care about is when these activities start or end.

1.2.1 Properties of a mutual exclusion protocol


To show that the flag protocol is a correct solution to Alice and Bob's
problem, we must understand what properties a solution requires,
and then show that the protocol meets them.
We already proved that the pets are excluded from being in the
yard at the same time, a property we call mutual exclusion.
Mutual exclusion is only one of several properties of interest. After
all, a protocol in which Alice and Bob never release a pet satisfies the
mutual exclusion property, but it is unlikely to satisfy their pets.
Here is another property of central importance: If only one pet
wants to enter the yard, then it eventually succeeds. In addition, if
both pets want to enter the yard, then eventually at least one of them
succeeds. We consider this deadlock-freedom property to be essential.
Note that whereas mutual exclusion is a safety property, deadlock-
freedom is a liveness property.
We claim that Alice and Bob's protocol is deadlock-free. Suppose
both pets want to use the yard. Alice and Bob both raise their flags.
Bob eventually notices that Alice's flag is raised, and defers to her by
lowering his flag, allowing Alice's cat into the yard.
Another property of interest is starvation-freedom (sometimes called
lockout-freedom): If a pet wants to enter the yard, will it eventually
succeed? Here, Alice and Bob's protocol performs poorly. Whenever
Alice and Bob are in conflict, Bob defers to Alice, so it is possible that
Alice's cat can use the yard over and over again, while Bob's dog
becomes increasingly uncomfortable. Later on, we consider
protocols that prevent starvation.
The last property of interest concerns waiting. Imagine that Alice
raises her flag, and is then suddenly stricken with appendicitis. She
(and the cat) are taken to the hospital, and after a successful
operation, she spends the next week under observation at the
hospital. Although Bob is relieved that Alice is well, his dog cannot
use the yard for an entire week until Alice returns. The problem is
that the protocol states that Bob (and his dog) must wait for Alice to
lower her flag. If Alice is delayed (even for a good reason), then Bob
is also delayed (for no apparently good reason).
The question of waiting is important as an example of fault-
tolerance. Normally, we expect Alice and Bob to respond to each
other in a reasonable amount of time, but what if they do not do so?
The mutual exclusion problem, by its very essence, requires waiting:
No mutual exclusion protocol, no matter how clever, can avoid it.
p
Nevertheless, we will see that many other coordination problems
can be solved without waiting, sometimes in unexpected ways.

1.2.2 The moral


Having reviewed the strengths and weaknesses of Alice and Bob's
protocol, we now turn our attention back to computer science.
First, we examine why shouting across the yard and placing cell
phone calls does not work. Two kinds of communication occur
naturally in concurrent systems:

• Transient communication requires both parties to participate


at the same time. Shouting, gestures, or cell phone calls are
examples of transient communication.
• Persistent communication allows the sender and receiver to
participate at different times. Posting letters, sending email,
or leaving notes under rocks are all examples of persistent
communication.

Mutual exclusion requires persistent communication. The problem


with shouting across the yard or placing cell phone calls is that it
may or may not be okay for Bob to release his dog, but if Alice does
not respond to messages, he will never know.
The can-and-string protocol might seem somewhat contrived, but
it corresponds accurately to a common communication protocol in
concurrent systems: interrupts. In modern operating systems, one
common way for one thread to get the attention of another is to send
it an interrupt. More precisely, thread A interrupts thread B by
setting a bit at a location periodically checked by B. Sooner or later, B
notices the bit has been set and reacts. After reacting, B typically
resets the bit (A cannot reset the bit). Even though interrupts cannot
solve the mutual exclusion problem, they can still be very useful. For
example, interrupt communication is the basis of the Java language's
() and () calls.
On a more positive note, the fable shows that mutual exclusion
between two threads can be solved (however imperfectly) using only
two one-bit variables, each of which can be written by one thread
and read by the other.

1.3 The producer–consumer problem


Mutual exclusion is not the only problem worth investigating.
Eventually, Alice and Bob fall in love and marry. Eventually, they
divorce. (What were they thinking?) The judge gives Alice custody
of the pets, and tells Bob to feed them. The pets now get along with
one another, but they side with Alice, and attack Bob whenever they
see him. As a result, Alice and Bob need to devise a protocol for Bob
to deliver food to the pets without Bob and the pets being in the yard
together. Moreover, the protocol should not waste anyone's time:
Alice does not want to release her pets into the yard unless there is
food there, and Bob does not want to enter the yard unless the pets
have consumed all the food. This problem is known as the producer–
consumer problem.
Surprisingly perhaps, the can-and-string protocol we rejected for
mutual exclusion does exactly what we need for the producer–
consumer problem. Bob places a can standing up on Alice's
windowsill, ties one end of his string around the can, and puts the
other end of the string in his living room. He then puts food in the
yard and knocks the can down. When Alice wants to release the pets,
she does the following:

1. She waits until the can is down.


2. She releases the pets.
3. When the pets return, Alice checks whether they finished the
food. If so, she resets the can.

Bob does the following:

1. He waits until the can is up.


2. He puts food in the yard.
3. He pulls the string and knocks the can down.
The state of the can thus reflects the state of the yard. If the can is
down, it means there is food and the pets can eat, and if the can is
up, it means the food is gone and Bob can put some more out. We
check the following three properties:

• Mutual exclusion: Bob and the pets are never in the yard
together.
• Starvation-freedom: If Bob is always willing to feed, and the
pets are hungry, then the pets will eventually eat.
• Producer–consumer: The pets will not enter the yard unless
there is food, and Bob will never provide more food if there
is unconsumed food.

Both this producer–consumer protocol and the earlier mutual


exclusion protocol ensure that Alice and Bob are never in the yard at
the same time. However, Alice and Bob cannot use this producer–
consumer protocol for mutual exclusion, and it is important to
understand why: The mutual exclusion problem requires deadlock-
freedom: Each person must be able to enter the yard if it is empty
(and the other is not trying to enter). By contrast, the producer–
consumer protocol's starvation-freedom property assumes
continuous cooperation from both parties.
Here is how we reason about this protocol:

• Mutual exclusion: Instead of a proof by contradiction, as we


used earlier, we use an inductive “state machine”-based
proof. Think of the stringed can as a machine that repeatedly
transitions between two states, up and down. To show that
mutual exclusion always holds, we must check that it holds
initially, and continues to hold when transitioning from one
state to the other.
Initially, the yard is empty, so mutual exclusion holds whether
the can is up or down. Next, we check that mutual exclusion,
once established, continues to hold when the state changes.
Suppose the can is down. Bob is not in the yard, and from
Another random document with
no related content on Scribd:
There are six of these strange compositions, upon the stories of
David and Goliath, of David and Saul, of Jacob and Leah, and
others. Some years later they undoubtedly suggested to Sebastian
Bach the delicate little capriccio which he wrote upon the departure
of his brother for the wars. Apart from this they are of slight
importance except as indications of the experimental frame of mind
of their composer. Indeed, beyond imitation and to a small extent
description, neither harpsichord nor pianoforte music has been able
to make much progress in the direction of program music.

Kuhnau’s musical narratives were published in August, 1700. Earlier


than this he had published his famous Sonata aus dem B. The work
so named was appended to Kuhnau’s second series of suites or
Partien. It has little to recommend it to posterity save its name, which
here appears in the history of clavier music for the first time. Nor
does this name designate a form of music akin to the sonatas of the
age of Mozart and Beethoven, a form most particularly associated
with the pianoforte. Kuhnau merely appropriated it from music for
string instruments. There it stood in the main for a work which was
made up of several movements like the suite, but which differed from
the suite in depending less upon rhythm and in having a style more
dignified than that which had grown out of experiments with dance
tunes. In addition, the various movements which constituted a
sonata were not necessarily in the same key. Here alone it
possessed a possible advantage over the suite. Yet though in other
respects it cannot compare favorably to our ears with the suite,
Kuhnau cherished the dignity of style and name with which tradition
had endowed it. These he attempted to bestow upon music for the
clavier.[6]

The various movements lack definite form and balance. The first is in
rather heavy chord style, the chords being supported by a dignified
counterpoint in eighth notes. This leads without pause into a fugue
on a figure of lively sixteenth notes. The key is B-flat major. There
follows a short adagio in E-flat major, modulating to end in C minor,
in which key the last movement, a short allegro in triple time, is taken
up. The whole is rounded off by a return to the opening movement,
signified by the sign Da Capo.

Evidently pleased with this innovation, Kuhnau published in 1696 a


set of seven more sonatas called Frische Clavier Früchte. These
show no advance over the Sonata aus dem B in mastery of musical
structure. Still they are evidence of the efforts of one man among
many to give clavier music a life of its own and to bring it in
seriousness and dignity into line with the best instrumental music of
the day, namely, with the works of such men as Corelli, Purcell, and
Vivaldi. That he was unable to do this the verdict of future years
seems to show. The attempt was none the less genuine and
influential.

In the matter of structure, then, the seventeenth century worked out


and tested but a few principles which were to serve as foundation for
the masterpieces of keyboard music in the years to come. But these,
though few, were of vast importance. Chief among them was the
new principle of harmony. This we now, in the year 1700, find at the
basis of fugue, of prelude and toccata, and of dance form, not
always perfectly grasped but always in evidence. Musical form now
and henceforth is founded upon the relation and contrast of keys.

Consistently to hold to one thematic subject throughout a piece in


polyphonic style, skillfully to contrast or weave with that secondary
subjects, mark another stage of development passed. The fugue is
the result, now articulate, though awaiting its final glory from the
hand of J. S. Bach. To write little dance pieces in neat and precise
form is an art likewise well mastered; and to combine several of
these, written in the same key, in an order which, by affording
contrast of rhythms, can stir the listener’s interest and hold his
attention, is the established rule for the first of the so-called cyclic
forms, prototype of the symphony and sonata of later days. Such
were the great accomplishments of the musicians of the seventeenth
century in the matter of form.
V
In the matter of style, likewise, much was accomplished. We have
had occasion frequently to point out that in the main the harpsichord
remained throughout the first half of the seventeenth century under
the influence of the organ. For this instrument a conjunct or legato
style has proved to be most fitting. Sudden wide stretches,
capricious leaps, and detached runs seldom find a place in the
texture of great organ music. The organist strives for a smoothness
of style compatible with the dignity of the instrument, and this
smoothness may be taken as corollary to the fundamental
relationship between organ music and the vocal polyphony of the
sixteenth century.

On the other hand, by comparison with the vocal style, the organ
style is free. Where the composer of masses was restricted by the
limited ability of the human voice to sing wide intervals accurately,
the organist was limited only by the span of the hand. Where
Palestrina could count only upon the ear of his singers to assure
accurate intonation, the organist wrote for a keyboard which,
supposing the organ to be in tune, was a mechanism that of itself
could not go wrong. Given, as it were, a physical guarantee of
accuracy as a basis for experiment, the organist was free to devise
effects of sheer speed or velocity of which voices would be utterly
incapable. He had a huge gamut of sounds equally at his command,
a power that could be mechanically bridled or let loose. His
instrument could not be fatigued while boys could be hired to pump
the bellows. So long as his finger held down a key, or his foot a
pedal, so long would the answering note resound, diminishing,
increasing, increasing, diminishing, according to his desire, never
exhausted.

Therefore we find in organ music, rapid scales, arpeggios rising from


depths, falling from heights, new figures especially suited to the
organ, such as the ‘rocking’ figure upon which Bach built his well-
known organ fugue in D minor; deep pedal notes, which endure
immutably while above them the artist builds a castle of sounds;
interlinked chords marching up and down the keyboard, strong with
dissonance. There are trills and ornamental turns, rapid thirds and
sixths. And in all these things organ music displays what is its own,
not what it has inherited from choral music.

Yet, notwithstanding the magnificent chord passages so in keeping


with the spirit of the instrument, in which only the beauty of harmonic
sequence is considered, the treatment of musical material by the
organists is prevailingly polyphonic. The sound of a given piece is
the sound of many quasi-independent parts moving along together,
in which definite phrases or motives constantly reappear. The
harmony on which the whole rests is not supplied by an
accompaniment, but by the movement of the several voice-parts
themselves in their appointed courses. And it may be said as a
generality that these parts progress by steps not wider than that
distance the hand can stretch upon the keyboard.

During the first half of the seventeenth century the harpsichord was
but the echo of the organ. Even the collections of early English
virginal music, which in some ways seem to offer a brilliant
exception, are the work of men who as instrumentalists were
primarily organists. In so far as they achieved an instrumental style
at all it was usually a style fitting to a small organ. The few cases
where John Bull’s cleverness displayed itself in almost a true
virtuoso style are exceptions which prove the rule. Not until the time
of Chambonnières and Froberger do we enter upon a second stage.

About the middle of the seventeenth century Chambonnières was


famous over Europe as a performer upon the harpsichord. As first
clavicinist at the court of France, his manner of playing may be taken
to represent the standard of excellence at that time. Constantine
Huygens, a Dutch amateur exceedingly well-known in his day,
mentions him many times in his letters with unqualified admiration,
always as a player of the harpsichord, or as a composer for that
instrument. Whatever skill he may have had as an organist did not
contribute to his fame; and his two sets of pieces for harpsichord,
published after his death in 1670, show the beginnings of a distinct
differentiation between harpsichord and organ style.
Title page of Kuhnan's "Neue Clavier-Übung".
The harpsichord possesses in common with the organ its keyboard
or keyboards, which render the playing of solid chords possible. The
lighter action of the harpsichord gives it the advantage over the
organ in the playing of rapid passages, particularly of those light
ornamental figures used as graces or embellishments, such as trills,
mordents, and turns. A further comparison with the organ, however,
reveals in the harpsichord only negative qualities. It has no volume
of sound, no power to sustain tones, no deep pedal notes.
Consequently the smooth polyphonic style which sounds rich and
flowing on the organ, sounds dry and thin upon the weaker
instrument. The composer who would utilize to advantage what little
sonority there is in the harpsichord must be free to scatter notes here
and there which have no name or place in the logic of polyphony, but
which make his music sound well. Voice parts must be interrupted,
notes taken from nowhere and added to chords. The polyphonic web
becomes disrupted, but the harpsichord profits by the change. It is
Chambonnières who probably first wrote in such a style for the
harpsichord.

He learned little of it from what had been written for the organ, but
much from music for the lute, which, quite as late as the middle of
the century, was interchangeable with the harpsichord in
accompaniments, and was held to be equal if not superior as a solo
instrument. It was vastly more difficult to play, and largely for this
reason fell into disuse. The harpsichord is by nature far nearer akin
to it than to the organ. The free style which lutenists were driven to
invent by the almost insuperable difficulties of their instrument, is
nearly as suitable to the harpsichord as it is to the lute. Without
doubt the little pieces of Denis Gaultier were played upon the
harpsichord by many an amateur who had not been able to master
the lute. The skilled lutenist would find little to give him pause in the
harpsichord music of Chambonnières. The quality of tone of both
instruments is very similar. For neither is the strict polyphony of
organ music appropriate; for the lute it is impossible. Therefore it fell
to the lutenists first to invent the peculiar instrumental style in which
lie the germs of the pianoforte style; and to point to their cousins,
players of the harpsichord, the way towards independence from
organ music.

Froberger came under the influence of Denis Gaultier and


Chambonnières during the years he spent in Paris, and he adopted
their style and made it his own. He wrote, it is true, several sets of
ricercars, capriccios, canzonas, etc., for organ or harpsichord, and in
these the strict polyphonic style prevails, according to the
conventionally more serious nature of the compositions. But his fame
rests upon the twenty-eight suites and fragments of suites which he
wrote expressly for the harpsichord. These are closely akin to lute
music, and from the point of view of style are quite as effective as
the music of Chambonnières. In harmony they are surprisingly rich.
Be it noted, too, in passing, that they are not lacking in emotional
warmth. Here is perhaps the first harpsichord music which demands
beyond the player’s nimble fingers his quick sympathy and
imagination—qualities which charmed in Froberger’s own playing.

Kuhnau as a stylist is far less interesting than Froberger, upon


whose style, however, his clavier suites are founded. His importance
rests in the attempts he made to adapt the sonata to the clavier, in
his experiments with descriptive music, and in the influence he had
upon his contemporaries and predecessors, notably Bach and
Handel. Froberger is the real founder of pianoforte music in
Germany, and beyond him there is but slight advance either in style
or matter until the time of Sebastian Bach.

What we may now call the harpsichord style, as exemplified in the


suites of Chambonnières and Froberger, is relatively free. Both
composers had a fondness for writing in four parts, but these parts
are not related to each other, nor woven together unbrokenly as in
the polyphonic style of the organ. They cannot often be clearly
followed throughout a given piece. The upper voice carries the music
along, the others accompany. The arrangement is not wholly an
inheritance from the lute, but is in keeping with the general tendency
in all music, even at times in organ music, toward the monodic style,
of which the growing opera daily set the model.

But the harpsichord style of this time is by no means a simple


system of melody and accompaniment. Though the three voice parts
which support the fourth dwell together often in chords, they are not
without considerable independent movement. They constitute the
harmonic background, as it were, which, though serving as
background, does not lack animation and character in itself. In other
words, we have a contrapuntal, not a polyphonic, style.

A marked feature of the music is the profuse number of graces and


embellishments. These rapid little figures may be akin to the vocal
embellishments which even at the beginning of the seventeenth
century were discussed in theoretical books; but they seem to flower
from the very nature of the harpsichord, the light tone and action of
which made them at once desirable and possible. They are but
vaguely indicated in the manuscripts, and there can be no certainty
as to what was the composer’s intention or his manner of
performance. Doubtless they were left to the discretion of the player.
At any rate for a century more the player took upon himself the
liberty of ornamenting any composer’s music to suit his own whim.
These agrémens[7] were held to be and doubtless were of great
importance. Kuhnau, in the preface to his Frische Clavier Früchte,
speaks of them as the sugar to sweeten the fruit, even though he left
them much to the taste of players; and Emanuel Bach in the second
half of the eighteenth century devoted a large part of his famous
book on playing the clavier to an analysis and minute explanation of
the host of them that had by then become stereotyped. They have
not, however, come down into pianoforte music. It is questionable if
they can be reproduced on the pianoforte, the heavy tone of which
obscures the delicacy which was their charm. They must ever
present difficulty to the pianist who attempts to make harpsichord
music sound again on the instrument which has inherited it.

The freedom from polyphonic restraint, inherited from the lute, and
the profusion of graces which have sprouted from the nature of the
harpsichord, mark the diversion between music for the harpsichord
and music for the organ. In other respects they are still much the
same; that is to say, the texture of harpsichord music is still close—
restricted by the span of the hand. This is not necessarily a sign of
dependence on the organ, but points rather to the young condition of
the art. It is not to be expected that the full possibilities of an
instrument will be revealed to the first composers who write for it
expressly. They lie hidden along the way which time has to travel.
But Chambonnières, in France, and Froberger, in Germany, opened
up the special road for harpsichord music, took the first step which
others had but to follow.

Neither in France nor in Germany did the next generation penetrate


beyond. Le Gallois, a contemporary of Chambonnières, has
remarked that of the great player’s pupils only one, Hardelle, was
able to approach his master’s skill. Among those who carried on his
style, however, must be mentioned d’Anglebert,[8] Le Begue,[9] and
Louis and François Couperin, relatives of the great Couperin to
come.

In Germany Georg and Gottlieb Muffat stand nearly alone with


Kuhnau in the progress of harpsichord music between Froberger and
Sebastian Bach. Georg Muffat spent six years in Paris and came
under French influence as Froberger had come, but his chief
keyboard works (Apparatus Musico Organisticus (1690)) are twelve
toccatas more suited to organ than to harpsichord. In 1727 his son
Gottlieb had printed in Vienna Componimenti musicali per il
cembalo, which show distinctly the French influence. Kuhnau looms
up large chiefly on account of his sonatas, which are in form and
extent the biggest works yet attempted for clavier. By these he
pointed toward a great expansion of the art; but as a matter of fact
little came of it. In France, Italy, and Germany the small forms were
destined to remain the most popular in harpsichord music; and the
sonatas and concertos of Bach are immediately influenced by study
of the Italian masters, Corelli and Vivaldi.
In Italy, the birthplace of organ music and so of a part of harpsichord
music, interest in keyboard music of any kind declined after the
death of Frescobaldi in 1644, and was replaced by interest in opera
and in music for the violin. Only one name stands out in the second
half of the century, Bernardo Pasquini, of whose work, unhappily,
little remains. He was famous over the world as an organist, and the
epitaph on his tombstone gives him the proud title of organist to the
Senate and People of Rome. Also he was a skillful performer on the
harpsichord; but he is more nearly allied to the old polyphonic school
than to the new. A number of works for one and for two harpsichords
are preserved in manuscript in the British Museum, and these are
named sonatas. Some are actually suites, but those for two
harpsichords have little trace of dance music or form and may be
considered as much sonatas as those works which Kuhnau
published under the same title. All of Kuhnau’s sonatas appeared
before 1700 and the date on the manuscript in the British Museum is
1704. Pasquini was then an old man, and it is very probable that
these sonatas were written some years earlier; in which case he and
not Kuhnau may claim the distinction of first having written music for
the harpsichord on the larger plan of the violin concerto and the
sonatas of Corelli.[10]

Two books of toccatas by Alessandro Scarlatti give that facile


composer the right to be numbered among the great pioneers in the
history of harpsichord music. These toccatas are in distinct
movements, usually in the same key, but sharply contrasted in
content. The seventh is a theme and variations, in which Scarlatti
shows an appreciation of tonal effects and an inventiveness which
are astonishingly in advance of the time. He foreshadows
unmistakably the brilliant style of his son Domenico; indeed, he
accounts in part for what has seemed the marvellous instinct of
Domenico. If, as is most natural, Domenico approached the
mysteries of the harpsichord through his father, he began his career
with advantages denied to all others contemporary with him, save
those who, like Grieco, received that father’s training. Alessandro
Scarlatti was one of the most greatly endowed of all musicians. The
trend of the Italian opera during the eighteenth century toward utter
senselessness has been often laid partly to his influence; but in the
history of harpsichord music that influence makes a brilliant showing
in the work of his son, who contributed perhaps more than any other
one man to the technique of writing not only for harpsichord but for
pianoforte.

Little of the harpsichord and clavichord music of the seventeenth


century is heard today. It has in the main only an historical interest.
The student who looks into it will be amazed at some of its beauties;
but as a whole it lacks the variety and emotional strength which
claim a general attention. Nevertheless it is owing to the labor and
talent of the composers of these years that the splendid
masterpieces of a succeeding era were possible. They helped
establish the harmonic foundation of music; they molded the fugue,
the prelude, the toccata, and the suite; they developed a general
keyboard style. After the middle of the century such men as
Froberger and Kuhnau in Germany, Chambonnières, d’Anglebert,
and Louis and François Couperin in France, and Alessandro
Scarlatti in Italy, finally gave to harpsichord music a special style of
its own, and to the instrument an independent and brilliant place
among the solo instruments of that day. Out of all the confusion and
uncertainty attendant upon the breaking up of the old art of vocal
polyphony, the enthusiasm of the new opera, the creation of a new
harmonic system, the rise of an instrumental music independent of
words, these men slowly and steadily secured for the harpsichord a
kingdom peculiarly its own.
FOOTNOTES:
[1] It should be noted in passing that during the early stages of the growth of
polyphonic music, roughly from the eleventh to the fifteenth century, composers
had brought over into their vocal music a great deal of instrumental technique or
style, which had been developed on the crude organs, and on the accompanying
instruments of the troubadours. In the period which we are about to treat the
reverse is very plainly the case.

[2] At the head of Sebastian Bach’s Musikalisches Opfer stands the Latin
superscription: Regis Iussu Cantio et Reliqua Canonica Arte Resoluta. The initial
letters form the word ricercar.

[3] Cf. Vol. VI, Chap. XV.

[4] Suites were known in England as ‘lessons,’ in France as ordres, in Germany as


Partien, and in Italy as sonate da camera.

[5] There was a form of suite akin to the variation form. In this the same melody or
theme served for the various dance movements, being treated in the style of the
allemande, courante, or other dances chosen. Cf. Peurl’s Pavan, Intrada, Dantz,
and Gaillarde (1611); and Schein’s Pavan, Gailliarde, Courante, Allemande, and
Tripla (1617). This variation suite is rare in harpsichord music. Froberger’s suite on
the old air, Die Mayerin, is a conspicuous exception.

[6] 'Denn warum sollte man auf dem Clavier nicht eben wie auf anderen
Instrumenten dergleichen Sachen tractieren können?’ he writes in his preface to
the ‘Seven New Partien,’ 1692.

[7] So they were called in France, which until the time of Beethoven set the model
for harpsichord style. In Germany they were called Manieren.

[8] D’Anglebert published in 1689 a set of pieces, for the harpsichord, containing
twenty variations on a melody known as Folies d’Espagne, later immortalized by
Corelli.

[9] Le Begue (1630-1702) published Pièces de clavecin in 1677.

[10] See J. S. Shedlock: ‘The Pianoforte Sonata,’ London, 1895.


CHAPTER II
THE GOLDEN AGE OF HARPSICHORD
MUSIC
The period and the masters of the ‘Golden Age’—Domenico
Scarlatti; his virtuosity; Scarlatti’s ‘sonatas’; Scarlatti’s technical
effects; his style and form; æsthetic value of his music; his
contemporaries—François Couperin, le Grand; Couperin’s
clavecin compositions; the ‘musical portraits’; ‘program music’—
The quality and style of his music; his contemporaries, Daquin
and Rameau—John Sebastian Bach; Bach as virtuoso; as
teacher; his technical reform; his style—Bach’s fugues and their
structure—The suites of Bach: the French suites, the English
suites, the Partitas—The preludes, toccatas and fantasies;
concertos; the ‘Goldberg Variations’—Bach’s importance; his
contemporary Handel.

In round figures the years between 1700 and 1750 are the Golden
Age of harpsichord music. In that half century not only did the
technique, both of writing for and performing on the harpsichord,
expand to its uttermost possibilities, but there was written for it music
of such beauty and such emotional warmth as to challenge the best
efforts of the modern pianist and to call forth the finest and deepest
qualities of the modern pianoforte.

It was an age primarily of opera, of the Italian opera with its


senseless, threadbare plots, its artificial singers idolized in every
court, its incredible, extravagant splendor. The number of operas
written is astonishing, the wild enthusiasm of their reception hardly
paralleled elsewhere in the history of music. Yet of these many works
but an air or two has lived in the public ear down to the present day;
whereas the harpsichord music still is heard, though the instrument
for which it was written has long since vanished from our general
musical life.

Practically the whole seventeenth century has been required to lay


down a firm foundation for the development of instrumental music in
all its branches. This being well done, the music of the next epoch is
not unaccountably surprising. As soon as principles of form had
become established, composers trod, so to speak, upon solid
ground; and, sure of their foothold, were free to make rapid progress
in all directions. In harpsichord music few new forms appeared. The
toccata, prelude, fugue, and suite offered room enough for all the
expansion which even great genius might need. Within these limits
the growth was twofold: in the way of virtuosity and refinement of
style, and in the way of emotional expression. That music which
expands at once in both directions, or in which, rather, the two
growths are one and the same, is truly great music. Such we shall
now find written for the harpsichord.

Each of the three men whose work is the chief subject of this chapter
is conspicuous in the history of music by a particular feature.
Domenico Scarlatti is first and foremost a great virtuoso, Couperin
an artist unequalled in a very special refinement of style, Sebastian
Bach the instrument of profound emotion. In these features they
stand sharply differentiated one from the other. These are the
essential marks of their genius. None, of course, can be
comprehended in such a simple characterization. Many of Scarlatti’s
short pieces have the warmth of genuine emotion, and Couperin’s
little works are almost invariably the repository of tender and naïve
sentiment. Bach is perhaps the supreme master in music and should
not be characterized at all except to remind that his vast skill is but
the tool of his deeply-feeling poetic soul.
I
It will be noticed that each of these great men speaks of a different
race. We may consider Scarlatti first as spokesman in harpsichord
music of the Italians, who at that time had made their mark so deep
upon music that even now it has not been effaced, nor is likely to be.
His father, Alessandro, was the most famous and the most gifted
musician in Europe. From Naples he set the standard for the opera
of the world, and in Naples his son Domenico was born on October
26, 1685, a few months only after the birth of Sebastian Bach in
Eisenach. Domenico lived with his father and under his father’s
guidance until 1705, when he set forth to try his fame. He lived a few
years in Venice and there met Handel in 1708, with whom he came
back to Rome. Here in Rome, at the residence of Corelli’s patron,
Cardinal Ottoboni, took place the famous contest on organ and
harpsichord between him and Handel. For Handel he ever professed
a warm friendship and the most profound admiration.

He remained for some years in Rome, at first in the service of Marie


Casimire, queen of Poland, later as maestro di capella at St. Peter’s.
In 1719 came a journey to London in order to superintend
performances of his operas. From 1721 to 1725 he seems to have
been installed at the court of Lisbon; and then, after four years in
Naples, he accepted a position at the Spanish court in Madrid. Just
how long he stayed there is not known. In 1754 he was back again in
Naples, and in Naples he died in 1757, seven years after the death
of Bach.

Scarlatti wrote many operas in the style of his father, and these were
frequently performed, with success, in Italy, England, Spain, and
elsewhere. During his years at St. Peter’s he also wrote sacred
music; but his fame now rests wholly upon his compositions for the
harpsichord and upon the memory of the extraordinary skill with
which he played them.

We have dwelt thus briefly upon a few events of his life to show how
widely he had travelled and in how many places his skill as a player
must have been admired. That in the matter of virtuosity he was
unexcelled can hardly be doubted. It is true that in the famous
contest with Handel he came off the loser on the organ, and even his
harpsichord playing was doubted to excel that of his Saxon friend.
But these contests were a test of wits more than of fingers, a trial of
extempore skill in improvising fugues and double fugues, not of
virtuosity in playing.

Two famous German musicians, J. J. Quantz and J. A. Hasse, both


heard him and both marvelled at his skill. Monsieur L’Augier, a gifted
amateur whom Dr. Burney visited in Vienna, told a story of Scarlatti
and Thomas Roseingrave,[11] in which he related that when
Roseingrave first heard Scarlatti play, he was so astonished that he
would have cut off his own fingers then and there, had there been an
instrument at hand wherewith to perform the operation; and, as it
was, he went months without touching the harpsichord again.

Whom he had to thank for instruction is not known. There is nothing


in his music to suggest that he was ever a pupil of Bernardo
Pasquini, who, however, was long held to have been his master. J.
S. Shedlock, in his ‘History of the Pianoforte Sonata,’ suggests that
he learned from Gaëtano Greco or Grieco, a man a few years his
senior and a student under his father; but it would seem far more
likely that Domenico profited immediately from his father, who, we
may see from a letter to Ferdinand de’ Medici, dated May 30, 1705,
had watched over his son’s development with great care. It must not
be forgotten that Alessandro Scarlatti’s harpsichord toccatas,
described in the previous chapter, are, in spite of a general
heaviness, often enlivened by astonishing devices of virtuosity.

Scarlatti wrote between three and four hundred pieces for the
harpsichord. The Abbé Santini[12] possessed three hundred and
forty-nine. Scarlatti himself published in his lifetime only one set of
thirty pieces. These he called exercises (esercizii) for the
harpsichord. The title is significant. Before 1733 two volumes, Pièces
pour le clavecin, were published in Paris; and some time between
1730 and 1737 forty-two ‘Suites of Lessons’ were published in
London under the supervision of Roseingrave. More were printed in
London in 1752. Then came Czerny’s edition, which includes two
hundred pieces; and throughout the nineteenth century various
selections and arrangements have appeared from time to time, von
Bülow having arranged several pieces in the order of suites, Tausig
having elaborated several in accordance with the modern pianoforte.
A complete and authoritative edition has at last been prepared by
Sig. Alessandro Longo and has been printed in Italy by Ricordi and
Company.

By far the greater part of these many pieces are independent of each
other. Except in a few cases where Scarlatti, probably in his youth,
followed the model of his father’s toccatas, he keeps quite clear of
the suite cycle. The pieces have been called sonatas, but they are
not for the most part in the form called the sonata form. This form
(which is the form in which one piece or movement may be cast and
is not to be confused with the sequence or arrangement of
movements in the classical sonata) is, as we shall later have ample
opportunity to observe, a tri-partite or ternary form; whereas the so-
called sonatas of Scarlatti are in the two-part or binary form, which
is, as we have seen, the form of the separate dance movements in
the suite. Each ‘sonata’ is, like the dance movements, divided into
two sections, usually of about equal length, both of which are to be
repeated in their turn. In general, too, the harmonic plan is the same
or nearly the same as that which underlies the suite movement, the
first section modulating from tonic to dominant, the second back from
dominant to tonic. But within these limits Scarlatti allows himself
great freedom of modulation. It is, in fact, this harmonic expansion
within the binary form which makes one pause to give Scarlatti an
important place in the development of the sonata form proper.

The harmonic variety of the Scarlatti sonatas is closely related to the


virtuosity of their composer. He spins a piece out of, usually, but not
always, two or three striking figures, by repeating them over and
over again in different places of the scale or in different keys. His
very evident fondness for technical formulæ is thus gratified and the
piece is saved from monotony by its shifting harmonies.
A favorite and simple shift is from major to minor. This he employs
very frequently. For example, in a sonata in G major, No. 2 of the
Breitkopf and Härtel collection of twenty sonatas[13] measures 13,
14, 15, and 16, in D major, are repeated immediately in A major. In
20, 21, 22, and 23, the same style of figure and rhythm appears in D
major and is at once answered in D minor. Toward the end of the
second part of the piece the process is duplicated in the tonic key. In
the following sonata at the top of page seven occurs another similar
instance. It is one of the most frequent of his mannerisms.

The repetition of favorite figures is by no means always


accompanied by a change of key. The two-measure phrase
beginning in the fifteenth measure of the third sonata is repeated
three times note for note; a few measures later another figure is
treated in the same fashion; and in yet a third place, all in the first
section of this sonata, the trick is turned again. Indeed, there are
very few of Scarlatti’s sonatas in which he does not play with his
figures in this manner.

We have said that often he varies his key when thus repeating
himself, and that such variety saves from monotony. But it must be
added that even where there is no change of key he escapes being
tedious to the listener. The reason must be sought in the sprightly
nature of the figures he chooses, and in the extremely rapid speed at
which they are intended to fly before our ears. He is oftenest a
dazzling virtuoso whose music appeals to our bump of wonder, and,
when well played, leaves us breathless and excited.

The pieces are for the most part extremely difficult; and this, together
with his ever-present reiteration of special harpsichord figures, may
well incline us to look upon them as fledgling études. The thirty
which Scarlatti himself chose to publish he called esercizii, or
exercises. We may not take the title too literally, bearing in mind that
Bach’s ‘Well-Tempered Clavichord’ was intended for practice, as
were many of Kuhnau’s suites. But that Scarlatti’s sonatas are
almost invariably built up upon a few striking, difficult and oft-
repeated figures, makes their possible use as technical practice

You might also like