Понимание внутреннего продукта APL
Вот отрывок из книги Mastering Dyalog APL из главы " Внутренние продукты":
HMS is a variable which contains duration in Hours, Minutes, and Seconds: HMS ← 3 44 29 Chapter J – Operators 397
We would like to convert it into seconds. We shall see 3 methods just now, and a 4th
method
will be given in another chapter.
A horrible solution (3600×HMS[1]) + (60×HMS[2]) + HMS[3]
A good APL solution +/ 3600 60 1 × HMS
An excellent solution with Inner Product 3600 60 1 +.× HMS
Затем говорится, что второе и третье решения эквивалентны с точки зрения количества набранных символов и производительности.
Насколько я понимаю, программисты APL, как правило, должны как можно больше использовать Inner Product, а также Outer Product. Это верно?
Можете ли вы привести пример, когда использование Inner Product приведет к увеличению производительности? Что именно происходит, когда я использую Inner Product (на более низком уровне)? Является ли первое решение, представленное ниже, ужасным только потому, что оно не использует синтаксис APL надлежащим образом или оно действительно имеет худшую производительность?
Я знаю, что есть несколько вопросов, но в целом я хочу задать вопрос о том, как работают Внутренние / Внешние продукты и когда именно их должен использовать программист APL.
3 ответа
Программисты APL, как правило, должны максимально использовать Внутренний продукт, а также Внешний продукт. Это верно?
Это действительно зависит от программиста APL и стоящей перед ним задачи, но если что-то делает код APL более лаконичным и эффективным, я не понимаю, почему программист не выбрал бы его.
В этом конкретном случае 60⊥HMS
еще более лаконичен и эффективен, чем внутренний продукт.
Можете ли вы привести пример, когда использование Inner Product приведет к увеличению производительности?
Как это обычно бывает в программировании на основе массивов, прирост производительности достигается за один раз. Большинство функций APL являются неявными циклами - их реализация использует счетчик, ограничение для него и шаг приращения. Чем короче ваш код, тем лучше, потому что он не только легче держать в голове, но и эффективнее, поскольку интерпретатору нужно меньше проходить через данные. Некоторые реализации делают объединение циклов в попытке уменьшить эти издержки. У некоторых есть идиома- определенные комбинации загогулин в специальном случае переводчика. Выполнение действий за один раз также позволяет интерпретатору выполнять умные оптимизации, такие как использование набора инструкций SSE или графических процессоров.
Возвращаясь к внутреннему продукту, давайте возьмем пример A f.g B
где A
а также B
векторы и посмотрим, как f
а также g
применяются (в Дьялоге):
f←{⎕←(⍕⍺),' f ',⍕⍵ ⋄ ⍺+⍵}
g←{⎕←(⍕⍺),' g ',⍕⍵ ⋄ ⍺×⍵}
0 1 2 3 4 f.g 5 6 7 8 9
4 g 9
3 g 8
24 f 36
2 g 7
14 f 60
1 g 6
6 f 74
0 g 5
0 f 80
80
Из вышесказанного видно, что звонки f
а также g
чередуются. Переводчик яблок f
и уменьшает на g
одновременно, за один проход, избегая создания временного массива, как f/ A g B
сделал бы.
Другой пример: http://archive.vector.org.uk/art10500200
Вы можете проверить производительность различных решений для себя и посмотреть, какое из них работает лучше всего:
)copy dfns.dws cmpx
⍝ or: ")copy dfns cmpx" if you are using Windows
HMS ← 3 44 29
cmpx '(3600×HMS[1]) + (60×HMS[2]) + HMS[3]' '+/ 3600 60 1 × HMS' '3600 60 1 +.× HMS' '60⊥HMS'
(3600×HMS[1]) + (60×HMS[2]) + HMS[3] → 2.7E¯6 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
+/ 3600 60 1 × HMS → 9.3E¯7 | -66% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
3600 60 1 +.× HMS → 8.9E¯7 | -68% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
60⊥HMS → 4.8E¯7 | -83% ⎕⎕⎕⎕⎕⎕⎕
Мы проделали работу по оптимизации как +/, так и +. ×.
MBaas прав в том, что бывает, что +/ в этом случае немного лучше, чем +. ×
Наш общий совет: используйте конструкции на языке, который лучше всего подходит для работы, и в конечном итоге реализация наверстает упущенное.
"Ужасное" решение считается плохим, так как оно не использует массивное мышление.
С Уважением,
Винс, Dyalog Поддержка
Проблема с обобщением заключается в том, что они могут быть неправильными, но, как я бы сказал, использование внутренних и внешних продуктов будет способствовать удобочитаемости и производительности;-)
Теперь посмотрим на вещь на практике:
`] performance.RunTime (3600 × HMS [1]) + (60 × HMS [2]) + HMS [3] -repeat =100000
Сравнительный анализ "(3600×HMS[1])+(60×HMS[2])+HMS[3]", повтор =100000 Эксп. ЦП (среднее значение): 0,001558503836 Истекло: 0,001618446292
] performance.RunTime '+ / 3600 60 1 × HMS' -repeat =100000
Бенчмаркинг "+/ 3600 60 1 × HMS", повтор =100000 Эксп. ЦП (ср.): 0.0004698496481 Прошло: 0.0004698496481 `
Это большая разница - если вы повторите это достаточно много раз, чтобы его можно было измерить;-) Но, конечно, с большим набором данных преимущество становится более заметным! Давайте также посмотрим на 3 варианта:
`] performance.RunTime '3600 60 1 +. × HMS' -repeat =100000
- Бенчмаркинг "3600 60 1 +.× HMS", повтор =100000 Эксп. ЦП (среднее значение): 0.0004698496481 Истекло: 0.000439859245
`
Здесь нет никакой разницы, но опять же - с "реальными данными" (большим массивом) вы должны увидеть гораздо более четкую разницу. Я думаю, что простое объяснение состоит в том, что внутренний продукт подобен одному "утверждению" для интерпретатора, тогда как первый вариант имеет 3 одинарных умножения, индексации и должен учитывать приоритеты (скобки), а затем суммировать этот вектор, который звучит как много sweat;-) 2-е утверждение имеет только одно умножение (для вектора), поэтому оно уже облегчает несколько шагов, и внутренний продукт позволяет интерпретатору, возможно, объединить некоторые из его внутренних операций, чтобы выполнять свою работу еще быстрее.
НО теперь вот сюрприз: v1←(10000/3600 60 1) ⋄v2← 10000/HMS ] Производительность.RunTime '+/v1 × v2' 'v1 +.× v2' -repeat=100000 -сравнить
+/v1 × v2 → 6.0E¯5 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
v1 +.× v2 → 6.3E¯5 | +5% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
Я ожидал, что более крупные аргументы помогут сделать преимущество в производительности последнего выражения более заметным, но на самом деле победил второй. Может быть, Dyalog оптимизировал случай № 2 больше, чем № 3...;-)