713 Тем для обсуждения
6,530 Открытых обсуждений
- Association for Computing Machinery TechNews (ACM)
- Go Parallel! (Dr. Dobbs)
- HPCwire (Tabor Communications, Inc.)
- insideHPC (John West)
- Joe Duffy's Weblog (Microsoft)
- Microsoft Parallel Programming Development Center (Microsoft Germany)
- MultiCoreInfo.com
- scalability.org (Scalable Informatics)
- Software Dev Blog (Intel Germany)
- Soft Talk Blog (Intel United Kingdom)
- The Moth (Microsoft)
Китайское искусство программирования
yuryserdyuk (10 пост(а)) 29.07.2009 15:23
Те, кто интересовался Intel Threading Challenge 2007-2008 и Intel Threading Challenge 2009 , наверно, заметили активное участие в них и, самое главное, отличные результаты программистов из Китая. Регулярно борются они за первые места и на чемпионатах мира по программированию (ACM ICPC). Причем, теперь уже по моим личным наблюдениям, с ними совершенно невозможно соперничать в классических задачах программирования, таких как, например, сортировки, поиск и др. Однако, кроме великолепного знания методов решения стандартных задач, не откажешь им и в креативности. Вот показательный пример на эту тему …

Наверняка, всем хорошо известна задача о расстановке ферзей на шахматной доске таким образом, чтобы они взаимно не били друг друга. Суть задачи, а точнее, ее обобщения состоит в том, чтобы подсчитать количество всех возможных таких расстановок на доске размером N x N (NQueens Problem). Удивительно то, что хотя самой задаче уже около 150 лет, но существенный прорыв в ее решении произошел совсем недавно, в 2002 г. (а еще один прорыв состоялся в июле 2009г., но об этом позже).
(Прежде чем читать дальше, тем, кто никогда не пробовал самостоятельно запрограммировать решение этой задачи, настоятельно рекомендуется попытаться сделать собственную реализацию.)
И автором этого прорыва, как вы уже, наверно, догадались, стал человек из Китая по имени Qiu Zongyan из Пекинского университета (его статья помещена в журнале ACM SIGPLAN Notices).
Поскольку алгоритм решения задачи представляет собой, в сущности, полный перебор, то, чтобы сделать его максимально быстрым, нужно эффективным образом организовать структуры данных, отображающие текущую конфигурацию ферзей на доске с тем, чтобы потратить минимальное число операций на переход к следующей конфигурации.
Суть гениального по своей простоте приема, предложенного Qiu Zongyan, проиллюстрируем на примере.
Предположим, что в алгоритме ферзи выставляются на доску по порядку, начиная с самой верхней горизонтали. Например, после расстановки двух первых ферзей мы можем иметь следующую конфигурацию на двух первых горизонталях доски 8 x 8:
| 0 | 0 | Ф | 0 | 0 | 0 | 0 | 0 | |
| 0 | 0 | 0 | 0 | 0 | Ф | 0 | 0 |
Теперь, чтобы вычислить допустимые позиции на третьей горизонтали, нам нужно определить какие из позиций на этой горизонтали находятся под боем ранее выставленных ферзей; а именно, какие позиции принадлежат
1) диагоналям, по которым выставленные ферзи бьют справа-налево,
2) вертикалям, по которым ферзи бьют сверху-вниз,
3) диагоналям, по которым ферзи бьют слева-направо.
Представим эти позиции, находящиеся под боем (отметив их единичками) в виде соответствующих трех битовых векторов:
| left: | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| down: | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
| right: | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
Поэлементно складываем все эти вектора логически по ИЛИ и получаем занятые/свободные позиции на третьей горизонтали:
| 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
Итак, у нас имеется 3 возможных способа поставить очередного ферзя. Выбираем, например, вторую позицию слева и ставим туда третьего ферзя.
А теперь – внимание ! – ключевой момент алгоритма. Как нам получить новую конфигурацию доски, а именно, занятые позиции теперь уже на четвертой горизонтали? А делаем всего лишь вот что − ставим единицу во вторую позицию (т.е., позицию, куда мы поставили нового ферзя) каждого из векторов left, down и right, и
a) сдвигаем вектор left влево на 1 бит (вычисляем позиции под боем на диагоналях, идущих от уже поставленных ферзей справа-налево),
б) ничего не делаем с down,
в) сдвигаем вектор right вправо на 1 бит (аналогично пункту а, но для диагоналей слева-направо).
Суммируем чего мы добились − мы представили конфигурации ферзей на доске в виде битовых векторов, а их модификацию выразили исключительно через битовые операции.
Остальная часть алгоритма − это обычный бектрекинг с целью получения всех возможных расстановок. Полное решение этой задачи на языке C# представлено ниже.
using System;
public class NQueens {
public static int Backtrack(int y, int left, int down,
int right, int N, int MASK)
{
int bitmap, bit;
int count = 0;
if (y == N) {
return ( 1 );
} else {
bitmap = MASK & ~(left | down | right);
while (bitmap != 0) {
bit = -bitmap & bitmap;
bitmap ^= bit;
count += Backtrack(y+1,(left | bit)<<1, down | bit,
(right | bit)>>1, N, MASK);
}
return ( count );
}
}
public static void Main(String[] args )
{
int N = Convert.ToInt32 ( args [ 0 ] );
int MASK = (1 << N) - 1;
int result = Backtrack(0, 0, 0, 0, N, MASK);
Console.WriteLine ("N=" + N + " -> " + result);
}
}
Схема параллелизации этого алгоритма не так интересна − она очевидна, и, похоже, единственна. О ней и о сопутствующих результатах, я надеюсь, мы поговорим в планирующемся продолжении этого поста. А пока лишь упомяну, что детальное описание приведенного выше алгоритма вместе со схемой параллелизации на языке MC# можно будет вскоре найти в учебном курсе “Параллельное программирование для многоядерных процессоров”, который будет размещен на сайте Интернет-университета информационных технологий и на сайте Microsoft MSDN Academic Alliance . Варианты решения этой же задачи с использованием OpenMP и Intel TBB можно найти в документе “Parallelizing N-Queens with the Intel Parallel Composer” , а с использованием Cilk++ - здесь.
В заключении, по традиции две загадки − как вы думаете, что это такое?
1)
int t(int a, int b, int c){
int d=0, e=a&~b&~c, f=1;
if (a)
for (f=0; d=(e-=d)&-e;)
f+=t(a-d,(b+d)*2,(c+d)/2);
return f;
}
int main( int argc, char *argv[]) {
int q = atoi ( argv [ 1 ] );
printf("%d\n",t(~(~0<<q),0,0));
}
2)

