сокеты — RPC из кода C ++ в код Common Lisp

У меня есть две кодовые базы: одна написана на C ++, а другая на Common Lisp. В базе кода Lisp реализована определенная функциональность, к которой я хотел бы получить доступ из своего кода C ++. Я искал Интерфейсы внешних функций для вызова функций Lisp из C ++, но, похоже, не смог их найти (в основном я нашел FFI для другого направления). Поэтому я решил внедрить некоторую форму RPC, которая соответствует моим требованиям, а именно:

  • оба кода будут выполняться на одном компьютере, поэтому расширяемость для вызовов удаленных компьютеров не важна.

  • входные данные из C ++ будут представлять собой список в стиле Lisp, который функция из кода Lisp будет принимать в качестве входных данных.

  • этот вызов будет выполняться 1000 раз за выполнение кода, поэтому производительность на удаленный вызов имеет решающее значение.

До сих пор я узнал из различных ресурсов в сети, что возможные решения:

  • Розетки — установить экземпляр кода на Лиспе, который будет прослушивать вызовы функций из кода C ++, запускать функцию для заданного ввода и возвращать результат в код C ++.

  • XML-RPC — настроить сервер XML-RPC на стороне Lisp (это будет легко, поскольку я использую Allegro Common Lisp, который предоставляет API, поддерживающий XML-RPC), а затем использую одну из множества библиотек XML-RPC для C ++, чтобы сделать клиента боковой звонок.

Плюсы и минусы, которые я вижу с этими подходами, заключаются в следующем:

  • Сокеты — это низкоуровневая конструкция, поэтому мне кажется, что мне нужно было бы выполнять большую часть управления соединениями, считывать и анализировать данные в сокетах и ​​т. Д. Самостоятельно.

  • XML-RPC, кажется, намного лучше отвечает моим потребностям, но я читал, что это всегда использует HTTP, и нет возможности использовать доменные сокеты UNIX. Таким образом, кажется, что XML-RPC может быть излишним для того, что я имею в виду.

У кого-нибудь есть опыт в достижении подобной интеграции кодов? Существуют ли значительные различия в производительности между сокетами и XML-RPC для локального RPC? Любой совет, какой подход мог бы быть лучше, был бы чрезвычайно полезен. Кроме того, предложения по другой технике, чтобы сделать это, также будут оценены.

РЕДАКТИРОВАТЬ: Вот еще несколько деталей об общей функциональности. В коде Лиспа есть функция f (которая достаточно сложна, чтобы сделать повторную реализацию в C ++ чрезмерно дорогой). Он принимает в качестве входных данных два списка L1 и L2. Как я это представляю, так это:

  • L1 и L2 построены на C ++ и отправлены на сторону Lisp и ждут результатов,
  • f вызывается на стороне Lisp на входах L1 и L2 и возвращает результаты обратно на сторону C ++,
  • сторона C ++ принимает результаты и продолжает вычисления.

Размеры L1 и L2 обычно невелики:

  • L1 — это список, содержащий, как правило, 100 элементов, причем каждый элемент представляет собой список максимум из 3-4 атомов.

  • L2 также список, содержащий < 10 элементов, каждый элемент из списка не более 3-4 атомов.

Таким образом, общий объем данных на RPC, вероятно, составляет строку 100 с / 1000 с байтов. Этот вызов выполняется в начале каждого цикла while в моем коде на C ++, поэтому сложно дать конкретные цифры о количестве вызовов в секунду. Но из моих экспериментов я могу сказать, что обычно это делается 10-100 раз в секунду. е это не численное вычисление: это символическое. Если вы знакомы с ИИ, по сути, он выполняет символическое объединение в логике первого порядка. Так что, это без побочных эффектов.

4

Решение

Есть много других способов установить связь между двумя процессами. Вы могли бы прочитать межпроцессного взаимодействия вики страницы.

Одним из параметров является асинхронный или синхронный символ. Ваша удаленная обработка удаленный вызов процедур (каждый запрос от клиента имеет ровно один ответ от сервера) или это асинхронный передача сообщений (обе стороны отправляют сообщения, но нет понятия запроса и ответа; каждая сторона обрабатывает входящие сообщения как события).

Другим параметром является задержка и полоса пропускания, то есть объем данных, которыми обмениваются (за сообщение и, например, за секунду).

Пропускная способность имеет значение даже на одной машине. Конечно, каналы или сокеты Unix дают вам очень большую пропускную способность, например, 100 мегабайт в секунду. Но есть сценарии, в которых этого может быть недостаточно. В этом случае данные обычно копируются (часто дважды) из памяти в память (например, из одного адресного пространства процесса в другое).

