Да, действительно, факторизация по трем первым простым числам мне тоже показалась «оптимальной» именно из-за восьми бит на период.
Хочу только отметить, что если взять достаточно большое количество простых чисел, можно добиться произвольно большого сжатия. Я когда-то подсчитал, что для факторизации по первым четырем миллиардам простых чисел потребуется примерно 0.0225 бит на натуральное число. Однако на практике, конечно, этот алгоритм неприменим, потому что выгода начнется только для простых чисел, состоящих из триллионов (десятичных) знаков. Кроме того, размер таблицы INDEX_TO_BIT будет больше количества атомов во вселенной.
Вроде бы чисто теоретически должно сработать, но затрудняюсь сказать, насколько это усложнит код. Кроме того, размер поля 1 мегабайт, а размер линии — 1 килобайт (по 4бита на клетку), так что экономия на одной линии не особо должна повлиять на производительность. Вот действительно где можно было бы сильно сэкономить, — это хранить клетки не в 4 битах, а в одном бите, и распаковывать только для подсчетов, примерно как это делается в этой статье. К сожалению, чтобы биты расширить в байты более-менее эффективно, нужен десяток AVX-инструкций; я пытался делать, но производительность только ухудшилась. В AVX512 будет специальная инструкция для этого, тогда можно будет попробовать, но пока процессоры не особо его поддерживают.
Спасибо за комментарий, это действительно отличная идея, я ее проверил уже после написания статьи.
Тут есть техническая проблема: в AVX невозможно сдвинуть на 4 бита весь регистр как одно 256-разрядное число, минимум нужно делать два сдвига и одно сложение, так что ускорение будет немного меньше, чем в два раза, и код сильно усложняется, но выигрыш в скорости все равно получается существенный.
Вкупе с еще одной идеей: избавиться от временного буффера вообще, и хранить дополнительно только три линии поля (текущую, выше, и ниже) получается суммарно ускорение в 330 раз от начальной версии, от 25 до 7400 шагов в секунду!
Можно код посмотреть на гитхабе: AdvancedLifeExtensionsInLineCompressed.cs.
Спасибо за статью!
По поводу превосходства GPU: я когда-то пытался делать неоптимизированную реализацию на CUDA, и она оказалась сильно медленнее, чем я ожидал (медленнее вашей версии). Потом я наткнулся на эту статью, в которой утверждается, что ключ к оптимизации — расположение данных в GPU-памяти правильного типа (в идеале, в регистровой памяти).
Их производительность — 0.16 секунд на 1024 итераций на поле 16K на 16K на видеокарте GeForce GTX TITAN X. Эта разница из-за того, что видеокарты несопоставимы по производительности, или Вашу версию еще можно ускорить, если применить аналогичные трюки?
Вы имеете в виду, фигура, как на КПДВ? Вообще-то это твердый знак (ну, по крайней мере задумывался).
Нету смысла искать оптимальную стратегию в обычном тетрисе — известно, что он проходится до момента, пока не встретится какое-то количество z- и s- фигурок в определенной последовательности. Вот я и добавил фигурку, которая все «портит», чтобы вручную его тоже было сложно пройти — я, например, дальше компьютера не смог.
Спасибо! Было бы интересно, чего можно достичь на не-топовых FPGA.
Вообще интересно, чего можно достичь на разных технологиях за разные деньги. Я по сумме всех комментариев понял примерно так (плюс-минус порядок):
— процессор ($1'000) — 50'000 итераций в секунду
— видеокарта ($1'500) — 500'000 итераций в секунду
— FPGA ($15'000) — 50 миллионов итераций в секунду
— ASIC ($1'500'000) — 1 миллиард итераций в секунду
А это чисто физически можно сделать на двумерной схеме? Ведь у каждой клетки будет свой сумматор, и надо будет к нему тянуть линию от всех восьми ее соседей.
Круто! Получается, скорость будет ограничена только пропускной способностью канала памяти? После Вашего комментария полез искать, пользуются ли FPGA для ресёрча, и нашел, что пользуются, но, похоже, это единичные случаи.
Два вопроса по этому поводу (чисто теоретических):
1) Нашел в гугле, что есть FPGA Virtex-7 2000T с двумя миллионами ячеек; в нем можно сделать симуляцию игры не в памяти, а напрямую в этих ячейках? Или топология не позволяет?
2) Может быть, можно сделать специализированную микросхему с двумя мегабайтами SRAM, и к каждой ячейке прикрутить трехбитный сумматор? Тогда теоретически можно добиться по одной итерации на такт?
Ваш комментарий верен в общем случае, но при обходе двумерного массива внутренний цикл должен быть по второму индексу, так что в данном случае этот порядок правильный, можете поменять местами и проверить. Тут скорее проблема в том, что массив определялся как new bool[WIDTH, HEIGHT], хотя надо было бы длину и ширину поменять местами.
А вот — русский вариант:
Насколько я понимаю, ministro de culto — это религиозный деятель, а министр культуры будет ministro de cultura. Этому переводу, кстати, уже лет 10, удивительно, что его так и не исправили, можете по ссылке сходить, посмотреть.
Если Вы имеете в виду наложение кода на картинку, то не фотошопом: я использовал paint.net, сгенерировал пять картинок, и воспользовался одним из онлайн-генераторов анимированных gif.
Я провел такой эксперимент, ускорение получилось в 3.5 раза для рандомной начальной конфигурации (но, конечно, для маленьких и статичных конфигураций ускорение может быть в сотни и тысячи раз)
Обсуждение и код в этой ветке. Может быть, кто-то сможет еще больше ускорить.
Я прошу прощения, нам с самого начала надо было вести обсуждение под Вашим комментарием (теперь, наверно, не перенести).
Последовал Вашему совету и полез в мой любимый zlib. Поймите меня правильно, я люблю ассемблер, и сам на нем когда-то писал, но не могу удержаться:
/* inffast.c -- fast decoding
* Copyright (C) 1995-2017 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
#include "zutil.h"
#include "inftrees.h"
#include "inflate.h"
#include "inffast.h"
#ifdef ASMINF
# pragma message("Assembler code may have bugs -- use at your own risk")
#else
...
Кстати, насчет ассемблера: у меня изначально была реализация на C++ через интринсики, но она работала с абсолютно такой же скоростью, что и версия на C#. Я не ожидал, что JIT сможет скомпилировать такой же хороший код, что и C++
Хочу только отметить, что если взять достаточно большое количество простых чисел, можно добиться произвольно большого сжатия. Я когда-то подсчитал, что для факторизации по первым четырем миллиардам простых чисел потребуется примерно 0.0225 бит на натуральное число. Однако на практике, конечно, этот алгоритм неприменим, потому что выгода начнется только для простых чисел, состоящих из триллионов (десятичных) знаков. Кроме того, размер таблицы INDEX_TO_BIT будет больше количества атомов во вселенной.
Тут есть техническая проблема: в AVX невозможно сдвинуть на 4 бита весь регистр как одно 256-разрядное число, минимум нужно делать два сдвига и одно сложение, так что ускорение будет немного меньше, чем в два раза, и код сильно усложняется, но выигрыш в скорости все равно получается существенный.
Вкупе с еще одной идеей: избавиться от временного буффера вообще, и хранить дополнительно только три линии поля (текущую, выше, и ниже) получается суммарно ускорение в 330 раз от начальной версии, от 25 до 7400 шагов в секунду!
Можно код посмотреть на гитхабе: AdvancedLifeExtensionsInLineCompressed.cs.
По поводу превосходства GPU: я когда-то пытался делать неоптимизированную реализацию на CUDA, и она оказалась сильно медленнее, чем я ожидал (медленнее вашей версии). Потом я наткнулся на эту статью, в которой утверждается, что ключ к оптимизации — расположение данных в GPU-памяти правильного типа (в идеале, в регистровой памяти).
Их производительность — 0.16 секунд на 1024 итераций на поле 16K на 16K на видеокарте GeForce GTX TITAN X. Эта разница из-за того, что видеокарты несопоставимы по производительности, или Вашу версию еще можно ускорить, если применить аналогичные трюки?
Нету смысла искать оптимальную стратегию в обычном тетрисе — известно, что он проходится до момента, пока не встретится какое-то количество z- и s- фигурок в определенной последовательности. Вот я и добавил фигурку, которая все «портит», чтобы вручную его тоже было сложно пройти — я, например, дальше компьютера не смог.
Вообще интересно, чего можно достичь на разных технологиях за разные деньги. Я по сумме всех комментариев понял примерно так (плюс-минус порядок):
— процессор ($1'000) — 50'000 итераций в секунду
— видеокарта ($1'500) — 500'000 итераций в секунду
— FPGA ($15'000) — 50 миллионов итераций в секунду
— ASIC ($1'500'000) — 1 миллиард итераций в секунду
Два вопроса по этому поводу (чисто теоретических):
1) Нашел в гугле, что есть FPGA Virtex-7 2000T с двумя миллионами ячеек; в нем можно сделать симуляцию игры не в памяти, а напрямую в этих ячейках? Или топология не позволяет?
2) Может быть, можно сделать специализированную микросхему с двумя мегабайтами SRAM, и к каждой ячейке прикрутить трехбитный сумматор? Тогда теоретически можно добиться по одной итерации на такт?
Если кому интересно, простой поиск по коду Linux находит дофига и переменных temp, и копирования в и из них
А вот — русский вариант:
Насколько я понимаю, ministro de culto — это религиозный деятель, а министр культуры будет ministro de cultura. Этому переводу, кстати, уже лет 10, удивительно, что его так и не исправили, можете по ссылке сходить, посмотреть.
Обсуждение и код в этой ветке. Может быть, кто-то сможет еще больше ускорить.
Я прошу прощения, нам с самого начала надо было вести обсуждение под Вашим комментарием (теперь, наверно, не перенести).
Лучше, чем LifeBytes, но хуже, чем LongLife. Код:
По поводу последнего: в такой большой рандомной картинке даже на тысяной итерации изменяется около сорока тысяч клеток:
насчет ассемблера: у меня изначально была реализация на C++ через интринсики, но она работала с абсолютно такой же скоростью, что и версия на C#. Я не ожидал, что JIT сможет скомпилировать такой же хороший код, что и C++