Ответы − в следующем посте.
Категории: Intel Software Network, Параллельное программирование, Разработка софта
Пожалуйста, обратитесь к странице Уведомление об оптимизации для более подробной информации относительно производительности и оптимизации в программных продуктах компании Intel.
Комментарии (13)
| 29.07.2009 08:37
yuryserdyuk
|
>Пошел за чипсами и попкорном, надо подготовиться к холивару постов на 50 ;) Не, я теперь уже усиленно слежу за тем, чтоб вдруг не написать, что " ... решение X лучше, чем решение в Intel TBB" :- |
| 29.07.2009 08:45
ksili
|
Я так понял, свои варианты ответов уже можно предлагать )) 1) Это тот самый обещанный прорыв 2009 года. 2) видимо какой-то умелец решил реализовать решение задачи о ферзях на цифровой схемотехнике. Может даже использовал VHDL, а схема сгенерилась более-менее полуавтоматически. Получилась такая схема. В ней наверно параллелизм уже такой, что сильнее распараллелить не удастся. |
| 29.07.2009 08:53
ksili
| Я кстати сам никогда не пробовал реализовать решение задчи о ферзях. Но прочитав сейчас задание и посмотрев на доску, у меня в голове возникла схема перебора, которую по идее наиболее правильно было бы реализовать при помощи рекурсии. А потом смотрю - в 1) тоже рекурсивное решение. |
| 29.07.2009 08:59
yuryserdyuk
|
>1) Это тот самый обещанный прорыв 2009 года. Нет, неправильно - реализуйте приведенные выше 2 варианта программы на одном языке программирования и замерьте время их работы. Что получится ? Хотя по самому коду уже можно сделать вывод ... Вспомним также строчку из поста " ... Суммируем чего мы добились − мы представили конфигурации ферзей на доске в виде битовых векторов, а их модификацию выразили исключительно через битовые операции. " |
| 29.07.2009 09:05
yuryserdyuk
|
>2) видимо какой-то умелец решил реализовать решение задачи о ферзях на цифровой схемотехнике. Ну да, почти правильно, только с тем замечанием, что утверждение, что аппаратный параллелизм сильнее, чем программный не совсем верно. Другое дело, что в силу банальных причин аппаратная параллельная схема работает на порядок- другой быстрее любой программной параллельной реализации. |
| 30.07.2009 05:33
Alexey Kukanov (Intel)
|
> усиленно слежу за тем, чтоб вдруг не написать, что " ... решение X лучше, чем решение в Intel TBB" :-) Пишите, Юрий; при формулировке "лучше/хуже" по некоторому критерию (быстрее/медленнее, проще/сложнее) я буду только рад участвовать при конструктивном обсуждении. Но я возражаю против формулировок типа "правильный/неправильный" (подход, язык, модель) и против намеренно необъективного выставления языков/моделей/подходов, которые вы не разделяете, в невыгодном свете. К примеру, если на основе кода в вашей задачке номер 1 постулировать, что С/С++ - язык позапрошлого века, потому что на С# решение выглядит куда более элегантно - это было бы передёргиванием, против которого я бы возражал. |
| 30.07.2009 05:35
Alexey Kukanov (Intel)
|
"буду рад участвовать _в_ конструктивном обсуждении". Извините, при правке фразы не заметил несогласованность, а редактировать комментарии невозможно. |
| 30.07.2009 06:00
yuryserdyuk
|
Да, Дима, оказался прав, но приходится отвечать - слишком уж серьезные обвинения выставлены ... >намеренно необъективного выставления языков/моделей/подходов, >которые вы не разделяете, в невыгодном свете. Пожалуйста, приведите конкретные примеры во-первых, "намеренного" и во-вторых, "необъективного" - далее по тексту ... С этой же точки зрения, просьба прокомментировать пост Клэя Брешерса - http://software.intel.com/en-us/blogs/2006/12/18/threading-b.....a-problem/ - попадает ли он тоже под Вашу статью "намеренного и необъективного" ? Наконец, Вы, действительно, подумали, что приведенный мной вариант на С противопоставлен варианту на C# ;) ? Отнюдь нет - он приведен исключительно по двум причинам: 1) показать насколько компактно может быть записан этот алгоритм (кстати, его в абсолютно таком же виде можно записать и на C#), 2) показать пример умышленно компактной записи, которая бы затруднила верификацию этой программы (см. http://why.lri.fr/queens/index.en.html). |
| 30.07.2009 07:00
Alexey Kukanov (Intel)
|
Юрий, мне интересно Вас читать (было бы наоборот - я бы не стал вступать в споры), и мне не хотелось бы, чтобы Вы (и другие) думали, будто я целенаправленно на вас нападаю - поверьте, не имею такого намерения. Я не считаю, что Дима оказался прав - мы же пока что здесь не спорим о языках? Я вообще свой комментарий писал с другими, если не противоположными, целями, нежели обвинения. Но раз Вы его так восприняли (а значит, я плохо выразил свои мысли и намерения), мне придётся отвечать Вам. Однако, если Вы позволите, давайте переведём выяснение отношений (и приведение примеров) в приват. А пока я отвечу на два других вопроса. 1) Пост Клэя не попадает под моё понимание некорректного сравнения. Клэй сравнивает конструкции того же самого уровня TBB и OpenMP, и возражает против технически некорректного маркетинга. Вопрос "What did TBB bring to the table that wasn't already available from something else?" корректен и уместен. Точно так же его можно задать в отношении PPL, OpenMP 3.0, или MC#. Его вывод "Perhaps the marketing has overreached the initial 1.0 version of the product" не вызывает возражений. 2) Нет, я не подумал о противопоставлении языков в этом случае, просто воспользовался "подручным материалом" для иллюстрации своей позиции. Спасибо за комментарий о целях, код действительно отлично иллюстрирует их. |
| 30.07.2009 08:00
Dmitry Oganezov (Intel)
|
Я не авторитарен, я просто всегда прав :))))))) На самом деле пример 1) читать сложно. Но ведь на любом языке можно написать код так, чтобы он читался. И так, чтобы он выглядел как шифровка Штирлица. Более того, есть у меня подоозрение, что хороший компилятор сделает из обоих вариантов примерно одинаковый по эффективности исполняемый код (хотя последнее утверждение надо бы проверить). |
| 30.07.2009 13:41
mt2
|
> Но ведь на любом языке можно написать код так, чтобы он читался. И так, чтобы он выглядел как шифровка Штирлица. В принципе верно, но попробуйте написать хорошочитаемый код этого примера на ФОРТРАНе-4 (который рекурсию не поддерживал, и с битовыми операциями были проблемы ;))) |
| 16.08.2010 06:20
asdewq |
Спасибо, интересно! Будем читать дальше. http://infoprog.tk – программирование для начинающих и программирование для чайников |
Обратная ссылка (2)
- Блоги Intel® Software Network » Китайское искусство программирования 2, или 9,58 vs 22,317,699,616,364,044
05.09.2009 02:57 - Блоги Intel® Software Network » Суров закон параллелизма, или «что за #@%&^ этот ваш параллелизм?»
11.12.2009 04:31






Dmitry Oganezov (Intel)
25,473
Кстати, как там наши в TC2009? Что-то я не от русских участников, не от американских судей давненько ничего не слышал.
Что такое 1) я кажись понял. Пошел за чипсами и попкорном, надо подготовиться к холивару постов на 50 ;)