From 8c7ea784856d79a57d4c0bdb0e86d4bd1431d2e0 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Thu, 11 Jul 2013 12:42:17 -0400 Subject: [PATCH] Add binary decoding for floats --- bench_test.go | 122 ++++++++++++++++++++++++++++++++++++++++++++ value_transcoder.go | 31 +++++++++-- 2 files changed, 149 insertions(+), 4 deletions(-) diff --git a/bench_test.go b/bench_test.go index 2efd46c9..a7a534cb 100644 --- a/bench_test.go +++ b/bench_test.go @@ -11,6 +11,8 @@ var narrowTestDataLoaded bool var int2TextVsBinaryTestDataLoaded bool var int4TextVsBinaryTestDataLoaded bool var int8TextVsBinaryTestDataLoaded bool +var float4TextVsBinaryTestDataLoaded bool +var float8TextVsBinaryTestDataLoaded bool func mustPrepare(b *testing.B, conn *Connection, name, sql string) { if err := conn.Prepare(name, sql); err != nil { @@ -407,3 +409,123 @@ func BenchmarkInt8Binary(b *testing.B) { } } } + +func createFloat4TextVsBinaryTestData(b *testing.B, conn *Connection) { + if float4TextVsBinaryTestDataLoaded { + return + } + + if _, err := conn.Execute(` + drop table if exists t; + + create temporary table t( + a float4 not null, + b float4 not null, + c float4 not null, + d float4 not null, + e float4 not null + ); + + insert into t(a, b, c, d, e) + select + (random() * 1000000)::float4, (random() * 1000000)::float4, (random() * 1000000)::float4, (random() * 1000000)::float4, (random() * 1000000)::float4 + from generate_series(1, 10); + `); err != nil { + b.Fatalf("Could not set up test data: %v", err) + } + + float4TextVsBinaryTestDataLoaded = true +} + +func BenchmarkFloat4Text(b *testing.B) { + conn := getSharedConnection() + createFloat4TextVsBinaryTestData(b, conn) + + binaryDecoder := valueTranscoders[oid(700)].DecodeBinary + valueTranscoders[oid(700)].DecodeBinary = nil + defer func() { valueTranscoders[oid(700)].DecodeBinary = binaryDecoder }() + + mustPrepare(b, conn, "selectFloat32", "select * from t") + defer func() { conn.Deallocate("selectFloat32") }() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := conn.SelectRows("selectFloat32"); err != nil { + b.Fatalf("Failure while benchmarking: %v", err) + } + } +} + +func BenchmarkFloat4Binary(b *testing.B) { + conn := getSharedConnection() + createFloat4TextVsBinaryTestData(b, conn) + mustPrepare(b, conn, "selectFloat32", "select * from t") + defer func() { conn.Deallocate("selectFloat32") }() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := conn.SelectRows("selectFloat32"); err != nil { + b.Fatalf("Failure while benchmarking: %v", err) + } + } +} + +func createFloat8TextVsBinaryTestData(b *testing.B, conn *Connection) { + if float8TextVsBinaryTestDataLoaded { + return + } + + if _, err := conn.Execute(` + drop table if exists t; + + create temporary table t( + a float8 not null, + b float8 not null, + c float8 not null, + d float8 not null, + e float8 not null + ); + + insert into t(a, b, c, d, e) + select + (random() * 1000000)::float8, (random() * 1000000)::float8, (random() * 1000000)::float8, (random() * 1000000)::float8, (random() * 1000000)::float8 + from generate_series(1, 10); + `); err != nil { + b.Fatalf("Could not set up test data: %v", err) + } + + float8TextVsBinaryTestDataLoaded = true +} + +func BenchmarkFloat8Text(b *testing.B) { + conn := getSharedConnection() + createFloat8TextVsBinaryTestData(b, conn) + + binaryDecoder := valueTranscoders[oid(701)].DecodeBinary + valueTranscoders[oid(701)].DecodeBinary = nil + defer func() { valueTranscoders[oid(701)].DecodeBinary = binaryDecoder }() + + mustPrepare(b, conn, "selectFloat32", "select * from t") + defer func() { conn.Deallocate("selectFloat32") }() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := conn.SelectRows("selectFloat32"); err != nil { + b.Fatalf("Failure while benchmarking: %v", err) + } + } +} + +func BenchmarkFloat8Binary(b *testing.B) { + conn := getSharedConnection() + createFloat8TextVsBinaryTestData(b, conn) + mustPrepare(b, conn, "selectFloat32", "select * from t") + defer func() { conn.Deallocate("selectFloat32") }() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := conn.SelectRows("selectFloat32"); err != nil { + b.Fatalf("Failure while benchmarking: %v", err) + } + } +} diff --git a/value_transcoder.go b/value_transcoder.go index f76fa9ff..af5cf2f2 100644 --- a/value_transcoder.go +++ b/value_transcoder.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "fmt" "strconv" + "unsafe" ) type valueTranscoder struct { @@ -58,13 +59,15 @@ func init() { // float4 valueTranscoders[oid(700)] = &valueTranscoder{ - DecodeText: decodeFloat4FromText, - EncodeTo: encodeFloat4} + DecodeText: decodeFloat4FromText, + DecodeBinary: decodeFloat4FromBinary, + EncodeTo: encodeFloat4} // float8 valueTranscoders[oid(701)] = &valueTranscoder{ - DecodeText: decodeFloat8FromText, - EncodeTo: encodeFloat8} + DecodeText: decodeFloat8FromText, + DecodeBinary: decodeFloat8FromBinary, + EncodeTo: encodeFloat8} // varchar -- same as text valueTranscoders[oid(1043)] = valueTranscoders[oid(25)] @@ -167,6 +170,16 @@ func decodeFloat4FromText(mr *MessageReader, size int32) interface{} { return float32(n) } +func decodeFloat4FromBinary(mr *MessageReader, size int32) interface{} { + if size != 4 { + panic("Received an invalid size for an float4") + } + + i := mr.ReadInt32() + p := unsafe.Pointer(&i) + return *(*float32)(p) +} + func encodeFloat4(w *messageWriter, value interface{}) { v := value.(float32) s := strconv.FormatFloat(float64(v), 'e', -1, 32) @@ -183,6 +196,16 @@ func decodeFloat8FromText(mr *MessageReader, size int32) interface{} { return v } +func decodeFloat8FromBinary(mr *MessageReader, size int32) interface{} { + if size != 8 { + panic("Received an invalid size for an float4") + } + + i := mr.ReadInt64() + p := unsafe.Pointer(&i) + return *(*float64)(p) +} + func encodeFloat8(w *messageWriter, value interface{}) { v := value.(float64) s := strconv.FormatFloat(float64(v), 'e', -1, 64)