Но вы можете рассмотреть, например, CORBA (см. например CLORB на боковой стороне, и этот учебник на OmniORB) или RPC / XDR, или XML-RPC (с S-XML-RPC на боковой стороне) или JSON-RPC так далее…

Если у вас мало данных и много пропускной способности (или много запросов или сообщений в секунду), я бы предложил использовать текстовый протокол (возможно, сериализация с JSON или YAML или XML), потому что это проще, чем двоичный протокол (BSON, Protobuf, так далее…)

Уровень сокетов (который может использовать UNIX (7) AF_UNIX розетки, анонимные или именные трубы (7)-с или TCP (7) т. е. TCP / IP, который имеет преимущество в том, что дает вам возможность распределять вычисления по двум машинам, обменивающимся данными по сети), вероятно, является самым простым, поскольку у вас есть обе стороны (C ++ и Lisp) — мультиплексный системный вызов Опрос (2). Вам нужно буферизовать сообщения с обеих сторон.

Может ты хочешь MPICL-MPI на боковой стороне).

Мы не сможем вам больше помочь, если вы не объясните действительно хорошо и гораздо подробнее в деталях, что такое «функциональность» для совместного использования из C ++ в Lisp (что он делает, сколько удаленных вызовов в секунду, какой объем и вид данные, какое время вычислений и т. д. и т. д. ….). Является ли удаленный вызов функции идемпотент или же nullipotent, это имеет побочные эффекты? Это протокол без сохранения состояния

Фактические типы данных, задействованные в удаленном вызове процедуры, имеют большое значение: сериализация сложного [математического] циклического графа с общими узлами обходится гораздо дороже, чем простая читаемая человеком строка ….

Учитывая ваши последние данные, я бы предложил использовать JSON … Это вполне подходит для передачи абстрактное синтаксическое дерево как данные. В качестве альтернативы передайте только s-выражения (у вас может остаться небольшая проблема в C ++ для их анализа, что действительно легко сделать, если вы указали и задокументировали свои соглашения; если ваши листовые или символические имена содержат произвольные символы, вам просто нужно определить конвенция для их кодирования.).

1

Другие решения

Если вы посмотрите на некоторые реализации Common Lisp, их FFI позволяют вызывать Lisp со стороны C. Это не удаленно, а локально. Иногда имеет смысл включать Lisp напрямую, а не вызывать его удаленно.

Коммерческие Лиспы, такие как LispWorks или Allegro CL, также могут предоставлять общие библиотеки, которые вы можете использовать из кода своего приложения.

Например определить иностранные вызываемым позволяет вызывать функцию LispWorks.

Франц ACL может сделать это: http://www.franz.com/support/documentation/9.0/doc/foreign-functions.htm#lisp-from-c-1

Также что-то вроде ECL должно использоваться со стороны C.

5

Я недавно начал работать над проектом, который требует аналогичных функций. Вот некоторые вещи, которые я исследовал до сих пор с некоторыми комментариями:

  • cl-mpi в принципе разрешает (хотя и очень низкоуровневое) прямое межпроцессное взаимодействие, но кодирование данных — это кошмар! У вас очень неудобный дизайн на стороне C / C ++ (просто очень-очень ограниченный + нет способа отправить массивы переменной длины). А с другой стороны, библиотека Lisp устарела и, похоже, находится на самой ранней стадии своего развития.

  • Apache Trift, который больше похож на язык, чем на программу. Медленно, память боров. Protobuf, BSON одинаковы. Protobuf может быть самым эффективным в этой группе, но вам нужно создать собственное коммуникационное решение, это всего лишь протокол кодирования / декодирования.

  • XML, JSON, S-выражения. S-выражения выигрывают в этой категории, потому что они более выразительны, и одна сторона уже имеет очень эффективный синтаксический анализатор. Увы, это даже хуже, чем Trift / Protobuf с точки зрения скорости / памяти.

  • CFFI. Вздох … Управление указателями с обеих сторон будет кошмаром. Это возможно в теории, но должно быть очень сложно на практике. Это также неизбежно скажется на производительности сборщика мусора Lisp, потому что вам придется помешать ему.

  • Наконец я перешел на ECL. Все идет нормально. Я исследую mmapEd файлы как средство обмена данными. Вывод, который я сделал для себя, это будет путь. По крайней мере, сейчас я не могу придумать ничего лучшего.

2