[GiNaC-devel] Fix the compliation error *for real* ... and restore performance

Alexei Sheplyakov varg at metalica.kh.ua
Sat Aug 8 10:43:12 CEST 2009


Hello,

> Subject: [PATCH] Fix compilation error on 64-bit architectures for real.
> 
> According to [expr.reinterpret.cast] it's not ok to convert a pointer
> into an integer of a different size.
[skipped the patch]

Although reading user-defined classes works again it's a bit slower than
it used to be. The fix is below (should be applied after the patch
mentioned above). Please note: this patch is for 1.5 branch only, I'll
post a different patch for the master branch.

From: Alexei Sheplyakov <varg at metalica.kh.ua>
Subject: [PATCH][GiNaC 1.5] Make the parser as fast as it used to be (before 1.5.2).

Commit 8bf0597dde55e4c94a2ff39f1d8130902e3d7a9b (titled as 'Fixed the parser
such that it can read in user defined classes again.') made the parser a bit
slower, especially if the input contains many terms of user-defined type.
The reason for that is quite simple: we throw and catch an exception every
time we construct an object of user-defined type:

       // dirty hack to distinguish between serial numbers of functions and real
       // pointers.
       GiNaC::function* f = NULL;
       try {
               unsigned serial = (unsigned)(unsigned long)(void *)(reader->second);
               f = new GiNaC::function(serial, args);
       }
       catch ( std::runtime_error ) {
               if ( f ) delete f;
               ex ret = reader->second(args);
               return ret;
       }

Fortunately functions are aligned and we can use much more efficient
technique to distinguish between serial and pointers to functions.
---
 ginac/parser/default_reader.tpl |   55 ++++++++++++++++++++++++++++----------
 ginac/parser/parser.cpp         |   47 ++++++++++++++++++++++++---------
 2 files changed, 74 insertions(+), 28 deletions(-)

diff --git a/ginac/parser/default_reader.tpl b/ginac/parser/default_reader.tpl
index 5f83cfe..2018e04 100644
--- a/ginac/parser/default_reader.tpl
+++ b/ginac/parser/default_reader.tpl
@@ -19,6 +19,12 @@ COMMENT a part of GiNaC parser -- construct functions from a byte stream.
 #include "power.h"
 #include "operators.h"
 #include "inifcns.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h> // for uintptr_t
+#endif
 
 namespace GiNaC
 {
@@ -31,6 +37,31 @@ static ex [+ (get "name") +]_reader(const exvector& ev)
 		  (format '#f "~{ev[~a]~^, ~}" (sequence 0 (- nargs 1)))) +]);
 }[+ ENDFOR +]
 
+// function::registered_functions() is protected, but we need to access it
+class registered_functions_hack : public function
+{
+public:
+	static const std::vector<function_options>& get_registered_functions()
+	{
+		return function::registered_functions();
+	}
+private:
+	registered_functions_hack();
+	registered_functions_hack(const registered_functions_hack&);
+	registered_functions_hack& operator=(const registered_functions_hack&);
+};
+
+// Encode an integer into a pointer to a function. Since functions
+// are aligned (the minimal alignment depends on CPU architecture)
+// we can distinguish between pointers and integers.
+static reader_func encode_serial_as_reader_func(unsigned serial)
+{
+	uintptr_t u = (uintptr_t)serial;
+	u = (u << 1) | (uintptr_t)1;
+	reader_func ptr = reinterpret_cast<reader_func>((void *)u);
+	return ptr;
+}
+
 const prototype_table& get_default_reader()
 {
 	using std::make_pair;
@@ -42,22 +73,16 @@ const prototype_table& get_default_reader()
 			(if (exist? "args") (get "args") "1")
 			+])] = [+ (get "name") +]_reader;[+
 		ENDFOR +]
-		try {
-			for ( unsigned ser=0; ; ++ser ) {
-				GiNaC::function f(ser);
-				std::string name = f.get_name();
-				for ( std::size_t nargs=0; ; ++nargs ) {
-					try {
-						function::find_function(name, nargs);
-						prototype proto = std::pair<std::string, std::size_t>(name, nargs);
-						std::pair<prototype_table::iterator, bool> ins = reader.insert(std::pair<prototype,reader_func>(proto, (reader_func)ser));
-						if ( ins.second ) break;
-					}
-					catch ( std::runtime_error ) { }
-				}
-			}
+		std::vector<function_options>::const_iterator it =
+			registered_functions_hack::get_registered_functions().begin();
+		std::vector<function_options>::const_iterator end =
+			registered_functions_hack::get_registered_functions().end();
+		unsigned serial = 0;
+		for (; it != end; ++it) {
+			prototype proto = make_pair(it->get_name(), it->get_nparams());
+			reader[proto] = encode_serial_as_reader_func(serial);
+			++serial;
 		}
-		catch ( std::runtime_error ) { }
 		initialized = true;
 	}
 	return reader;
diff --git a/ginac/parser/parser.cpp b/ginac/parser/parser.cpp
index cfa313b..acf5f3d 100644
--- a/ginac/parser/parser.cpp
+++ b/ginac/parser/parser.cpp
@@ -32,6 +32,36 @@
 
 namespace GiNaC {
 
+// <KLUDGE>
+// Find out if ptr is a pointer to a function or a specially crafted integer.
+// It's possible to distinguish between these because functions are aligned.
+// Returns true if ptr is a pointer and false otherwise.
+static bool decode_serial(unsigned& serial, const reader_func ptr)
+{
+	uintptr_t u = (uintptr_t)(void *)ptr;
+	if (u & 1) {
+		u >>= 1;
+		serial = (unsigned)u;
+		return false;
+	}
+	return true;
+}
+
+// Figures out if ptr is a pointer to function or a serial of GiNaC function.
+// In the former case calls that function, in the latter case constructs
+// GiNaC function with corresponding serial and arguments.
+static ex dispatch_reader_fcn(const reader_func ptr, const exvector& args)
+{
+	unsigned serial = 0; // dear gcc, could you please shut up?
+	bool is_ptr = decode_serial(serial, ptr);
+	if (is_ptr)
+		return ptr(args);
+	else
+		return function(serial, args);
+}
+// </KLUDGE>
+
+
 /// identifier_expr:  identifier |  identifier '(' expression* ')'
 ex parser::parse_identifier_expr()
 {
@@ -66,19 +96,10 @@ ex parser::parse_identifier_expr()
 		Parse_error_("no function \"" << name << "\" with " <<
 			     args.size() << " arguments");
 	}
-	// dirty hack to distinguish between serial numbers of functions and real
-	// pointers.
-	GiNaC::function* f = NULL;
-	try {
-		unsigned serial = (unsigned)(unsigned long)(void *)(reader->second);
-		f = new GiNaC::function(serial, args);
-	}
-	catch ( std::runtime_error ) {
-		if ( f ) delete f;
-		ex ret = reader->second(args);
-		return ret;
-	}
-	return f->setflag(status_flags::dynallocated);
+	// reader->second might be a pointer to a C++ function or a specially
+	// crafted serial of a GiNaC::function.
+	ex ret = dispatch_reader_fcn(reader->second, args);
+	return ret;
 }
 
 /// paren_expr:  '(' expression ')'
-- 
1.6.3.3

Best regards,
	Alexei



More information about the GiNaC-devel mailing list