Skip to content

Commit 84b1005

Browse files
committed
Auto merge of rust-lang#85390 - Mark-Simulacrum:fast-bridge, r=petrochenkov
Optimize proc macro bridge This optimizes the proc macro bridge code for a win of 0.7% instruction counts on the diesel-check benchmark (non-incr, full). These wins are small, but hopefully not limited to just the diesel benchmark; the code is also not seriously impacted by the changes here.
2 parents b663c0f + 8c20808 commit 84b1005

File tree

2 files changed

+42
-16
lines changed

2 files changed

+42
-16
lines changed

library/proc_macro/src/bridge/buffer.rs

+40-14
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub struct Buffer<T: Copy> {
3939
data: *mut T,
4040
len: usize,
4141
capacity: usize,
42-
extend_from_slice: extern "C" fn(Buffer<T>, Slice<'_, T>) -> Buffer<T>,
42+
reserve: extern "C" fn(Buffer<T>, usize) -> Buffer<T>,
4343
drop: extern "C" fn(Buffer<T>),
4444
}
4545

@@ -78,18 +78,44 @@ impl<T: Copy> Buffer<T> {
7878
mem::take(self)
7979
}
8080

81+
// We have the array method separate from extending from a slice. This is
82+
// because in the case of small arrays, codegen can be more efficient
83+
// (avoiding a memmove call). With extend_from_slice, LLVM at least
84+
// currently is not able to make that optimization.
85+
pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[T; N]) {
86+
if xs.len() > (self.capacity - self.len) {
87+
let b = self.take();
88+
*self = (b.reserve)(b, xs.len());
89+
}
90+
unsafe {
91+
xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
92+
self.len += xs.len();
93+
}
94+
}
95+
8196
pub(super) fn extend_from_slice(&mut self, xs: &[T]) {
82-
// Fast path to avoid going through an FFI call.
83-
if let Some(final_len) = self.len.checked_add(xs.len()) {
84-
if final_len <= self.capacity {
85-
let dst = unsafe { slice::from_raw_parts_mut(self.data, self.capacity) };
86-
dst[self.len..][..xs.len()].copy_from_slice(xs);
87-
self.len = final_len;
88-
return;
89-
}
97+
if xs.len() > (self.capacity - self.len) {
98+
let b = self.take();
99+
*self = (b.reserve)(b, xs.len());
100+
}
101+
unsafe {
102+
xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
103+
self.len += xs.len();
104+
}
105+
}
106+
107+
pub(super) fn push(&mut self, v: T) {
108+
// The code here is taken from Vec::push, and we know that reserve()
109+
// will panic if we're exceeding isize::MAX bytes and so there's no need
110+
// to check for overflow.
111+
if self.len == self.capacity {
112+
let b = self.take();
113+
*self = (b.reserve)(b, 1);
114+
}
115+
unsafe {
116+
*self.data.add(self.len) = v;
117+
self.len += 1;
90118
}
91-
let b = self.take();
92-
*self = (b.extend_from_slice)(b, Slice::from(xs));
93119
}
94120
}
95121

@@ -131,16 +157,16 @@ impl<T: Copy> From<Vec<T>> for Buffer<T> {
131157
}
132158
}
133159

134-
extern "C" fn extend_from_slice<T: Copy>(b: Buffer<T>, xs: Slice<'_, T>) -> Buffer<T> {
160+
extern "C" fn reserve<T: Copy>(b: Buffer<T>, additional: usize) -> Buffer<T> {
135161
let mut v = to_vec(b);
136-
v.extend_from_slice(&xs);
162+
v.reserve(additional);
137163
Buffer::from(v)
138164
}
139165

140166
extern "C" fn drop<T: Copy>(b: Buffer<T>) {
141167
mem::drop(to_vec(b));
142168
}
143169

144-
Buffer { data, len, capacity, extend_from_slice, drop }
170+
Buffer { data, len, capacity, reserve, drop }
145171
}
146172
}

library/proc_macro/src/bridge/rpc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ macro_rules! rpc_encode_decode {
2727
(le $ty:ty) => {
2828
impl<S> Encode<S> for $ty {
2929
fn encode(self, w: &mut Writer, _: &mut S) {
30-
w.write_all(&self.to_le_bytes()).unwrap();
30+
w.extend_from_array(&self.to_le_bytes());
3131
}
3232
}
3333

@@ -114,7 +114,7 @@ impl<S> DecodeMut<'_, '_, S> for () {
114114

115115
impl<S> Encode<S> for u8 {
116116
fn encode(self, w: &mut Writer, _: &mut S) {
117-
w.write_all(&[self]).unwrap();
117+
w.push(self);
118118
}
119119
}
120120

0 commit comments

Comments
 (0)