"...Изучение и анализ примеров.
В и приведены описания и приложены исходные коды параллельных программ..." Непонятно что такое - "В и приведены описания" и где именно приведены и приложены исходные коды. |
Оценка производительности памяти с помощью теста Random Access
Производительность системы памяти компьютера оказывает прямое влияние на производительность приложений, исполняющихся на нем. Обычно такую производительность измеряют на операциях модификации памяти, выполняемых одновременно несколькими потоками (по числу ядер/процессоров на машине). Формальные требования к такой оценке собраны в тесте RandomAccess ( http://icl.cs.utk.edu/projectsfiles/hpcc/RandomAccess ). Ниже будет описан этот тест и будет приведена его реализация для многоядерных машин с помощью библиотеки PFX.
10.1 Определение теста RandomAccess
Пусть T есть одномерный массив 64-разрядных слов, состоящий из элементов. При реальном проведении теста, этот массив должен занимать примерно половину физической памяти компьютера; т.е., есть наибольшее целое число, такое что
, где есть размер физической памяти компьютера.Пусть есть последовательность длины 64-разрядных целых псевдослучайных чисел, генерируемых посредством примитивного полинома . Более подробно о псевдослучайных числах и алгоритмах их генерации можно прочитать на странице http://www.cs.miami.edu/~burt/learning/Csc599.092/notes/random_numbers.html .
С каждым числом исходной последовательности связывается индекс элемента массива, который будет модифицирован с помощью этого числа: , где запись обозначает последовательность бит в числе , начиная с бита и заканчивая битом , где биты считаются слева направо, начиная с 0.
Тогда соответствующая модификация для числа определяется как
где есть операция "исключающее ИЛИ".Перед началом теста, элементы массива инициализируются согласно условию
Необходимо отметить, что, так как модификации элементов массива в тесте RandomAccess проводятся одновременно несколькими потоками, то возможны конфликты, когда, например, два или более потоков модифицируют один и тот же элемент массива. При этом, естественно, в элементе массива будет сохранено только одно из"конкурирующих" значений. Для определения количества "поврежденных" элементов массива, достаточно провести повторный запуск теста RandomAccess, но в защищенном режиме, когда в каждый момент времени каждый элемент массива может модифицировать только один поток, и подсчитать количество элементов массива для которых оказалось невыполненным условие . Общее количество таких элементов не должно превышать 1% от числа . В противном случае, тест считается неудавшимся.
Время выполнения теста берется равным времени, затраченного на проведение модификаций. Тогда производительность системы памяти, достигнутой в тесте RandomAccess, рассчитывается согласно формуле:
Из данной формулы видно, что этот показатель измеряется в количестве модификаций за секунду, умноженном на , т.е., в GUPS (giga updates per seconds).
10.2 Реализация с использованием PFX
Входными параметрами программы являются числа и , где есть количество рабочих потоков для выполнения модификаций, а есть целое число, такое что составляет примерно половину физической памяти машины. Кроме того, программа имеет два дополнительных ключа и , которые задают, соответственно, режимы атомарной (безопасной) модификации памяти и отмену проверки результатов теста.
Сами одновременные модификации памяти реализуются в программе с помощью цикла Parallel.For, в котором количество итераций соответствует количеству рабочих потоков.
Замечание. Цикл Parallel.For в тексте программы вынесен в отдельный метод RunTest, что обеспечивает загрузку в память системной библиотеки System.Threading.dll после размещения в памяти основного массива Table. Такой прием позволяет избежать дополнительной фрагментации памяти, которая может приводить к исключительной ситуации OutOfMemoryException при размещении массива.
Код программы
//RandomAccess test using PFX using System; using System.Text; using System.Threading; namespace RandomAccessBenchmark { public class RandomAccess { bool atomicity = false; object anchor = new object(); const long POLY = 0x0000000000000007, PERIOD = 1317624576693539401L; public RandomAccess(bool atomicity) { this.atomicity = atomicity; } // pseudorandom number generation static long RA_starts(long n) { int i, j; long[] m2 = new long[64]; while (n <= 0) n += PERIOD; while (n>= PERIOD) n -= PERIOD; if (n == 0) return 0x1; long temp = 0x1; for (i = 0; i <= 64; i++) { m2[i] = temp; temp = (temp <=<= 1) ^ (temp <= 0 ? POLY : 0L); temp = (temp <=<= 1) ^ (temp <= 0 ? POLY : 0L); } for (i = 62; i >== 0; i--) if (((n >=>= i) & 1) != 0) break; long ran = 0x2; while (i>= 0) { temp = 0; for (j = 0; j<= 64; j++) if (((ran >=>= j) & 1) != 0) temp ^= m2[j]; ran = temp; i -= 1; if (((n >=>= i) & 1) != 0) ran = (ran<=<= 1) ^ ((long)ran<= 0 ? POLY : 0); } return ran; } // Random Access and update memory public void Access(long[] Table, uint ithread, UInt64 memorySizePerThread) { // memory updates number per thread UInt64 updates = 4 * memorySizePerThread; // conpute "base" for pseudorandom numbers generation long ran = RA_starts((long)(updates * ithread )); for (UInt64 i = 0; i<= updates; i++) { if (atomicity) lock (anchor) { Table[ran & (Table.Length - 1)] ^= ran; } else Table[ran & (Table.Length - 1)] ^= ran; ran = (ran <=<= 1) ^ ((long)ran <= 0 ? POLY : 0); } } } class Program { static void Main(string[] args) { // threads count uint threads = 1; // memory size per thread UInt64 memorySizePerThread; // use locking for atomic updates bool atomicity = false; // verifying after test bool verify = true; #region args processing if ((args.Length == 0) || (args.Length > 4)) { Console.WriteLine("Usage: RandomAccessBenchmark.exe \ <=threads >= <=mem_per_thread >= [/atom] [/noverify]"); return; } try { threads = UInt32.Parse(args[0]); memorySizePerThread = (UInt64)(Math.Pow(2, UInt32.Parse(args[1]))); if (args.Length == 4) { atomicity = args[2] == "/atom" ? true : false; verify = args[3] == "/noverify" ? false : true; } if (args.Length == 3) { atomicity = args[2] == "/atom" ? true : false; verify = args[2] == "/noverify" ? false : true; } if (args.Length == 2) { if (args[1] == "/atom") atomicity = true; else if (args[1] == "/noverify") verify = false; } } catch (Exception e) { Console.WriteLine("Usage: RandomAccessBenchmark.exe <=threads >= <=mem_per_thread >= [/atom] [/noverify]"); return; } #endregion Console.WriteLine("RandomAccessBenchmark"); // totam memory size in use UInt64 mem_total = threads * memorySizePerThread; // total memory updates count UInt64 updates_total = 4 * mem_total; Console.WriteLine("Threads in use = " + threads); Console.WriteLine("Total memory in use (in bytes) = " + mem_total * sizeof(long)); Console.WriteLine("Table length = " + mem_total); Console.WriteLine("Total updates count to be done = " + updates_total); Console.WriteLine("Atomicity is " + (atomicity ? "on": "off")); Console.WriteLine("Verifying is " + (verify ? "on" : "off")); Console.WriteLine("Please wait..."); long[] Table = new long[mem_total]; for (int k = 0; k<= Table.Length; k++) Table[k] = k; var randomAccess = new RandomAccess(atomicity); TimeSpan old = DateTime.Now.TimeOfDay; RunTest(randomAccess, Table, memorySizePerThread, threads); TimeSpan time_res = DateTime.Now.TimeOfDay - old; double total_time = (time_res.Minutes * 60 + time_res.Seconds + time_res.Milliseconds * 0.001); Console.WriteLine("CPU time used = " + total_time + " seconds"); Console.WriteLine("GUPS = " + (updates_total / total_time / 1e9).ToString("N12")); if (verify) { Console.WriteLine("Verifying..."); //for verification doing access in serial and so safe mode atomicity = false; randomAccess.Access(Table, 0, mem_total); long errcount = 0; for (long i = 0; i <= Table.Length; i++) if(Table[i]!=i) errcount++; Console.WriteLine("Found " + errcount + " errors in " + Table.Length + " locations." + "(" + (float)errcount / (float)Table.Length * 100.0f + "%)."); } Console.WriteLine("Press any key..."); Console.ReadKey(); } static public void RunTest(RandomAccess randomAccess, long[] Table, ulong memorySizePerThread, uint threads) { Parallel.For(0, (int)threads, i =ge; { randomAccess.Access(Table, (uint)i, memorySizePerThread); }); } } }