Здесь мы излагаем видение проекта xtensor, n-мерного массива на языке C ++, который упрощает написание высокопроизводительного кода и привязку его к языкам науки о данных (Python, Julia и R).

Важно показать четкое видение - особенно для проектов с открытым исходным кодом: только общая цель может объединить людей. Вот почему мы решили наконец составить документ, в котором изложено наше видение того, как C ++ может сыграть важную роль в будущем науки о данных и почему. Три основных языка науки о данных сегодня - это Python, Julia и R.

В экосистеме научных вычислений Python есть множество интересных библиотек, которые недоступны на других языках, таких как Julia и R, и наоборот. Например, scikit-learn - это специальный пакет Python. Обходные пути обычно вызывают интерпретатор Python от Джулии, но это не особенно быстро или элегантно, а цена преобразования высока. Численные методы, включенные в эти пакеты, хорошо зарекомендовали себя и были тщательно протестированы благодаря большой базе пользователей. К сожалению, выбор одного из этих трех языков для эталонной реализации вместо языка с общим знаменателем запрещает принятие другими языками науки о данных. Дублирование работы в разных сообществах вредит устойчивости экосистемы и воспроизводимости научных результатов. Это верно не только для хорошо зарекомендовавших себя численных методов, но и для реализации стандартных протоколов, считывателей форматов файлов, общих структур данных для науки о данных.

Для этой задачи основными преимуществами C ++ являются скорость и зрелость, а также большое сообщество разработчиков. Хотя есть и другие многообещающие языки, C ++ долгое время был краеугольным камнем научных вычислений и никуда не денется, даже несмотря на то, что ему катастрофически не хватало библиотеки n-мерных массивов. Напротив, современный C ++ становится все лучше и лучше, язык и стандартная библиотека сейчас так же хороши, как никогда раньше.

  1. C ++ может быть очень быстрым. Вы обычно находите C ++ на вершине тестов. Например, в тестовой игре C ++ неизменно входит в тройку лучших с C и Rust. Однако такой язык, как Rust, показывает большую вариативность результатов, в то время как C и C ++ остаются стабильными. Вы можете быть уверены, что всегда располагаете средствами для создания максимально быстрого кода на C ++ - язык никогда не помешает вам добраться до него.
  2. В C ++ есть сильная система типов, которую можно использовать с большой пользой, и мощная, гибкая система шаблонов, которая позволяет компилятору генерировать чрезвычайно оптимизированную сборку.
  3. Универсальность означает возможность многократного использования: в идеале весь код должен быть написан только один раз. Вот почему универсальный язык должен упростить его использование с других языков. Три главных направления Data Science - это Python, R и Julia. У каждого из них есть отличные инструменты для общения с C ++: pybind11 для Python, RCpp для R и CxxWrap для Джулии. Когда вы пишете свой код на C ++, вы можете быть уверены, что сможете повторно использовать его с каждого из этих языков.

Как xtensor пытается решить эту проблему

С помощью xtensor мы стремимся разработать решение, которое будет работать для всей экосистемы Data Science. xtensor - это контейнер n-мерных массивов для C ++, например NumPy для Python. Мы внимательно следим за NumPy API, но на чистом C ++ и строго типизированном (есть даже шпаргалка, чтобы показать, насколько точно соответствуют API). Кроме того, мы предлагаем официальные привязки к Python, R и Julia. Это означает, что если вы однажды напишете свой числовой алгоритм на C ++ с помощью xtensor, вы сможете беспрепятственно использовать свои подпрограммы на всех крупных языках науки о данных. xtensor достаточно гибок, чтобы использовать динамические языки для управления памятью: если вы выделяете массив Julia с помощью xtensor-julia, используются собственный распределитель памяти Julia и сборщик мусора. xtensor также прозрачно обрабатывает любой макет памяти, такой как макет Julia's и R по основным столбцам и NumPy по основным строкам. В последние годы C ++ добился больших успехов в модернизации (с помощью C ++ 14 и 17), что сделало его невероятно производительным языком.

В оставшейся публикации блога мы рассмотрим реализацию алгоритма для карт высот с затенением лучей, который сначала стал проблемой для сообщества R, а затем был поднят, чтобы показать, как GraalVM может ускорить R, что было оспорено. пользователя Julia and Pythran. Цель этой статьи - показать, как можно раз и навсегда реализовать алгоритм на C ++ с помощью xtensor, а затем создать привязки к каждому из этих языков, сохраняя при этом максимальную скорость.

Соревнование

Мы портировали оригинальную R & Julia (от Тайлера Морган-Уолла и Саймона Дэниша) сначала на Python, а затем на xtensor (вы можете найти версию Python здесь).

Наша версия находится в https://github.com/wolfv/xtensor-showcase

Вы можете заметить, что, за исключением использования фигурных скобок здесь и там, длина перенесенного кода C ++ в основном такая же, как у исходной реализации Python или R. Пользователи NumPy также могут распознавать такие функции, как linspace. Это потому, что мы придерживаемся API NumPy для большинства высокоуровневых операций с массивами. Большая разница, конечно, заключается в предоставлении параметра шаблона <double>, который представляет собой строгую типизацию во время компиляции, необходимую для эффективных вычислений и генерации быстрого кода с помощью нашего механизма выражения шаблонов (отличное объяснение того, как это работает, можно найти здесь ).

