Skip to content

Commit 29d325f

Browse files
author
bors-servo
authored
Auto merge of #1799 - jrmuizel:unsafe-serialize, r=Gankro,kvark
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. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/1799) <!-- Reviewable:end -->
2 parents a884e67 + 214a48c commit 29d325f

File tree

1 file changed

+77
-11
lines changed

1 file changed

+77
-11
lines changed

webrender_api/src/display_list.rs

+77-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,72 @@ 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+
debug_assert!(self.0.len() <= self.0.capacity());
528+
ptr::copy_nonoverlapping(buf.as_ptr(), self.0.as_mut_ptr().offset(old_len as isize), buf.len());
529+
}
530+
Ok(buf.len())
531+
}
532+
fn flush(&mut self) -> io::Result<()> { Ok(()) }
533+
}
534+
535+
struct SizeCounter(usize);
536+
537+
impl<'a> Write for SizeCounter {
538+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
539+
self.0 += buf.len();
540+
Ok(buf.len())
541+
}
542+
fn flush(&mut self) -> io::Result<()> { Ok(()) }
543+
}
544+
545+
fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: &T) {
546+
// manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
547+
let mut size = SizeCounter(0);
548+
bincode::serialize_into(&mut size,e , bincode::Infinite).unwrap();
549+
vec.reserve(size.0);
550+
551+
bincode::serialize_into(&mut UnsafeVecWriter(vec), e, bincode::Infinite).unwrap();
552+
}
553+
486554
#[derive(Clone)]
487555
pub struct DisplayListBuilder {
488556
pub data: Vec<u8>,
@@ -541,28 +609,26 @@ impl DisplayListBuilder {
541609
}
542610

543611
fn push_item(&mut self, item: SpecificDisplayItem, info: &LayoutPrimitiveInfo) {
544-
bincode::serialize_into(
612+
serialize_fast(
545613
&mut self.data,
546614
&DisplayItem {
547615
item,
548616
clip_and_scroll: *self.clip_stack.last().unwrap(),
549617
info: *info,
550618
},
551-
bincode::Infinite,
552-
).unwrap();
619+
)
553620
}
554621

555622
fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
556623
let info = LayoutPrimitiveInfo::new(LayoutRect::zero());
557-
bincode::serialize_into(
624+
serialize_fast(
558625
&mut self.data,
559626
&DisplayItem {
560627
item,
561628
clip_and_scroll: *self.clip_stack.last().unwrap(),
562629
info,
563-
},
564-
bincode::Infinite,
565-
).unwrap();
630+
}
631+
)
566632
}
567633

568634
fn push_iter<I>(&mut self, iter: I)
@@ -575,10 +641,10 @@ impl DisplayListBuilder {
575641
let len = iter.len();
576642
let mut count = 0;
577643

578-
bincode::serialize_into(&mut self.data, &len, bincode::Infinite).unwrap();
644+
serialize_fast(&mut self.data, &len);
579645
for elem in iter {
580646
count += 1;
581-
bincode::serialize_into(&mut self.data, &elem, bincode::Infinite).unwrap();
647+
serialize_fast(&mut self.data, &elem);
582648
}
583649

584650
debug_assert_eq!(len, count);
@@ -1149,8 +1215,8 @@ impl DisplayListBuilder {
11491215

11501216
// Append glyph data to the end
11511217
for ((font_key, color), sub_glyphs) in glyphs {
1152-
bincode::serialize_into(&mut self.data, &font_key, bincode::Infinite).unwrap();
1153-
bincode::serialize_into(&mut self.data, &color, bincode::Infinite).unwrap();
1218+
serialize_fast(&mut self.data, &font_key);
1219+
serialize_fast(&mut self.data, &color);
11541220
self.push_iter(sub_glyphs);
11551221
}
11561222

0 commit comments

Comments
 (0)