Skip to content

Commit 2bbdbb7

Browse files
committed
Use an unsafe serializer.
This gives a noticable impact on serialization performance in Gecko. wr_dp_push_text() goes from 35.6% of nsDisplayText::CreateWebRenderCommands down to 24%. The generated code is still pretty bad but hopefully adding proper noalias information to rust will fix that.
1 parent aa81aeb commit 2bbdbb7

File tree

1 file changed

+76
-11
lines changed

1 file changed

+76
-11
lines changed

webrender_api/src/display_list.rs

+76-11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use YuvImageDisplayItem;
1717
use bincode;
1818
use serde::{Deserialize, Serialize, Serializer};
1919
use serde::ser::{SerializeMap, SerializeSeq};
20+
use std::io::Write;
21+
use std::{io, ptr};
2022
use std::marker::PhantomData;
2123
use time::precise_time_ns;
2224

@@ -483,6 +485,71 @@ impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
483485
}
484486
}
485487

488+
// This is a replacement for bincode::serialize_into(&vec)
489+
// The default implementation Write for Vec will basically
490+
// call extend_from_slice(). Serde ends up calling that for every
491+
// field of a struct that we're serializing. extend_from_slice()
492+
// does not get inlined and thus we end up calling a generic memcpy()
493+
// implementation. If we instead reserve enough room for the serialized
494+
// struct in the Vec ahead of time we can rely on that and use
495+
// the following UnsafeVecWriter to write into the vec without
496+
// any checks. This writer assumes that size returned by the
497+
// serialize function will not change between calls to serialize_into:
498+
//
499+
// For example, the following struct will cause memory unsafety when
500+
// used with UnsafeVecWriter.
501+
//
502+
// struct S {
503+
// first: Cell<bool>,
504+
// }
505+
//
506+
// impl Serialize for S {
507+
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
508+
// where S: Serializer
509+
// {
510+
// if self.first.get() {
511+
// self.first.set(false);
512+
// ().serialize(serializer)
513+
// } else {
514+
// 0.serialize(serializer)
515+
// }
516+
// }
517+
// }
518+
//
519+
520+
struct UnsafeVecWriter<'a>(&'a mut Vec<u8>);
521+
522+
impl<'a> Write for UnsafeVecWriter<'a> {
523+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
524+
unsafe {
525+
let old_len = self.0.len();
526+
self.0.set_len(old_len + buf.len());
527+
ptr::copy_nonoverlapping(buf.as_ptr(), self.0.as_mut_ptr().offset(old_len as isize), buf.len());
528+
}
529+
Ok(buf.len())
530+
}
531+
fn flush(&mut self) -> io::Result<()> { Ok(()) }
532+
}
533+
534+
struct SizeCounter(usize);
535+
536+
impl<'a> Write for SizeCounter {
537+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
538+
self.0 += buf.len();
539+
Ok(buf.len())
540+
}
541+
fn flush(&mut self) -> io::Result<()> { Ok(()) }
542+
}
543+
544+
fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: &T) {
545+
// manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
546+
let mut size = SizeCounter(0);
547+
bincode::serialize_into(&mut size,e , bincode::Infinite).unwrap();
548+
vec.reserve(size.0);
549+
550+
bincode::serialize_into(&mut UnsafeVecWriter(vec), e, bincode::Infinite).unwrap();
551+
}
552+
486553
#[derive(Clone)]
487554
pub struct DisplayListBuilder {
488555
pub data: Vec<u8>,
@@ -541,28 +608,26 @@ impl DisplayListBuilder {
541608
}
542609

543610
fn push_item(&mut self, item: SpecificDisplayItem, info: &LayoutPrimitiveInfo) {
544-
bincode::serialize_into(
611+
serialize_fast(
545612
&mut self.data,
546613
&DisplayItem {
547614
item,
548615
clip_and_scroll: *self.clip_stack.last().unwrap(),
549616
info: *info,
550617
},
551-
bincode::Infinite,
552-
).unwrap();
618+
)
553619
}
554620

555621
fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
556622
let info = LayoutPrimitiveInfo::new(LayoutRect::zero());
557-
bincode::serialize_into(
623+
serialize_fast(
558624
&mut self.data,
559625
&DisplayItem {
560626
item,
561627
clip_and_scroll: *self.clip_stack.last().unwrap(),
562628
info,
563-
},
564-
bincode::Infinite,
565-
).unwrap();
629+
}
630+
)
566631
}
567632

568633
fn push_iter<I>(&mut self, iter: I)
@@ -575,10 +640,10 @@ impl DisplayListBuilder {
575640
let len = iter.len();
576641
let mut count = 0;
577642

578-
bincode::serialize_into(&mut self.data, &len, bincode::Infinite).unwrap();
643+
serialize_fast(&mut self.data, &len);
579644
for elem in iter {
580645
count += 1;
581-
bincode::serialize_into(&mut self.data, &elem, bincode::Infinite).unwrap();
646+
serialize_fast(&mut self.data, &elem);
582647
}
583648

584649
debug_assert_eq!(len, count);
@@ -1103,8 +1168,8 @@ impl DisplayListBuilder {
11031168

11041169
// Append glyph data to the end
11051170
for ((font_key, color), sub_glyphs) in glyphs {
1106-
bincode::serialize_into(&mut self.data, &font_key, bincode::Infinite).unwrap();
1107-
bincode::serialize_into(&mut self.data, &color, bincode::Infinite).unwrap();
1171+
serialize_fast(&mut self.data, &font_key);
1172+
serialize_fast(&mut self.data, &color);
11081173
self.push_iter(sub_glyphs);
11091174
}
11101175

0 commit comments

Comments
 (0)