Skip to content

Commit b460126

Browse files
authored
perf: replace ToString with AsRef (#1415)
Avoid allocations when possible for different net messages by avoiding ToString trait and using AsRef<str>. before -> after <img width="1125" height="196" alt="image" src="https://github.com/user-attachments/assets/558c9f5c-7ad5-40f9-85db-512e805a2e6a" /> before <img width="2076" height="1085" alt="image" src="https://github.com/user-attachments/assets/f2eb8578-2edd-4a40-abe1-ebfda0a39d01" /> after <img width="2081" height="1274" alt="image" src="https://github.com/user-attachments/assets/c46e8508-470b-42e4-8ce2-c23e0bc3fa5a" /> I have to come up with very specific shape of benchmark sql to see the good performance boost, otherwise it was not hurting performance anyway in most cases, but better to overprovision.
1 parent a15d291 commit b460126

12 files changed

Lines changed: 43 additions & 40 deletions

File tree

pgdog/src/backend/replication/logical/subscriber/stream.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Statement {
8787
fn new(query: &str) -> Result<Self, Error> {
8888
let name = statement_name();
8989
Ok(Self {
90-
parse: Parse::named(name, query.to_string()),
90+
parse: Parse::named(name, query),
9191
})
9292
}
9393
}

pgdog/src/frontend/client/query_engine/test/sharded_prepared.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::net::{CommandComplete, DataRow, Message};
33
use super::prelude::*;
44
use super::*;
55

6-
async fn query(client: &mut TestClient, sql: impl ToString) -> Vec<Message> {
6+
async fn query(client: &mut TestClient, sql: impl AsRef<str>) -> Vec<Message> {
77
client.send_simple(Query::new(sql)).await;
88
client.read_until('Z').await.unwrap()
99
}

pgdog/src/frontend/router/parser/query/test/setup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ impl QueryParserTest {
166166
/// Set a parameter value.
167167
pub(crate) fn with_param(
168168
mut self,
169-
name: impl ToString,
169+
name: impl AsRef<str>,
170170
value: impl Into<ParameterValue>,
171171
) -> Self {
172172
self.params.insert(name, value);

pgdog/src/net/messages/auth/password.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ impl Password {
2424
}
2525
}
2626

27-
pub fn new_password(response: impl ToString) -> Self {
27+
pub fn new_password(response: impl AsRef<str>) -> Self {
2828
Self::PasswordMessage {
29-
response: response.to_string() + "\0",
29+
response: [response.as_ref(), "\0"].concat(),
3030
}
3131
}
3232

pgdog/src/net/messages/bind.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use uuid::Uuid;
55
use super::Error;
66
use super::FromDataType;
77
use super::Vector;
8+
use super::c_string_bytes;
89
use super::code;
910
use super::prelude::*;
1011
use bytes::BytesMut;
@@ -186,8 +187,8 @@ impl Bind {
186187
}
187188

188189
/// Rename this Bind message to a different prepared statement.
189-
pub fn rename(&mut self, name: impl ToString) {
190-
self.statement = Bytes::from(name.to_string() + "\0");
190+
pub fn rename(&mut self, name: impl AsRef<str>) {
191+
self.statement = c_string_bytes(name.as_ref());
191192
self.original = None;
192193
}
193194

@@ -227,30 +228,30 @@ impl Bind {
227228

228229
pub fn new_statement(name: &str) -> Self {
229230
Self {
230-
statement: Bytes::from(name.to_string() + "\0"),
231+
statement: c_string_bytes(name),
231232
..Default::default()
232233
}
233234
}
234235

235236
pub fn new_params(name: &str, params: &[Parameter]) -> Self {
236237
Self {
237-
statement: Bytes::from(name.to_string() + "\0"),
238+
statement: c_string_bytes(name),
238239
params: params.to_vec(),
239240
..Default::default()
240241
}
241242
}
242243

243244
pub fn new_name_portal(name: &str, portal: &str) -> Self {
244245
Self {
245-
statement: Bytes::from(name.to_string() + "\0"),
246-
portal: Bytes::from(portal.to_string() + "\0"),
246+
statement: c_string_bytes(name),
247+
portal: c_string_bytes(portal),
247248
..Default::default()
248249
}
249250
}
250251

251252
pub fn new_params_codes(name: &str, params: &[Parameter], codes: &[Format]) -> Self {
252253
Self {
253-
statement: Bytes::from(name.to_string() + "\0"),
254+
statement: c_string_bytes(name),
254255
codes: codes.to_vec(),
255256
params: params.to_vec(),
256257
..Default::default()

pgdog/src/net/messages/command_complete.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ impl CommandComplete {
7979
Self::from_str("COMMIT")
8080
}
8181

82-
pub fn new(command: impl ToString) -> Self {
83-
Self::from_str(command.to_string().as_str())
82+
pub fn new(command: impl AsRef<str>) -> Self {
83+
Self::from_str(command.as_ref())
8484
}
8585
}
8686

pgdog/src/net/messages/copy_fail.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use super::{code, prelude::*};
1+
use super::{c_string_bytes, code, prelude::*};
22

33
#[derive(Debug, Clone, PartialEq)]
44
pub struct CopyFail {
55
error: Bytes,
66
}
77

88
impl CopyFail {
9-
pub fn new(error: impl ToString) -> Self {
10-
let error = error.to_string();
9+
pub fn new(error: impl AsRef<str>) -> Self {
1110
Self {
12-
error: Bytes::from(format!("{}\0", error)),
11+
error: c_string_bytes(error.as_ref()),
1312
}
1413
}
1514
}

pgdog/src/net/messages/describe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ impl Describe {
6262
self.kind() != 'S' || self.statement().is_empty()
6363
}
6464

65-
pub fn rename(&mut self, name: impl ToString) {
65+
pub fn rename(&mut self, name: impl AsRef<str>) {
6666
let mut payload = Payload::named('D');
6767
payload.put_u8(self.kind() as u8);
68-
payload.put_string(&name.to_string());
68+
payload.put_string(name.as_ref());
6969
self.payload = payload.freeze();
7070
self.original = None;
7171
}

pgdog/src/net/messages/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ use crate::{net::Error, stats::memory::MemoryUsage};
8383

8484
use bytes::Bytes;
8585

86+
/// Encode a string as a NULL-terminated C string in a single allocation.
87+
pub(crate) fn c_string_bytes(value: &str) -> Bytes {
88+
Bytes::from([value.as_bytes(), b"\0"].concat())
89+
}
90+
8691
/// Convert a Rust struct to a PostgreSQL wire protocol message.
8792
pub trait ToBytes {
8893
/// Create the protocol message as an array of bytes.

pgdog/src/net/messages/parse.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ use std::str::from_utf8_unchecked;
1111
use super::code;
1212
use super::prelude::*;
1313

14-
fn c_string(value: &str) -> Bytes {
15-
Bytes::from([value.as_bytes(), b"\0"].concat())
16-
}
14+
use super::c_string_bytes;
1715

1816
/// Parse (F) message.
1917
#[derive(Clone, Hash, Eq, PartialEq, Default)]
@@ -48,17 +46,17 @@ impl Parse {
4846
pub fn new_anonymous(query: &str) -> Self {
4947
Self {
5048
name: Bytes::from("\0"),
51-
query: c_string(query),
49+
query: c_string_bytes(query),
5250
data_types: Bytes::copy_from_slice(&0i16.to_be_bytes()),
5351
original: None,
5452
}
5553
}
5654

5755
/// New prepared statement.
58-
pub fn named(name: impl ToString, query: impl ToString) -> Self {
56+
pub fn named(name: impl AsRef<str>, query: impl AsRef<str>) -> Self {
5957
Self {
60-
name: c_string(&name.to_string()),
61-
query: c_string(&query.to_string()),
58+
name: c_string_bytes(name.as_ref()),
59+
query: c_string_bytes(query.as_ref()),
6260
data_types: Bytes::copy_from_slice(&0i16.to_be_bytes()),
6361
original: None,
6462
}
@@ -90,16 +88,16 @@ impl Parse {
9088
// won't pin any original buffers (allowing to modify them without new allocation)
9189
// and the new allocation memory size will be just limited by the actual data, not buffers
9290
Parse {
93-
name: c_string(name),
91+
name: c_string_bytes(name),
9492
query: Bytes::copy_from_slice(&self.query),
9593
data_types: Bytes::copy_from_slice(&self.data_types),
9694
original: None,
9795
}
9896
}
9997

10098
/// Rename the prepared statement with minimal allocations.
101-
pub fn rename(&mut self, name: &str) {
102-
self.name = c_string(name);
99+
pub fn rename(&mut self, name: impl AsRef<str>) {
100+
self.name = c_string_bytes(name.as_ref());
103101
self.original = None;
104102
}
105103

@@ -116,7 +114,7 @@ impl Parse {
116114

117115
/// Update the SQL for this prepared statement.
118116
pub fn set_query(&mut self, query: &str) {
119-
self.query = c_string(query);
117+
self.query = c_string_bytes(query);
120118
self.original = None;
121119
}
122120

0 commit comments

Comments
 (0)