IntBinaryDumper.register(int, ctx)
FloatDumper.register(float, ctx)
FloatBinaryDumper.register(float, ctx)
- # TODO: benchmark to work out if the binary dumper is faster
- # (the binary format is usually larger)
- # for now leaving the text format as default.
+ # The binary dumper is currently some 30% slower, so default to text
+ # (see tests/scripts/testdec.py for a rough benchmark)
DecimalBinaryDumper.register("decimal.Decimal", ctx)
DecimalDumper.register("decimal.Decimal", ctx)
Int2Dumper.register(Int2, ctx)
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
+ # TODO: this implementation is about 30% slower than the text dump.
+ # This might be probably optimised by accessing the C structure of
+ # the Decimal object, if available, which would save the creation of
+ # several intermediate Python objects (the DecimalTuple, the digits
+ # tuple, and then accessing them).
+
cdef object t = obj.as_tuple()
cdef int sign = t[0]
cdef tuple digits = t[1]
--- /dev/null
+"""
+A quick and rough performance comparison of text vs. binary Decimal adaptation
+"""
+from random import randrange
+from decimal import Decimal
+import psycopg3
+from psycopg3 import sql
+
+ncols = 10
+nrows = 500000
+format = psycopg3.pq.Format.BINARY
+test = "copy"
+
+
+def main():
+ cnn = psycopg3.connect()
+
+ cnn.execute(
+ sql.SQL("create table testdec ({})").format(
+ sql.SQL(", ").join(
+ [
+ sql.SQL("{} numeric(10,2)").format(sql.Identifier(f"t{i}"))
+ for i in range(ncols)
+ ]
+ )
+ )
+ )
+ cur = cnn.cursor()
+
+ if test == "copy":
+ with cur.copy(
+ f"copy testdec from stdin (format {format.name})"
+ ) as copy:
+ for j in range(nrows):
+ copy.write_row(
+ [
+ Decimal(randrange(10000000000)) / 100
+ for i in range(ncols)
+ ]
+ )
+
+ elif test == "insert":
+ ph = ["%t", "%b"][format]
+ cur.executemany(
+ "insert into testdec values (%s)" % ", ".join([ph] * ncols),
+ (
+ [Decimal(randrange(10000000000)) / 100 for i in range(ncols)]
+ for j in range(nrows)
+ ),
+ )
+ else:
+ raise Exception(f"bad test: {test}")
+
+
+if __name__ == "__main__":
+ main()