Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » Developing U++ » U++ Developers corner » Little experiment compiling to function pointer calls...
Little experiment compiling to function pointer calls... [message #34342] Wed, 16 November 2011 19:54 Go to previous message
mirek is currently offline  mirek
Messages: 14265
Registered: November 2005
Ultimate Member
Thinking about TCC and runtime compilation frameworks (also web templates) I have got an idea how represent the code structure in the tree of virtual objects (basically, via function pointers).

I have put together a litte experimental snippet:

#include <Core/Core.h>

using namespace Upp;

struct Oper {
	virtual double Execute() = 0;
	virtual ~Oper() {}
};

struct BinOper : Oper {
	One<Oper> a;
	One<Oper> b;
};

struct Add : BinOper {
	virtual double Execute() { return a->Execute() + b->Execute(); }
};

struct Sub : BinOper {
	virtual double Execute() { return a->Execute() - b->Execute(); }
};

struct Mul : BinOper {
	virtual double Execute() { return a->Execute() * b->Execute(); }
};

struct Div : BinOper {
	virtual double Execute() { return a->Execute() / b->Execute(); }
};

struct Const : Oper {
	double value;
	virtual double Execute() { return value; }
};

struct Var : Oper {
	double *var;
	virtual double Execute() { return *var; }
};

struct Compiler {
	VectorMap<String, double *> var;
	One<Oper> Term(CParser& p);
	One<Oper> Exp(CParser& p);
	One<Oper> Factor(CParser& p);
};

One<Oper> Compiler::Term(CParser& p)
{
	One<Oper> result;
	if(p.IsId()) {
		double *v = var.Get(p.ReadId(), NULL);
		if(!v)
			p.ThrowError("unknown variable");
		result.Create<Var>().var = v;
	}
	else
	if(p.Char('(')) {
		result = Exp(p);
		p.PassChar(')');
	}
	else
		result.Create<Const>().value = p.ReadDouble();
	return result;
}

One<Oper> Compiler::Factor(CParser& p)
{
	One<Oper> result = Term(p);
	for(;;)
		if(p.Char('*')) {
			One<Oper> rr;
			Mul& m = rr.Create<Mul>();
			m.a = result;
			m.b = Term(p);
			result = rr;
		}
		else
		if(p.Char('/')) {
			One<Oper> rr;
			Div& m = rr.Create<Div>();
			m.a = result;
			m.b = Term(p);
			result = rr;
		}
		else
			return result;
}

One<Oper> Compiler::Exp(CParser& p)
{
	One<Oper> result = Factor(p);
	for(;;)
		if(p.Char('+')) {
			One<Oper> rr;
			Add& m = rr.Create<Add>();
			m.a = result;
			m.b = Factor(p);
			result = rr;
		}
		else
		if(p.Char('-')) {
			One<Oper> rr;
			Sub& m = rr.Create<Sub>();
			m.a = result;
			m.b = Factor(p);
			result = rr;
		}
		else
			return result;
}

VectorMap<String, double> var;

double Exp(CParser& p);

double Term(CParser& p)
{
	if(p.Id("abs")) {
		p.PassChar('(');
		double x = Exp(p);
		p.PassChar(')');
		return fabs(x);
	}
	if(p.IsId())
		return var.Get(p.ReadId(), 0);
	if(p.Char('(')) {
		double x = Exp(p);
		p.PassChar(')');
		return x;
	}
	return p.ReadDouble();
}

double Mul(CParser& p)
{
	double x = Term(p);
	for(;;)
		if(p.Char('*'))
			x = x * Term(p);
		else
		if(p.Char('/')) {
			double y = Term(p);
			if(y == 0)
				p.ThrowError("Divide by zero");
			x = x / y;
		}
		else
			return x;
}

double Exp(CParser& p)
{
	double x = Mul(p);
	for(;;)
		if(p.Char('+'))
			x = x + Mul(p);
		else
		if(p.Char('-'))
			x = x - Mul(p);
		else
			return x;
}


CONSOLE_APP_MAIN
{
	double x, y;
	
	Compiler c;
	c.var.Add("x", &x);
	c.var.Add("y", &y);
	
	CParser p("1 / (1 - x * y + x - y)");
	One<Oper> fn = c.Exp(p);
	
	x = 5;
	y = 10;
	
	RDUMP(1 / (1 - x * y + x - y));
	RDUMP(fn->Execute());

	{
		RTIMING("Interpreted");
		double sum = 0;
		for(x = 0; x < 1; x += 0.001)
			for(y = 0; y < 1; y += 0.001) {
				var.GetAdd("x") = x;
				var.GetAdd("y") = y;
				sum += Exp(CParser("1 / (1 - x * y + x - y)"));
			}
		RDUMP(sum);
	}
	{
		RTIMING("Compiled");
		double sum = 0;
		for(x = 0; x < 1; x += 0.001)
			for(y = 0; y < 1; y += 0.001)
				sum += fn->Execute();
		RDUMP(sum);
	}
	{
		RTIMING("Direct");
		double sum = 0;
		for(x = 0; x < 1; x += 0.001)
			for(y = 0; y < 1; y += 0.001)
				sum += 1 / (1 - x * y + x - y);
		RDUMP(sum);
	}
}


This compares three methods to evaluate expression - interpreted, compiled to the virtual tree and 'native' ("compile time compiled").

Results are interesting:

TIMING Direct         :  8.00 ms -  8.00 ms ( 8.00 ms / 1 ), min:  8.00 ms, max:  8.00 ms, nesting: 1 - 1
TIMING Compiled       : 29.00 ms - 29.00 ms (29.00 ms / 1 ), min: 29.00 ms, max: 29.00 ms, nesting: 1 - 1
TIMING Interpreted    : 551.00 ms - 551.00 ms (551.00 ms / 1 ), min: 551.00 ms, max: 551.00 ms, nesting: 1 - 1


Means 'compiled' expression evaluation is only 3 times slower than native code, while being 20 times faster than interpreted results.

I believe this approach would scale up to complete compiler of e.g. C. The advantage over TCC is that such code is 100% portable, no need to emit assembler opcodes...

[/code]
 
Read Message
Read Message
Read Message
Read Message
Read Message
Previous Topic: upp GTK compatibility for Ubuntu broken?
Next Topic: Trouble with SizePos...
Goto Forum:
  


Current Time: Fri Jul 04 07:40:36 CEST 2025

Total time taken to generate the page: 0.04508 seconds