Галогенное оптимальное планирование

Я пытаюсь разработать наилучшее расписание для эталонного кода Halide, и я могу что-то упустить, потому что результаты времени не имеют для меня особого смысла.

Я использую компиляцию AOT, и вот часть алгоритма кода:

ImageParam input1(type_of<float>(), 3);
ImageParam input2(type_of<float>(), 3); 
Func in1 = BoundaryConditions::constant_exterior(input1, 0.0f);
Func in2 = BoundaryConditions::constant_exterior(input2, 0.0f);   
f1(x, y, z) = (in1(x + 1, y, z) + in1(x, y, z) + in1(x - 1, y,z));
f2(x, y, z) = (in2(x + 2, y, z) + in2(x + 1, y, z) + in2(x, y, z) +in2(x - 1, y, z) + in2(x - 2, y, z));
res(x, y, z) = f1(x, y, z) + f1(x - 1, y, z) + f2(x - 1, y, z) + f2(x, y, z);

Для расписания это то, что у меня есть:

f1.store_at(res, y).compute_at(res, yi).vectorize(x, 8);
f2.store_at(res, y).compute_at(res, yi).vectorize(x, 8);
res.split(y, y, yi, 8).vectorize(x, 8).parallel(y);
res.print_loop_nest();

Я использую функцию current_time для определения времени выполнения моего кода. Когда я использую упомянутое расписание как для f1, так и для f2, время выполнения больше, чем когда я использую расписание только для одной из этих функций. Учитывая структуру трафаретов, я подумал, что планирование их обоих улучшит производительность. Что мне здесь не хватает? Также, когда я печатаю циклы, чтобы увидеть сгенерированный код:

  for k:
    parallel j.j:
      store f1:
        store f2:
          for j.in_y in [0, 7]:
            produce f1:
              for k:
                for j:
                  for i.i:
                    vectorized i.v122 in [0, 7]:
                      f1(...) = ...
            consume f1:
              produce f2:
                for k:
                  for j:
                    for i.i:
                      vectorized i.v126 in [0, 7]:
                        f2(...) = ...
              consume f2:
                for i.i:
                  vectorized i.v133 in [0, 7]:
                    result(...) = ...
consume result:

Это просто отступ или продукт f2 вложен в продукт f1? Есть предложения по лучшему расписанию?


person B.Md    schedule 23.12.2016    source источник


Ответы (1)


Я думаю, что это, вероятно, ограничено пропускной способностью памяти. Несколько дополнительных добавлений, подразумеваемых встраиванием f1 или f2 в res, на самом деле не имеют значения. Действительно, я получаю лучшую производительность при следующем графике:

in1.compute_at(res, yi).vectorize(in1.args()[0], 8);
in2.compute_at(res, yi).vectorize(in2.args()[0], 8);
res.split(y, y, yi, 8).vectorize(x, 8).parallel(y);

т.е. просто вытягивая дополненную строку сканирования каждого входа, а затем выполняя всю встроенную математику.

Но он едва ли быстрее вашего. Разница может заключаться в шуме. Мой полный эксперимент:

https://gist.github.com/abadams/c2e6f67d79e1768af6db5afcabb1caab

Продукция f2 вложена внутрь consume f1. Это нормально - он не использует f1, но он используется чем-то, что использует f1, так что это разумное место для его завершения.

person Andrew Adams    schedule 23.12.2016
comment
Я не понял, что делает часть .args()!? Кроме того, есть ли у вас какие-либо предложения о том, как найти хорошее расписание для случая, когда один из шаблонов расположен вертикально и смотрит на соседей по оси Y? Или, может быть, трафареты, которые представляют собой комбинацию двух и не такие простые, как этот? Я пытался разбить вывод (вместо разделения), но это кажется слишком медленным. - person B.Md; 23.12.2016