Достижение высокой производительности в R

R — прекрасный язык программирования, и у него много сильных сторон.

К сожалению, производительность не является одним из них. Хотя R удобочитаем и универсален, он не известен своей производительностью. Будучи динамически типизированным и интерпретируемым языком, R подвержен всем недостаткам, присущим языкам, попадающим в эти категории. Производительность языка программирования определяется многими факторами. Но два факта заключаются в том, что каждый раз, когда мы запускаем R-скрипт, мессенджер под названием «Интерпретатор» передает и переводит код. И каждый раз, когда мы запускаем сценарий R, типа каждой переменной, указанной в процессе перевода, называемой «Время выполнения», достаточно, чтобы сделать R медленным языком программирования.

Хорошая новость заключается в том, что существует множество противоядий для преодоления трудности медлительности. Мы можем добиться значительного увеличения производительности с помощью правильного набора инструментов. Самый законный способ — обратиться за помощью к низкоуровневому языку программирования. Цель этой статьи — помочь вам начать работу с переписыванием узких мест вашего кода на C++.

Что такое C++ и почему это важно?

C++ — один из самых мощных языков программирования в мире, и он везде. Тысячи устройств, которые мы используем каждый день, работают на C++. Это преемник другого известного языка программирования под названием C.

Чтобы дать немного истории, C был создан в 1972 году Деннисом Ритчи. В то время C произвел революцию в компьютерном мире, но со временем его пользователи поняли, что ему не хватает некоторых из лучших свойств, которыми обладали другие языки. Одним из них было объектно-ориентированное программирование. В результате этого осознания Бьерн Страуструп, датский ученый-компьютерщик, создал C++ в 1985 году. C++ расширил возможности C и добавил объектно-ориентированную парадигму. На самом деле C++ просто означает «C с классами».

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

Старый школьный способ расширить R

Традиционно R предоставляет несколько встроенных инструментов для взаимодействия с двумя другими языками, C и Fortran. Поскольку Fortran устарел, C был основным языком для R. Для этой цели стандартный R предоставляет две функции для взаимодействия R с C: .C() и .Call(). Однако этот традиционный способ настолько устарел и болезненный. Итак, мы рассмотрим гораздо более современный и удобный способ и не будем упоминать эти две функции в нашей статье.

Rcpp

Кусочек рая, который помогает нам украсить наш проект R с помощью C++, — это пакет под названием Rcpp. Rcpp разработан командой под руководством Дирка Эддельбюттеля, еще одного коллеги-эконометриста из Канады. Rcpp обеспечивает интерфейс между R и C++, и этот пакет настолько мощный, что Springer опубликовал целую книгу «Бесшовная интеграция R и C++ с Rcpp».

Вы можете начать, запустив код ниже:

library(Rcpp)

Превращение C++ в R в скрипте

Самый простой и быстрый способ повысить производительность R-скрипта — изменить проблемные части скрипта и переписать их на C++. Функция Rcpp::cppFunction() — это то, что вам нужно. Это аккуратно и удобно. cppFunction() берет функцию C++ и преобразует ее в функцию R. Все, что вам нужно сделать, это передать вашу функцию C++ в виде строки, и она сделает все остальное.

Rcpp::cppFunction()

Для простоты напишем две дополнительные функции: одну на R, а другую на C++. Они иллюстрируют использование cppFunction().

Ниже вы можете найти дополнительную функцию, написанную на R:

addition_r <- function(a , b) {
  a + b
}
addition_r(5 , 10)
## [1] 15

Теперь мы пишем ту же функцию на C++ и используем ее так, как если бы мы написали ее на R.

addition_cpp <- cppFunction('int additioncpp(int a, int b) {
  int sum = a + b ;
  return sum;
}')
addition_cpp(5 , 10)
## [1] 15

Это до смешного просто и может сэкономить вам огромное количество времени.

Хотя заманчиво использовать быстрые и грязные методы, подобные приведенным выше, для сценариев из пары сотен строк, это не самый желательный способ создания всего проекта. Большинство проектов R не являются сценарием, а состоят из десятков сценариев. В таком случае мы можем захотеть импортировать весь файл C++ в наш сеанс R. Это тема следующей темы.

Использование отдельных файлов C++ в проектах R

Чтобы использовать части отдельного файла C++, мы пользуемся функцией Rcpp::sourceCpp().

Rcpp::sourceCpp()

И хитрость в том, что для каждой функции, которую вы хотите импортировать из файла C++, вы должны добавить этот индикатор: // [[Rcpp::export]].

Ниже вы можете увидеть пример того, как должен выглядеть готовый к импорту файл C++. Он сохраняется как arithmetic.cpp:

Теперь приведенный ниже код импортирует предварительно указанное содержимое файла arithmetic.cpp.

sourceCpp("C:/Users/ugurc/Desktop/Medium Blog/High Performance in R/arithmetic.cpp")
subtractioncpp(a = 10 , b = 5)
## [1] 5
multiplicationcpp(a = 10 , b = 5)
## [1] 50

Заключение

Каждый день по всему миру разработчики R создают тысячи замечательных проектов, тысячи приложений используют R-код, и миллионы долларов в отраслях используют R. Всякий раз, когда этого прекрасного языка не хватает, мы внедряем различные решения.

Применение низкоуровневого языка является наиболее четким решением этих недостатков. Теперь, используя информацию из этой статьи, вы можете воссоздать свое приложение R, переписав его узкие места на C++.