Юрий Ефимочев, Компилируемые в реальном времени dsl для...

42
Компилируемые в реальном времени DSL для С++ Юрий Ефимочев

Upload: sergey-platonov

Post on 16-Apr-2017

660 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Компилируемые в реальном времени DSL для С++

Юрий Ефимочев

Page 2: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

О себе

Архитектор в LogicNowСпециализация: высоконагруженные отказоустойчивые системы на C++

Бэкап-решение

Page 3: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Что такое DSL?

Domain-specific language - это язык программирования с ограниченными возможностями, ориентированный на конкретную предметную область.

Page 4: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Плюсы DSL

➢ управление сложностью➢ скорость разработки➢ комуникация с экспертами➢ альтернативные парадигмы➢ динамическое выполнение

Page 5: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Минусы DSL

➢ порог вхождения

➢ эволюция в язык общего назначения

➢ разработка и поддержка

Page 6: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Классификация DSL

➢ Внешние

➢ Внутренние

Page 7: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Внутренний DSLGiven(

Node("a")

(Node("b")

(Node("c"))

(Node("d")))

(Node("e")

(Node("f"))

(Node("g"))));

When().Filter("%f%");

Expect(

Node("a")

(Node("e")

(Node("f"))));

a

b

c d

e

f g

a

e

f

Page 8: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Классификация DSL

➢ Интерпретируемые

➢ Компилируемые

Page 9: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Архитектура DSL

ПрограмманаDSL

Семантическаямодель

Целевойкод

Опционально

Page 10: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Архитектура бэкап-решения

Cloudstorage

Support

Integration

Customers

Development

Sales

Finances

BackupclientBackupclientBackupclientBackupclient

~ 100 000 клиентов

~ 500 параметров у каждого

Page 11: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Примеры запросов

Count(true)

Sum(UsedStorage)

Count(LastSession.Timestamp < 2.days().ago())

Count(LastSession.Status == SessionStatus::Failed)

Average(LastSession.Duration)

Page 12: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Архитектура решения

Expression AST Result

Data

Page 13: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Синтаксический анализ

Flex & Bison

Antlr

boost::Spirit

Page 14: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Bison: грамматика

%start Start;

AtomicExpression:IntegerNumber { $$.Value = m_builder.CreateInteger($1.Name); } |Identifier { $$.Value = m_builder.CreateVariable($1.Name) };

AddSubExpression:AtomicExpression |AddSubExpression Plus AddSubExpression { m_builder.CreateAddNode(...); } |AddSubExpression Minus AddSubExpression { m_builder.CreateSubNode(...); };

Start:AddSubExpression { result.AstTree.reset($1.Value); };

Page 15: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

AST

Count(time > 2.days().ago())Count

time ago

days

2

>

x.ago() = now - x

Page 16: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

AST

Count(time > 2.days().ago())Count

time -

days

2

>

x.ago() = now - x

x.days() = x * 86400 now

Page 17: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

AST

Count(time > 2.days().ago())Count

time -

*

86400

>

x.ago() = now - x

x.days() = x * 86400 now

2

Page 18: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация объектов AST

1 class IntegerNode : public IAstNode 2 { 3 public: 4 IntegerNode(int const value) : 5 m_value(value) 6 { 7 } 8 9 private:10 virtual Variant Evaluate(IDataProvider& /*dataProvider*/) const11 {12 return Variant(m_value);13 }1415 int const m_value;16 };

Page 19: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация объектов AST

1 class VariableNode : public IAstNode 2 { 3 public: 4 VariableNode(std::string const& name) : 5 m_name(name) 6 { 7 } 8 9 private:10 virtual Variant Evaluate(IDataProvider& dataProvider) const11 {12 return Variant(dataProvider.GetVariableValue(m_name));13 }1415 private:16 std::string const m_name;17 };

Page 20: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация объектов AST

1 class AddNode : public IAstNode 2 { 3 public: 4 AddNode(IAstNodePtr left, IAstNodePtr right) : 5 m_left(std::move(left)), 6 m_right(std::move(right)) 7 { 8 } 910 private:11 virtual Variant Evaluate(IDataProvider& dataProvider) const12 {13 return m_left->Compile(dataProvider) + m_right->Compile(dataProvider);14 }1516 private:17 IAstNodePtr m_left;18 IAstNodePtr m_right;19 };

