Ответ 1
Общее правило заключается в том, что для программирования "vanilla" Haskell вы получаете очень мало (если есть) контроль над расположением памяти и локальностью памяти.
Однако существует ряд более продвинутых функций, которые позволяют такой контроль, и библиотеки, которые выставляют дружественные абстракции поверх них. Библиотека vector
, вероятно, самая популярная из последних. Эта библиотека предоставляет несколько типов массивов фиксированного размера, два из которых (Data.Vector.Unboxed
и Data.Vector.Storable
) дать вам местоположение данных, представляя векторы и их содержимое как непрерывные массивы памяти. Data.Vector.Unboxed
даже содержит простую автоматическую трансформацию "структуры массивов" - распакованный вектор пар будет представлен как пара нераспознанных векторов, по одному для каждой из парных компонентов.
Другим примером является библиотека JuicyPixels
для обработки изображений, которая представляет изображения в памяти как непрерывные растровые изображения. Это фактически заканчивается на Data.Vector.Storable
, который использует стандартную установку (Foreign.Storable
) для перевода пользовательских типов данных Haskell в и из необработанных байтов.
Но общий шаблон таков: в Haskell, когда вы заинтересованы в локальности памяти, вы определяете, какие данные должны извлечь выгоду из этого и объединить их вместе в пользовательский тип данных, реализация которого была разработана для обеспечения локальности и гарантии производительности, Написание такого типа данных является передовой задачей, но большая часть работы была уже сделана уже многократно (обратите внимание, например, что JuicyPixels
в основном просто повторяет vector
).
Обратите внимание, что:
-
vector
обеспечивает оптимизацию потока для устранения промежуточных массивов при применении вложенных векторных преобразований. Если вы создаете вектор от 0 до 1 000 000, отфильтруйте четные числа, сопоставьте функцию(^2)
над этим и суммируйте элементы результата, ни один массив не будет выделен - библиотека имеет умение переписывать это в цикл аккумулятора от 0 до 1 000 000. Таким образом,foldl
вектора не обязательно медленнее, чем циклfor
- не может быть никакого массива вообще! -
vector
также предоставляет изменяемые массивы. В более общем плане, в Haskell вы можете перезаписать существующую память, если вы действительно настаиваете. Это просто (а) не парадигма по умолчанию на языке, и поэтому (б) немного неуклюжий, но абсолютно послушный, если вам просто нужно это в нескольких чувствительных к производительности местах.
Поэтому большую часть времени ответ "Я хочу местность памяти" - "use vector
."