Представим, что перед нами стоит задача перемножить две матрицы A и B размера n×n. Как можно это осуществить? Применим алгоритм, действующий прямо по определению (назовем его “Iterative”). for i <- 1 to n do for j<- 1 to n do cij <-0 for k<-1 to n do cij<-cij + aikbk j Ci,j = ∑ aikbk j Время его выполнения на реальной машине: 140 c (N = 210) Тестирование происходило на системе: Intel Core i5 2.27Ghz 4096 Mb L1 32 Kb x2 64 B L2 256 Kb x2 64 B Windows 7 Разобьём каждую матрицу на (n/s)*(n/s) подматриц Мij размером s×s Следующий алгоритм использует эту стратегию(“Block-Mult”): for i<-1 to n/s do for j<-1 to n/s do for k<-1 to n/s do Ord-Mult(Aik, Bkj, Cij, s) ORD-MULT (A, B, C, s) – функция, которая присваивает C = C + AB. Использует итеративный алгоритм, описанный ранее. Изменение времени работы алгоритма от параметра s: t, sec 160 150 140 130 Block-Mult 120 110 100 90 2 4 8 16 32 64 128 256 512 S t, sec 160 150 140 130 Block-Mult Iterative 120 110 100 90 2 4 8 16 32 64 128 256 512 S Для умножения можно воспользоваться следующим алгоритмом (обозначим его за “Mult(A)”): Для умножения матрицы A размером m×n на матрицу размером n×p рассматривается три случая: m >= max{n, p} AB = ( A1 )B = ( A1B ) A2 n> = max{m,p} AB = (A 1 A2B B1 A 2)( ) = A 1 B 1 + A 2B2 B2 p> = max{m,n} AB = A(B 1 B 2) = (AB 1 AB 2) Это продолжается пока m = n = p = 1. Время его работы можно увидеть на графике: t, sec 160 150 140 130 Block-Mult Iterative Mult(A) 120 110 100 90 2 4 8 16 32 64 128 256 512 S Исследователи из MIT 1994 Чарльз Лейзерсон 1997 Гаральд Прокоп 1999 (Z, L) – идеальная модель кэша(“ideal-cache model”)(она будет использоваться для оценки сложности кэша (“cache complexity”)) Кэш-линии(“cache lines”) Высокий кэш(“tall cache”) (Z = Ω(L2)) Попадание в кэш (“cache hit”) Промах кэша (“cache miss”) W(n) - рабочая сложность (“work complexity”) Q(n, Z, L) - кэш сложность (“cache complexity”). Кэш зависимый (“cash aware”) Кэш-независимый (“cache oblivious”) Будут ли кэш-независимые алгоритмы, разработанные в рамках идеальной модели, работать также эффективно для модели с политиками LRU и FIFO или другой иерархией кэша? (Кэш сложность будет называться нормальной (“regular”), если Q(n; Z, L) = O(Q(n; 2Z, L))) Теорема: Оптимальный кэш-независимый алгоритм с нормальной кэш сложностью остается оптимальным в двухуровневой модели с вытесняющими политиками (LRU, FIFO). <(Z1, L1), (Z2,L2),…, (Zr,Lr) > - многоуровневая идеальная модель Свойство вложенности (“inclusion property”): Ʉi є [1,…, r-1] значения, хранимые в кэше i, хранятся и в кэше i+1 W(n), Qi(n; Zi, Li) - кэш сложности для Ʉi є[1,…,r] Теорема: Если кэш-независимый алгоритм оптимален в многоуровневой идеальной модели, то он вызывает оптимальное количество потерь кэша и на каждом уровне кэша. Теорема: Оптимальный кэш независимый алгоритм с нормальной кэш сложностью вызывает асимптотически оптимальное количество потерь кэша на каждом уровне кэша с оптимальной вытесняющей политикой или LRU. (Доказательство этих теорем можно найти в Harald Prokop. “Cache-Oblivious Algorithms. Masters thesis”, MIT. 1999.) Нужно перемножить две матрицы A и B размера n×n. Будем считать, что матрицы хранятся в построчном порядке и что n>L(чтобы упростить анализ). Построчный (“row major”) По столбцам (“column major”) “Iterative”: for i <- 1 to n do for j<- 1 to n do cij <-0 for k<-1 to n do cij<-cij + aikbk j Временная сложность - O(n³) Количество промахов кэша - Ω(n3/L) “Block-Mult”: for i<-1 to n/s do for j<-1 to n/s do for k<-1 to n/s do Ord-Mult(Aik, Bkj, Cij, s) ORD-MULT (A, B, C, s) – функция, которая присваивает C = C + AB. Использует итеративный алгоритм, описанный ранее(O(s3)). ϴ(n + n^2/L + n^3/(L*Z^(1/2))) “Mult(A)”: Для умножения матрицы A размером m×n на матрицу размером n×p рассматривается три случая: A1 A1B m >= max{n, p} AB = ( A2 )B = ( A2B ) B1 n> = max{m,p} AB = (A 1 A 2)( B2 ) = A 1 B 1 + A 2B2 p> = max{m,n} AB = A(B 1 B 2) = (AB 1 AB 2) Это продолжается пока m = n = p = 1. ϴ(mnp), ϴ(m + n + p + (mn + np + mp)/L + mnp/L(Z1/2)) 1. Самый быстрый с точки зрения асимптотической сложности алгоритм имеет не больше Ω(lg Z) промахов кэша, чем асимптотически самый быстрый кэш зависимый алгоритм. 2. Если существует класс оптимальных кэш зависимых алгоритмов, решающих определённую задачу, то существует кэш-независимый алгоритм, решающий ту же задачу. Нужно транспонировать квадратную A матрицу размера N×N. Применим алгоритм, действующий прямо по определению (назовем его “Iterative”). for (i = 0; i < N; i++) { for (j = i+1; j < N; j++) { tmp = A[i][j]; A[i][j] = A[j][i]; A[j][i] = tmp; } } Рассмотрим время его выполнения на реальной машине: Тестирование происходило на системе: UltraSPARC-II 300 MHz 512 мб L1 32 б 16 кб L2 64 б 2 мб SunOS 5.6 SUN’s Workshop 4.2. Время работы от размера от изменения входных данных можно увидеть на графике: t, sec 14 12 10 8 Iterative 6 4 2 0 10 11 12 13 N Если учесть затраты на ввод и вывод данных между кэшем и основной памятью, то можно в несколько раз уменьшить время работы алгоритма. Z - размер всего кэша L - длина линий кэша Aʳ΄ˢ = { ai,j | rL <= i < (r+1)L, sL <= j < (s+1)L, 0 <= r,s < N/B-1, r,s є NU{0} }. C = Aᵀ, где Cˢ΄ʳ = (Aʳ΄ˢ)ᵀ. “Block-Transpose” Пример его применения: L=2 1 2 3 4 1 2 3 4 5 6 7 8 5 6 7 8 9 10 11 12 9 10 11 12 13 14 15 16 13 14 15 16 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 3 7 4 8 Время его работы можно увидеть на графике: t, sec 14 12 10 8 Iterative Block-Transpose 6 4 2 0 10 11 12 13 N Для транспонирования можно воспользоваться следующим алгоритмом (обозначим его за “Transpose(A)”): Вход: Матрица A размера m×n Выход: Матрица B размера n×m Если n>=m A = (A1 A2) B = ( B1 ) B2 A1 Если n<m A = ( A2 ) B = ( B1 B2 ) B1 = Transpose(A1), B2 = Transpose(A2) Время его работы можно увидеть на графике: t, sec 14 12 10 Iterative 8 Block-Transpose Transpose(A) 6 4 2 0 10 11 12 13 N Изменим размер линий кэша (L = 27) t, sec 14 12 10 Iterative 8 Block-Transpose Transpose(A) 6 4 2 0 10 11 12 13 N Изменим размер линий кэша (L = 26) t, sec 14 12 10 Iterative Block-Transpose 8 Transpose(A) Iterative(2) Block-Transpose(2) 6 Transpose(A)(2) 4 2 0 10 11 12 13 N Изменим размер линий кэша (L = 25) t, sec 14 12 Iterative 10 Block-Transpose Transpose(A) Iterative(2) 8 Block-Transpose(2) Transpose(A)(2) Iterative(3) 6 Block-Transpose(3) Transpose(A)(3) 4 2 0 10 11 12 13 N Гареев Роман КН - 301, 2011 год