Page 21: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Итоги: первая версия

Плюсы:

➢ простота использования➢ скорость разработки➢ простота реализации

Минусы:

➢ производительность

Page 22: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

AST

Count(time > 2.days().ago())Count

time -

*

86400

>

now

2

Page 23: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Архитектура LLVM

Page 24: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Что такое LLVM IR?LLVM IR(Intermediate representation) - апаратно независимый низкоуровневый язык прогрммирования.

Page 25: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

LLVM IR

i1, … ,i32, … , i1942652 Целочисленные типы

half, float, double Типы с плавающей точкой

[40 x i32] Массивы

<4 x i32> Векторы (SIMD)

{ i32, i32, i32 } Структуры

i32*, [4 x i32]* Указатили

➢ функции

➢ типы данных(статическая строгая типизация)

➢ платформонезависимый

➢ SSA(static single assignment) нотация

Page 26: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

LLVM IR 1 bool MoveNext(); 2 int GetValue(); 3 4 int main() 5 { 6 int sum = 0; 7 8 while (MoveNext()) 9 {10 sum += GetValue();11 }1213 return sum;14 }

1 declare i1 @move_next() 2 declare i64 @get_value() 3 4 define i64 @main() 5 { 6 entry: 7 br label %loop_condition 8 9 loop_condition: 10 %1 = phi i64 [ 0, %entry ], [ %4, %loop_body ] 11 %2 = call i1 @move_next() 12 br i1 %2, label %loop_body, label %loop_exit 13 14 loop_body: 15 %3 = call i64 @get_value() 16 %4 = add i64 %3, %1 17 br label %loop_condition 18 19 loop_exit: 20 ret i64 %1 21 }

Page 27: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Архитектура решения

Expression AST LLVM IR AST

Native code

Data descriptor

Data provider

Result

Data

Page 28: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

1 class Expression 2 { 3 public: 4 Expression(std::string const& expression); 5 6 std::int64_t Evaluate(IDataProvider& dataProvider); 7 8 private: 9 typedef std::int64_t(*CompiledExpression)();1011 LLVMContext m_llvmContext;12 ExecutionEnginePtr m_executionEngine;13 CompiledExpression m_expression;14 ExecutionContext m_executionContext;15 };

Реализация

Page 29: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

1 std::int64_t Expression::Evaluate(IDataProvider& dataProvider) 2 { 3 m_executionContext.Reset(dataProvider); 4 5 std::int64_t const result = m_expression(); 6 7 m_executionContext.ThrowIfError(); 8 9 return result;10 }

Реализация

Page 30: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

1 Expression::Expression(std::string const& expression) 2 { 3 IAstNodePtr astTree = BuildAst(expression); 4 5 IRBuilder builder(m_llvmContext); 6 Module* module = new Module("module", m_llvmContext); 7 8 m_executionEngine.reset(ExecutionEngine::createJIT(module)); 910 FunctionType* mainType = FunctionType::get(builder.getInt64Ty(), Arguments(), false);11 Function* main = Function::Create(mainType, Function::ExternalLinkage, "main", module);12 BasicBlock* mainEntry = BasicBlock::Create(m_llvmContext, "entry", main);1314 builder.SetInsertPoint(mainEntry);1516 CompilationContext compilationContext(builder, *m_executionEngine, m_executionContext, module);17 Value* returnValue = astTree->Compile(compilationContext);18 builder.CreateRet(returnValue);1920 m_expression = reinterpret_cast<CompiledExpression>(m_executionEngine->getPointerToFunction(main));21 }

Реализация

Page 31: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация: константы 1 class IntegerNode : public IAstNode 2 { 3 public: 4 IntegerNode(int const value) : 5 m_value(value) 6 { 7 } 8 9 private:10 virtual Value* Compile(IDataProvider& context) const11 {12 return context.GetBuilder().getInt64(m_value);13 }1415 int const m_value;16 };