Теперь мы можем создавать привязки для большой тройки: Python, R и Julia. Для каждого языка существует соответствующий пакет xtensor:

  • Xtensor-python: без проблем работает с массивами NumPy
  • Xtensor-r: переносить туда и обратно R векторов и матриц
  • Xtensor-julia: связать n-мерные массивы Джулии

Все эти три пакета работают одинаково. Они используют зависящий от языка пакет C ++ (pybind11, CxxWrap.jl и RCpp) для создания структур данных на основном языке. Используя эти пакеты, мы создаем новые xtypes в C ++: xt :: pyarray ‹T› для массива Python с поддержкой данных NumPy, xt :: rarray ‹T› для версии R и xt :: jlarray ‹T› для Джулии. Кроме того, пакеты также содержат версии со статическим числом измерений (эквивалент xt :: xtensor ‹T, N›, где N - целое число, указывающее размерность. Это позволяет выполнить ряд оптимизаций для компилятора C ++ и в шаблоне механизм выражения.

Давайте посмотрим, например, на привязки Python (привязки Julia и R так же просты):

В качестве входных данных функции мы берем xt :: pyarray ‹float›. Этот тип регистрируется с помощью pybind11, и тип автоматически преобразуется из массива NumPy - без копирования содержимого буфера на любом этапе! Поскольку функция rayhade_impl принимает в качестве входных данных аргумент шаблона E, мы можем вызывать его с любым классом, который следует интерфейсу xexpression. Конечно, это делает pyarray (а также rarray и jlarray).

Контрольные точки

Ниже приведены тесты. Как видите, тесты показывают, как скомпилированные языки, такие как C ++ и Julia, могут генерировать чрезвычайно эффективный код, и что привязки xtensor вообще не влияют на скорость! (Кстати, результат C ++ немного медленнее, чем результат Джулии, потому что не использовался специальный пакет для тестирования, и мы рассчитали только одну итерацию на ноутбуке - процессору обычно требуется несколько итераций для увеличения скорости).

                 Benchmarks
        ╔══════════════════╦════════════╗
        ║     Language     ║  Time (s)  ║ 
        ╠══════════════════╬════════════╣
        ║ C++              ║   .0164812 ║ 
        ║ Python / xtensor ║   .0220982 ║ 
        ║ Python / NumPy   ║ 14.224207  ║
        ║ Julia / xtensor  ║   .014221  ║
        ║ Julia            ║   .014501  ║
        ║ R / xtensor      ║   .01328   ║
        ║ R                ║  9.905     ║
        ╚══════════════════╩════════════╝

Здесь нужно сделать почетное упоминание: Броди Гаслам впечатляюще показал, как сделать R-код работоспособным с помощью векторизации (тот же принцип можно использовать в NumPy, а также в xtensor). Его код работал почти так же быстро, как R с xtensor, и работал с частотой 0,058 с.

С чего начать

Код этого примера загружен на https://github.com/wolfv/xtensor-showcase. Мы создали много документации для xtensor, а также шаблонов проектов для печенья, чтобы помочь людям начать работу с плагинами для Python и Julia. Вы также можете ознакомиться с различными репозиториями xtensor на GitHub в рамках проекта QuantStack. И мы часто бываем онлайн в чате Gitter или Twitter.

Куда мы хотим идти дальше

Впереди у xtensor много интересных и сложных задач:

  • Больше о NumPy API: Мы уже многое рассмотрели, но все еще есть некоторые недостающие части! Мы даже скомпилировали проект GitHub с некоторыми легко устраняемыми недостающими частями NumPy API. Если, например, вы хотите снова попробовать C ++, мы будем рады помочь новичкам реализовать некоторые недостающие функции!
  • Ускорение некоторых функций: все еще есть узкие места в функциях, которые мы реализовали до сих пор. Пора избавиться от них всех.
  • Поддержка GPU - очевидная вещь. Он есть у всех, и мы должны его получить, надеюсь, в следующем году. Это может значительно ускорить операции с большими данными.
  • Больше взаимодействия: мы хотим иметь глубокую поддержку, например, Apache Arrow (работа в процессе) и PyTorch. Мы также были бы счастливы с большим количеством языковых привязок. Например, Matlab / Octave или Java!
  • Скомпилируйте NumPy в xtensor:. Это большая проблема. С помощью Pythran мы могли скомпилировать код NumPy на C ++ (в настоящее время Pythran имеет собственную реализацию NumPy на C ++). Мы уже тесно сотрудничаем с автором Pythran, и Pythran использует ту же библиотеку SIMD, что и xtensor (называемая xsimd). Большая идея здесь заключалась в том, чтобы можно было написать числовой код один раз, используя NumPy для быстрого прототипирования, а затем автоматически скомпилировать его на C ++. Оказавшись в C ++, мы могли легко сгенерировать код привязки к Python (для экспорта в отдельную библиотеку), а также к R и Julia! Это может сделать потенциально чрезвычайно приятным написание межъязыковых библиотек на основе высокоуровневого описания алгоритма.

Вы можете подписаться на автора в Twitter или посетить чат Gitter по адресу https://gitter.im/QuantStack/Lobby, чтобы обсудить xtensor и связанные с ним проекты!

Ссылки: