|
I just returned from two small conferences, CGO (Code Generation and Optimization, www.cgo.org) and PPoPP (Principles and Practice of Parallel Programming, www.ppopp.org).
One of the key themes in both conferences, perhaps the dominating
theme, was that multicore chips are here, are mainstream, and we'd
better figure out how to use them.
I just returned from two small conferences, CGO (Code Generation and Optimization, www.cgo.org) and PPoPP (Principles and Practice of Parallel Programming, www.ppopp.org).
One of the key themes in both conferences, perhaps the dominating
theme, was that multicore chips are here, are mainstream, and we'd
better figure out how to use them.
One of the PPoPP attendees,
Prof. Rudolf Eigenmann (Purdue Univ.) issued an indictment, saying that
we in the parallel programming research community should be ashamed of
ourselves. Single-processor systems have run out of steam, something
the parallel programming community has been predicting since I was a
college student. Now is the time to step up and reap the benefits of
all our past work. We've had 30 years to study this problem and come up
with a solution, but what's the end result? Surprise! We still have no
well-accepted method to generate parallel applications.
We had a
similar discussion at one of the workshops before CGO, where someone
said that the programming problem would finally get solved because "now
we're motivated!" This implies either that the past thirty years of
investigation was being done by the wrong people, or that perhaps now
with more people looking at the problem, someone will randomly stumble
on a good solution. Science at its best?
Dr. Andrew Chien
(Intel), one of the PPoPP keynote speakers, took issue with Eigenmann's
criticism. Chien said that in fact we've had a great deal of success in
parallel programming: just look at all the massively parallel systems
and the applications that run on them. However, halfway through his
talk was the slide "Wanted: Breakthrough Innovations in Parallel
Programming." I asked how he could claim past success, then state that
breakthrough innovations are needed; it sounded like a typical manager:
"good job, now get back to work." He replied that in the past, parallel
programming meant high performance. Now, parallel programming means
spreadsheets, games, email, and applications on your laptop. It's a
different target environment, with a different class of programmer, and
different expectations.
So parallel programming is hard. Hey,
sequential programming is hard too. Adding parallelism just makes it
harder. Two of the major problems are expressing the parallelism and
synchronizing or communicating between the parallel threads or
activities.
An approach to the synchronization problem that is
gaining lots of traction is transactional memory, or TM. This takes a
page from the successful database community, which has long had to deal
with many processes simultaneously accessing and modifying a shared
database. Rather than letting a process lock the database, the process
executes a transaction; the transaction may involve adding, removing,
or modifying relations in the database. While in the middle of a
transaction, no other process can see the modifications; at the end,
the process commits the results. The database then atomically exposes
all the updates to the other processes. In particular, this lets
multiple processes modify disjoint parts of the database in parallel
without conflict. If two processes try to update the same data at the
same time, the first commit will succeed, and the second will fail. The
application then has to restart its transaction from the start, since
it may have made some decisions based on values that are now stale.
Moving
transactions to the parallel programming world means rather than
updating shared data in a critical section, the model is to enter a
transaction, perform the updates, then commit the changes. The
implementation must buffer the modifications until the commit, then
atomically commit all the modifications at once. As in a database, if
some other parallel thread had made changes to the same shared data,
the commit will fail, and the transaction must restart and retry. The
expected behavior is that most transactions will succeed, so the retry
overhead is quite low.
However, it's still open as to how to
implement the transaction buffering and commit. Modified caches could
be used to buffer stores, unless you run out of associativity on the
cache line. A separate transactional store queue could be designed, but
the size of the queue would limit the size of the transaction.
There
are other problems as well. Imagine two transactions, A and B, reading
and modifying the same shared data. Suppose transaction B finishes and
commits while A is still working. If transaction A then reads some data
that was modified by B, A's commit is likely to fail (in fact, may be
guaranteed to fail), since some of the data it read was before B's
commit and some was read after. In fact, the inconsistency may cause A
to generate a fault (suppose B allocated or freed a pointer), or loop
infinitely; one speaker termed A a "zombie transaction," the walking
dead. It can be important to detect and kill zombies before they get to
the commit state, to avoid spurious faults.
In managed software
environments (think Java or C#), these problems can be handled in
software, and transactions are likely to be successful there. However,
it remains unclear how long it will be before transactions can migrate
into HPC.
The architectural trends for multicore processing are
still in flux as well. One idea is to build an array of small,
low-power cores on the chip. Each core is slower on a single thread,
but the array of cores could provide a great deal of job throughput.
Andrew Chien pointed out that the single stream performance of a core
grows roughly relative to the square root of the area, whereas the
aggregate performance of a multicore chip grows linearly with the
number of cores -- a strong argument.
The Sun UltraSPARC T1
(Niagara) processor uses this strategy, with eight cores, each core
swapping between four threads (much like the PPUs of the Control Data
6600, back in the late 1960s), giving very high throughput with
significant power savings. This sacrifices single-thread performance,
and Amdahl's Law says this will affect parallel applications
performance as well.
To solve that problem, some proposed designs
use one or two aggressive, out-of-order, deeply pipelined, superscalar
(large, power-hungry) cores surrounded by a sea of more tame, in-order,
(smaller, power-frugal) cores. A parallel application would run mostly
on the sea of slower cores, with the larger cores powered down. When
entering a large sequential region on the critical path, the program
would shift to the more powerful core.
This idea is not new, I
first heard something similar 15 years ago or more, from Samuel Ho, a
young PhD candidate at the University of Washington. At that time, he
was proposing an Intel 486 surrounded by a bunch of 386s (which gives
you the time frame), mostly for reduced cost. Today's reduced power
arguments are more compelling.
In some arenas, multicore has a
well-established history. High end graphics processing units (GPUs) in
personal computers and workstations have for years exposed a great deal
of parallelism, and now look more like a collection of programmable
processors with additional functional units, data types, and
instructions geared for graphics problems. There are new efforts to
expose GPU programming to the more general-purpose high performance
market -- the so-called GPGPU programming.
However, there are
some important caveats. For instance, today's GPUs implement floating
point arithmetic, but only 32-bit precision, and not all the IEEE
rounding modes. Some of the operations are not precise to the last ULP
(Unit in the Last Place). That much precision isn't needed in the
graphics world, and this simplifies the GPU, making it smaller and
faster.
The hardest nut to swallow is the graphics memory, which
right now doesn't implement ECC (error correcting code). With close to
a gigabyte of memory, transient single-bit errors are quite possible,
but again, in the graphics world, that will likely correspond to a
slightly off-shaded pixel somewhere on the screen for one frame, so who
cares? In your numerical simulation, that one bit could be a little
more important, so perhaps this isn't quite ready for life-critical
applications.
My summary of all the hype for GPGPUs is that
processors or coprocessors unconstrained by compatibility requirements,
with the freedom to redesign to the latest technology, can deliver
higher performance than general purpose CPUs. This is something we've
known since the days of Floating Point Systems, with its attached FP
processors. There are other coprocessors specifically designed for the
high performance market, such as the Clearspeed board. GPUs are
convenient for the budget-minded, because the development cost is paid
for by the consumer games market.
So that's what we have, and
it's up to you to figure out how to use it. High performance computing,
as usual, is left with the crumbs off the table of the mass market.
Right
now, we mostly think of multicore chips with 2 or 4 cores, even as Sun
prepares the UltraSPARC T2 with 8 cores for release this fall. The
sweet spot of high performance parallel computing has been in the range
of 4 to 32 processors. Many applications do scale up beyond that, but
hardware becomes much more complex to deliver scalable communication
bandwidth, and software must be restructured to take advantage of all
the parallelism without spending all its time waiting on remote data.
David
Callahan (Microsoft Research) pointed out that exponential grows really
fast. If we plan on doubling the number of cores on a chip every year
or 18 months, it won't be long before we have hundreds or thousands of
cores on a chip. Any software solution aimed at 16 or 32 cores will
quickly become irrelevant. We'd better be looking at productive ways to
use massively parallel systems, since these may well find their way
into our workstations, and yes, even our laptops, before the end of the
next decade.
At one CGO panel, the moderator asked the audience
whether new languages were needed for multicore. The response was
somewhat tepid. It's not clear that multicore is the reason for
developing new languages. There are several research projects looking
at designing or modifying languages specifically for developing
scalable parallel applications.
Several of these project involve
the so-called PGAS (Partitioned Global Address Space) languages. The
main idea is the data is explicitly and visibly partitioned across the
parallel machine, but any thread running on any node can fetch or store
remote data directly. This promotes MPI-style programming, with most of
the computation on local data, but exposes the communication as a
language primitive instead of using opaque library calls. They try to
get the advantages of HPF (High Performance Fortran), such as global
addressability, without relying on unproven compiler technology.
DARPA,
as part of its High Productivity Computing Systems (HPCS) project, is
sponsoring the development of three new languages; see the HPCwire
Q&A with Rusty Lusk (http://www.hpcwire.com/hpc/827250.html)
for a summary. Recently I took a tutorial of IBM's HPCS language, X10.
It includes some of the PGAS ideas for parallelism, and allows for
several ways to express parallelism at multiple levels beyond just data
parallelism. It will be quite interesting to watch the evolution of
these languages, but if this tutorial is indicative of their maturity,
we have years of development before they are ready for real users.
So
what's the answer to the question in this article's title? What DO we
do with all those cores? The gold standard which we would like to
achieve is to make them invisible. Upgrading to a multicore processor
should be as simple and as effective as upgrading to a faster processor
was in years past.
Previously, improvements to microarchitectural
elements, such as branch prediction, out-of-order execution control
units and pipelined functional units, were, for the most part,
invisible from software. Instruction-set architectural changes, such as
the SSE and SSE2 registers and instructions, were visible to the
compiler, but largely hidden from the programmer.
Some
architectural enhancements are significant enough that changing the
program to take advantage of them is worth the effort. The packed SSE
instructions on x86 processors and the vector instructions of classical
supercomputers fall into this category. If we can make multicore
processors no harder to use than this, we will have succeeded. The
biggest barrier is that the architecture and the OS today present the
multicore processor as indistinguishable from a multiprocessor. We need
hardware and software mechanisms to allow compilers and developers to
take advantage of these multicore chips as powerful processors. That is
the next short-term challenge and will be the subject of a future
column.
Read the original article: http://www.hpcwire.com/hpc/1347210.html
|