Page 32: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация: сложение 1 class AddNode : public IAstNode 2 { 3 public: 4 AddNode(IAstNodePtr left, IAstNodePtr right) : 5 m_left(std::move(left)), 6 m_right(std::move(right)) 7 { 8 } 910 private:11 virtual Value* Compile(ICompilationContext& context) const12 {13 Value* left = m_left->Compile(context);14 Value* right = m_right->Compile(context);15 return context.GetBuilder().CreateAdd(left, right);16 }1718 private:19 IAstNodePtr m_left;20 IAstNodePtr m_right;21 };

Page 33: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация: переменные 1 class VariableNode : public IAstNode 2 { 3 public: 4 VariableNode(std::string const& name) : 5 m_name(name) 6 { 7 } 8 9 private: 10 virtual Value* Compile(ICompilationContext& context) const 11 { 12 return context.GetHelper().GetVariable(m_name); 13 } 14 15 private: 16 std::string const m_name; 17 };

Page 34: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация: переменные 1 extern "C" std::int64_t GetVariableValue(std::int64_t const context, char const* const variableName) 2 { 3 ExecutionContext& executionContext = *reinterpret_cast<ExecutionContext*>(context); 4 std::int64_t const result = executionContext.GetVariableValue(variableName); 5 return result; 6 } 7 8 Value* LlvmHelper::GetVariable(std::string const& name) 9 {10 Value*& variable = m_variables[name];1112 if (variable == nullptr)13 {14 Function* llvmFunction = Export("get_value", &GetVariableValue);15 variable = Call(llvmFunction, &m_executionContext, name);16 }1718 return variable;19 }

Page 35: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация: переменные 1 template<typename ReturnType, typename... ArgumentTypes> 2 Function* LlvmHelper::Export(std::string const& name, ReturnType (*nativeFunction)(ArgumentTypes...)) 3 { 4 std::vector<Type*> llvmArgumentTypes = { (GetLlvmType<ArgumentTypes>())... }; 5 Type* llvmReturnType = GetLlvmType<ReturnType>(); 6 7 Function* function = Function::Create(functionType, Function::ExternalLinkage, name, m_module); 8 function->setCallingConv(llvm::CallingConv::C); 910 m_executionEngine.addGlobalMapping(function, (void*)nativeFunction);1112 return function;13 }1415 template<typename... ArgumentTypes>16 Value* LlvmHelper::Call(Function* llvmFunction, Arguments... arguments)17 {18 std::vector<Value*> llvmArguments = { (ToLlvmValue(arguments))... };19 return m_builder.CreateCall(llvmFunction, llvmArguments);20 }

Page 36: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Реализация: отрицание

1 Value* NotNode::Compile(ICompilationContext& context) const 2 { 3 CompiledNode child = m_value->Compile(context); 4 5 IRBuilder& builder = context.GetBuilder(); 6 7 Value* one = builder.getInt64(1); 8 Value* zero = builder.getInt64(0); 9 Value* notEqualZero = builder.CreateICmpNE(zero, child.Value); 10 notEqualZero = builder.CreateZExt(notEqualZero, builder.getInt64Ty()); 11 12 Value* value = builder.CreateXor(one, nonEqualZero); 13 value = builder.CreateZExt(value, builder.getInt64Ty()); 14 15 return value; 16 }

not x == 1 xor (x != 0)

Page 37: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

ПроизводительностьCount(x < 50 && x > 22 || x == 77), size = 1000000

Interpreted 1734 мс

LLVM 31 мс

Native 21 мс

Page 38: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Кодогенерация

LLVM

Google V8

Lua

Page 39: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

ПроизводительностьCount(x < 50 && x > 22 || x == 77), size = 1000000

Interpreted 1734 мс

V8 402 мс

Lua 309 мс

LLVM 31 мс

Native 21 мс

Page 40: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Итоги: вторая версия

Плюсы:

➢ простота использования➢ скорость разработки➢ производительность

Page 41: Юрий Ефимочев, Компилируемые в реальном времени DSL для С++

Итоги: вторая версия

Минусы:

➢ размер бинарного файла (~ 15 Mb)➢ сложность реализации➢ нативный код