예제 #1
0
def design(slides: Slides):
    slide = slides.new_slide()
    content = slide_header(slide, "Design by community")

    list = content.box()
    list_item(list).text("Open source")
    list_item(list).text("RFC")
    list.box(width=700, height=200, p_top=40).image("imgs/rust-rfc.png")

    slide = slides.new_slide()
    content = slide_header(slide, "Backwards compatibility")

    small = s(size=28)

    list = content.box()
    list_item(list).text("Strong BC guarantees")
    list_item(list, show="2+").text("New version every 6 weeks")
    list_item(list, show="2+", level=1).text(
        "Thousands of libraries tested to spot regressions", style=small)
    list_item(list, show="3+").text("Big changes => new edition")
    list_item(list, show="3+", level=1).text("Rust 2015 vs Rust 2018",
                                             style=small)

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Unstable features")

    code(content.box(), """
#![feature(async_await)]
async fn foo() {
    ...
}""")

    content.box(height=20)
    bash(content.box(show="2+"), "$ cargo +nightly build")
예제 #2
0
def structures(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(slide, "Structures")

    code_width = 920
    code(content.box(),
         """
struct Person {
    pub age: u32,
    name: String
}
""",
         width=code_width)

    code(content.box(show="next+"),
         """
impl Person {
    pub fn new(age: u32, name: String) -> Person {
        Person { age, name }
    }
    pub fn is_adult(&self) -> bool {
        self.age >= 18
    }
}    
""",
         width=code_width)
예제 #3
0
def adt(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(
        slide, """Algebraic data types/tagged unions/sum types/
discriminated unions/variants""")

    code_width = 960
    code(content.box(),
         """
enum Packet {
    Header { source: u32, tag: u32, data: Vec<u8> },
    Payload { data: Vec<u8> },
    Ack { seq: u64 }
}""",
         width=code_width)

    content.box(height=10)
    content.box(show="next+").text("Pattern matching",
                                   style=s(bold=True, size=40))

    code(content.box(show="last+"),
         """
match socket.get_packet() {
    Header {data, ..} | Payload {data, ..} => { },
    _ => { println!("Packet without data"); }
}
""",
         width=code_width)

    content.box(height=10)
    content.box(
        show="next+").text("The compiler forces you to handle all variants")
예제 #4
0
def parallelization(slides: Slides):
    slide = slides.new_slide()
    content = slide_header(slide, "Concurrency primitives")

    list = content.box()
    list_item(list).text("Mutexes")
    list_item(list, show="next+").text("Condition variables")
    list_item(list, show="next+").text("Atomics")
    list_item(list, show="next+").text("Synchronized queues")

    slide = slides.new_slide()
    slide.update_style("code", s(size=30))
    content = slide_header(slide, "Shared-memory parallelism")
    content.box().text("No OpenMP ☹")

    code_width = 800

    content.box(height=40)
    rayon = content.box(show="2+")
    rayon_box = rayon.box(horizontal=True)
    rayon_box.box().text("Rayon (+ Rayon adaptive")
    rayon_box.box(width=20)
    rayon_box.box(width=60).image("imgs/saurabh.png")
    rayon_box.box().text(" )")
    rayon.box(height=30)
    code_box = code(rayon.box(),
                    """
fn sum_of_squares(input: &[i32]) -> i32 {
    input.par_iter()
         .map(|&i| i * i)
         .sum()
}""",
                    width=code_width)
    code_box.line_box(1, z_level=99, x=165, width=165,
                      show="3+").rect(bg_color=CODE_HIGHLIGHT_COLOR)

    code(rayon.box(show="4+"),
         """
#[parallel]
for x in 0..10 {
    println!("{}", x);
}""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(slide, "Message-passing")
    code(
        content, """
let universe = mpi::initialize();
let world = universe.world();
let size = world.size();
let rank = world.rank();

if rank == 0 {
    let (msg, status) = world.any_process().receive_vec();
}
""")
예제 #5
0
    def unsafe_slide(header, code_body, content_show="1", code_size=36):
        slide = slides.new_slide()
        slide.update_style("code", s(size=code_size))
        content = slide_header(slide, "Unsafe Rust")
        content.box(y=0).text("Unsafe Rust allows:")

        content.box(height=20)
        content.box(show=content_show).text(header)
        content.box(height=10)
        code(content.box(show=content_show), code_body)
예제 #6
0
def multiphase_compiler(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    content = slide_header(slide, "Multi-phase compiler")

    code(
        content.box(), """
fn main() {
    look_ma_no_forward_declaration();
}

fn look_ma_no_forward_declaration() { }
""")
예제 #7
0
    def cpp_lifetime(comment=""):
        slide = slides.new_slide()
        slide.update_style("code", s(size=38))
        content = slide_header(slide, "Lifetimes (C++)")
        code(content,
             """
int* p;
{{
    int value = 5;
    p = &value;
}}{comment}
std::cout << *p << std::endl;
""".format(comment=comment),
             "cpp",
             width=800)
예제 #8
0
def constexpr(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=38))

    content = slide_header(slide, "Constexpr functions")

    code(
        content.box(), """
const fn double(x: i32) -> i32 {
    x * 2
}

const FIVE: i32 = 5;
const TEN: i32 = double(FIVE);
""")
예제 #9
0
def false_sharing(slides: SlideDeck, backup: bool):
    if backup:
        slide = new_slide(slides)
        content = slide_header(slide, "Code (backup)")
        code(
            content.box(), """// tid - [0, NO_OF_THREADS)
void thread_fn(int tid, double* data)
{
    size_t repetitions = 1024 * 1024 * 1024UL;
    for (size_t i = 0; i < repetitions; i++)
    {
        data[tid] *= i;
    }
}""")
        slide = new_slide(slides)
        content = slide_header(slide, "Result (backup)")
        content.box(height=600).image("images/example3-time.png")

    slide = new_slide(slides)
    content = slide_header(slide, "Cache system")
    content.box(height=600).image("images/haswell-diagram.png")
    content.box(width=230, height=40, x=816, y=530).rect(color=COLOR_BACKEND,
                                                         stroke_width=3)
    content.box(width=226, height=40, x=240, y=570).rect(color=COLOR_BACKEND,
                                                         stroke_width=3)

    slide = new_slide(slides)
    content = slide_header(slide, "Cache coherency")
    content.box(height=600).image("images/false-sharing.svg")

    slide = new_slide(slides)
    content = slide_header(slide, "False sharing")
    content.box(width="90%").image("images/false-sharing-array.svg")

    slide = new_slide(slides)
    content = slide_header(slide, "How to measure?")
    content.box().text("~tt{l2_rqsts.all_rfo}", s(size=48))
    content.box(p_top=20).text(
        "How many times some core invalidated data in other cores?")

    if backup:
        bash(content.box(p_top=40, show="next+"),
             """$ perf stat -e l2_rqsts.all_rfo ./example3
1 thread   ->        59 711
2 threads  -> 1 112 258 710""",
             text_style=s(align="left"))
예제 #10
0
def generics(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=30))
    content = slide_header(slide, "Generics")

    code_width = 900

    code(content.box(),
         """
struct KeyValue<K, V> {
    key: K,
    value: V
}""",
         width=code_width)
    code(content.box(show="next+"),
         """
trait Buffer<T> {
    fn read(&self) -> T;    
}""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         """
fn print_buffer<B: Buffer<T>, T: Display>(buffer: B) {
    println!("{}", buffer.read());
}""",
         width=code_width)
    code(content.box(show="next+"),
         """
fn print_bigger<T: PartialEq + Display>(a: T, b: T) {
    if (a > b) { println!("{}", a); }
}
""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         """
impl <T: Display> Serialize for T {
    ...
}
""",
         width=code_width)
예제 #11
0
def intrinsics(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=38))

    content = slide_header(slide, "Branch prediction")

    code(
        content.box(), """
if core::intrinsic::likely(condition) {
    ...
} else #[cold] {
    ...
}""")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    content = slide_header(slide, "SIMD")

    code_width = 900
    code(content.box(),
         """
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::_mm256_add_epi64;

_mm256_add_epi64(...);""",
         width=code_width)

    content.box(height=50)
    code(content.box(show="2+"),
         """
data.simd_iter()
    .simd_map(|v| {
        f32s(9.0) * v.abs().sqrt().ceil() -
        f32s(4.0) - f32s(2.0)
    })
    .scalar_collect();""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Inline assembly")

    code(
        content.box(), """
fn add(a: i32, b: i32) -> i32 {
    let c: i32;
    unsafe {
        asm!("add $2, $0"
             : "=r"(c)
             : "0"(a), "r"(b));
    }
    c
}""")
예제 #12
0
    def person_slide(end=""):
        slide = slides.new_slide()
        slide.update_style("code", s(size=50))
        content = slide_header(slide, "Ownership")
        return (slide,
                code(content,
                     """
fn foo(bitmap: Bitmap) {{
    ...
}}{}""".format(end),
                     width=840))
예제 #13
0
def modules(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=24))
    slide.set_style("bold", s(bold=True))
    content = slide_header(slide, "Proper module system")

    line = content.box(width="fill", horizontal=True)
    lib = line.box(width="50%", y=0)
    text_box = lib.box()
    text_box.box(show="1").text("foo.rs")
    text_box.overlay(show="next+").text("~bold{foo}.rs")
    code(
        lib.box(), """
pub fn fun1() {
    println!("fun1");
}
fn fun2() {
    println!("fun2");
}
""")

    main = line.box(width="50%", y=0, show="2+")
    main.box().text("main.rs")
    code(
        main.box(), """
use foo;

fn main() {
    foo::fun1();
    // foo::fun2(); private
}""")

    content.box(height=60)
    advantages = content.box(show="3+")
    advantages.update_style("default", s(size=40))
    list_item(advantages).text("visibility control")
    list_item(advantages).text("self-contained")
예제 #14
0
def c_interop(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=26))

    content = slide_header(slide, "C/C++ interop")

    code_width = 940
    c_from_rust = content.box()
    c_from_rust.text("C from Rust")
    code(content,
         """
extern {
    fn snappy_max_compressed_length(len: size_t) -> size_t;
}

let length = unsafe { snappy_max_compressed_length(100) };
""",
         width=code_width)

    content.box(height=20)
    box = content.box(show="2+")
    rust_from_c = box.box()
    rust_from_c.text("Rust from C")
    code(box,
         """
#[repr(C)]
struct Object {
    bar: i32,
}

extern "C" fn foo(param: *mut Object) {
    unsafe {
        (*target).bar = 5;
    }
}""",
         width=code_width)
예제 #15
0
def ownership(slides: Slides):
    slide = slides.new_slide()
    content = slide_header(slide, "Memory safety using the type system")
    list = content.box()
    list_item(list).text("Ownership")
    list_item(list, show="next+").text("Borrowing")
    list_item(list, show="next+").text("Lifetimes")

    slide = slides.new_slide()
    content = slide_header(slide, "Ownership")
    content.box().text("Every value in Rust has exactly one owner", s(size=50))
    content.box(height=10)
    content.box(show="next+").text(
        "When that owner goes out of scope, the value is dropped",
        style=s(size=36))

    def person_slide(end=""):
        slide = slides.new_slide()
        slide.update_style("code", s(size=50))
        content = slide_header(slide, "Ownership")
        return (slide,
                code(content,
                     """
fn foo(bitmap: Bitmap) {{
    ...
}}{}""".format(end),
                     width=840))

    (slide, box) = person_slide()
    pointer_to_line(slide,
                    box,
                    0,
                    100,
                    150,
                    "2+",
                    textbox_pos=("50%", "100%"),
                    code_pos=("40%", "10%")).text(
                        """No one else has any access to `bitmap`.
It can be mutated arbitrarily.""",
                        style=s(color="orange", size=40))

    person_slide(" // bitmap is dropped here")

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Ownership - move semantics")
    code_box = code(
        content.box(), """
fn foo(bitmap: Bitmap) { ... }

fn main() {
    let bitmap = Bitmap::load(...);
    foo(bitmap);
    ...
}
""")
    pointer_to_line(slide,
                    code_box,
                    4,
                    200,
                    120,
                    "2+",
                    textbox_pos=("40%", "100%"),
                    code_pos=("46%", "60%")).text("""`bitmap` is moved here.
It will not be `dropped` in the current scope.
""",
                                                  style=s(color="orange",
                                                          align="left"))

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Ownership - move semantics")
    code(
        content.box(), """
fn foo(bitmap: Bitmap) { ... }

fn main() {
    let bitmap = Bitmap::load(...);
    foo(bitmap);
    println!("{}", bitmap.width);
}
""")
    content.box(height=20)
    content.box(height=180, show="2+").image("imgs/ownership-moved.png")

    slide = slides.new_slide()
    content = slide_header(slide, "Constructors")
    list = content.box()
    list_item(list).text("Move constructors? Nope.")
    list_item(list, show="next+").text("Move assignment constructors? Nope.")

    slide = slides.new_slide()
    content = slide_header(slide, "Why are they needed in C++?")
    content.box(height=600).image("imgs/meme-lvalue.jpg")

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Why are they needed in C++?")
    box = code(
        content, """
void foo(Bitmap&& bitmap) { ... }

Bitmap bitmap(...);
foo(std::move(bitmap));
std::cout << bitmap.width << std::endl;""", "cpp")
    pointer_to_line(slide,
                    box,
                    4,
                    100,
                    600,
                    "2+",
                    textbox_pos=("40%", "0"),
                    code_pos=("40%", "100%")).text(
                        """`bitmap` is still accessible here.
It will be `dropped` at the end of scope.
Its state HAD to be reset in the move constructor.""",
                        style=s(color="orange", align="left"))

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, '"Copy" semantics')
    content.box().text("""Values are copied instead of moved
if they implement the `Copy` trait""",
                       style=s(bold=True))

    content.box(height=20)
    content.box(show="next+").text("Types are `Copy` if:")
    list = content.box()
    list_item(list,
              show="next+").text("they are primitive (integers, floats, etc.)")
    list_item(list, show="next+").text("they are marked as Copy")

    content.box(height=20)
    code(content.box(show="next+"),
         """
#[derive(Copy)]
struct Person {
    age: u32,
    male: bool
}""",
         width=500)

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, '"Copy" semantics')
    box = code(
        content.box(), """
fn foo(num: u32) { ... }

let number = 5;
foo(number);
println!("{}", number); // no error""")

    pointer_to_line(slide,
                    box,
                    3,
                    200,
                    600,
                    "2+",
                    textbox_pos=("40%", "0"),
                    code_pos=("34%", "60%")).text("""`number` is copied here.
    It can be still accessed after the call.""",
                                                  style=s(color="orange"))
예제 #16
0
def error_handling(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=24))
    content = slide_header(slide, "Error handling")

    code_width = 940
    code(content.box(),
         """
enum Option<T> {
    None,
    Some(T)
}
""",
         width=code_width)

    content.box(height=20)
    code(content.box(show="next+"),
         """
fn find_index(items: &[u32], item: u32) -> Option<usize> {
    ...
}""",
         width=code_width)

    code(content.box(show="next+"),
         """
let index = match find_index(&[1, 2, 3], 4) {
    Some(index) => println!("index found: {}", index),
    None => println!("index not found")
}
""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(slide, "Error handling")

    code(content.box(),
         """
enum Result<T, E> {
    Ok(T),
    Err(E),
}
""",
         width=code_width)

    content.box(height=20)
    code(content.box(show="next+"),
         """
fn find_in_db(...) -> Result<Vec<DbRow>, DbError> {
    ...
}""",
         width=code_width)

    code(content.box(show="next+"),
         """
let item = match find_in_db(...) {
    Ok(item) => item,
    Err(error) => {
        println!("Error: {}", error);
        vec!()
    }
}
""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=30))
    content = slide_header(slide, "Error handling")
    box = code(content.box(),
               """
fn download(address: String) -> Result<Vec<u8>> {
    let ip = address.parse()?;
    let client = TcpStream::connect(ip)?;

    let mut buf = vec!();
    client.read(&mut buf)?;
    buf
}
""",
               width=code_width)
    box.line_box(1, x=420, width=15, show="last+",
                 z_level=99).rect(bg_color=CODE_HIGHLIGHT_COLOR)
    box.line_box(2, x=585, width=15, show="last+",
                 z_level=99).rect(bg_color=CODE_HIGHLIGHT_COLOR)
    box.line_box(5, x=375, width=15, show="last+",
                 z_level=99).rect(bg_color=CODE_HIGHLIGHT_COLOR)

    content.box(height=20)
    code(content.box(show="next+"), "let item = value?;", width=code_width)
    code(content.box(show="next+"),
         """// expands to
let item = match value {
    Ok(v) => v,
    Err(e) => return Err(e)
};
""",
         width=code_width)
예제 #17
0
def lifetimes(slides: Slides):
    def cpp_lifetime(comment=""):
        slide = slides.new_slide()
        slide.update_style("code", s(size=38))
        content = slide_header(slide, "Lifetimes (C++)")
        code(content,
             """
int* p;
{{
    int value = 5;
    p = &value;
}}{comment}
std::cout << *p << std::endl;
""".format(comment=comment),
             "cpp",
             width=800)

    cpp_lifetime()
    cpp_lifetime(" // <-- `value` is destroyed here")

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Lifetimes (Rust)")
    code_box = code(content.box(),
                    """
let p;
{
    let value = 5;
    p = &value;
}
println!("{}", *p);""",
                    width=800)

    def line(start,
             end,
             start_x,
             end_x,
             right_x,
             offset_x,
             color,
             show,
             y="65%"):
        line_start = code_box.line_box(start, width="fill")
        line_end = code_box.line_box(end, width="fill")
        arrow = Arrow(20)

        start = line_start.p(start_x, y)
        end = line_end.p(end_x, y)

        content.box(show=show).line([
            start,
            line_start.p(right_x, y).add(offset_x, 0),
            line_end.p(right_x, y).add(offset_x, 0), end
        ],
                                    start_arrow=arrow,
                                    end_arrow=arrow,
                                    stroke_width=5,
                                    color=color)

    content.box(height=20)
    content.box(width=500, show="2+").text("Lifetime of reference `p`",
                                           style=s(color="orange",
                                                   align="left"))
    line(0, 5, "40%", "100%", "90%", 200, "orange", "2+")

    content.box(height=10)
    content.box(width=500, show="3+").text("Lifetime of `value`",
                                           style=s(color="green",
                                                   align="left"))
    line(2, 4, "100%", "10%", "100%", 50, "green", "3+")

    content.box(height=30)
    content.box(show="4+").text("""Lifetime of a value must be
>= lifetime of a reference to it""",
                                style=s(size=50))

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Lifetimes (Rust)")
    code_box = code(content.box(),
                    """
let p;
{
    let value = 5;
    p = &value;
}
println!("{}", *p);""",
                    width=800)

    content.box(height=20)
    content.box(height=280).image("imgs/lifetime-error.png")

    slide = slides.new_slide()
    slide.update_style("code", s(size=32))
    content = slide_header(slide, "What if compile time is not enough?")
    content.box().text("""If you can't prove to the compiler that the lifetimes
are correct, lifetime can be managed at runtime.""")

    code_width = 1000
    content.box(height=20)
    code_step(content.box(width=code_width, height=400),
              """
fn main() {
    let value = Rc::new(5); // refcount == 1
    {
        let a = value.clone(); // refcount == 2
    } // refcount == 1
} // refcount == 0, value is dropped
""",
              "2", ((0, 1, None, None, None, None), (0, 1, 2, 3, None, None),
                    (0, 1, 2, 3, 4, None), (0, 1, 2, 3, 4, 5)),
              width=code_width)
예제 #18
0
def traits(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(slide, "Traits")

    code_width = 960
    code(content.box(),
         """
trait Buffer {
    fn size(&self) -> usize;
    fn read(&self) -> u8;
}""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         "struct MemBuffer { data: Vec<u8> }",
         width=code_width)
    code(content.box(show="next+"),
         """
impl Buffer for MemBuffer {
    fn size(&self) -> usize { self.data.size() }
    fn read(&self) -> u8 { ... }
}""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         "struct FileBuffer { path: String }",
         width=code_width)
    code(content.box(show="next+"),
         """
impl Buffer for FileBuffer {
    fn size() -> usize { fs::metadata(self.path).len() }
    fn read(&self) -> u8 { ... }
}
""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=32))
    content = slide_header(slide, "Built-in traits")
    code(content.box(),
         """
impl Display for Person {
    fn fmt(&self, f: Formatter) -> Result { ... }
}
println!("{}", person);
""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         """
impl From<String> for IPAddress {
    fn from(value: String) -> IPAddress { ... }
}
let ip: IPAddress = "127.0.0.1".into();
""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         """
impl Add for Matrix {
    fn add(self, other: Matrix) -> Matrix { ... }
}
let c: Matrix = matA + matB;
""",
         width=code_width)
예제 #19
0
def branch_prediction(slides: SlideDeck, backup: bool):
    if backup:
        slide = new_slide(slides)
        content = slide_header(slide, "Code (backup)")
        code(
            content.box(),
            """std::vector<float> data = /* 32K random floats in [1, 10] */;
float sum = 0;
// std::sort(data.begin(), data.end());
for (auto x : data)
{
    if (x < 6.0f)
    {
        sum += x;
    }
}""")
        slide = new_slide(slides)
        content = slide_header(slide, "Result (backup)")
        content.box(width="90%").image("images/example0a-time.png")

    slide = new_slide(slides)
    content = slide_header(slide, "Most upvoted Stack Overflow question")
    content.box(height=600).image("images/stack-overflow.png")

    slide = new_slide(slides)
    content = slide_header(slide,
                           "What is going on? (Intel Amplifier - VTune)")
    content.box(height=600).image("images/vtune.png")

    slide = new_slide(slides)
    content = slide_header(slide, "What is going on? (perf)")
    bash(content.box(),
         """$ perf stat ./example0a --benchmark_filter=nosort
    853,672012  task-clock (msec) #   0,997 CPUs utilized
            30  context-switches  #   0,035 K/sec          
             0  cpu-migrations    #   0,000 K/sec
           199  page-faults       #   0,233 K/sec
 3 159 530 915  cycles            #   3,701 GHz
 1 475 799 619  instructions      #   0,47  insn per cycle
   419 608 357  branches          # 491,533 M/sec
   102 425 035  branch-misses     #  24,41% of all branches""",
         text_style=s(align="left", size=34))

    slide = new_slide(slides)
    content = slide_header(slide, "Branch predictor")
    content.box(height=600).image("images/haswell-diagram.png")
    content.box(width=130, height=40, x=595, y=140).rect(color=COLOR_FRONTEND,
                                                         stroke_width=3)

    slide = new_slide(slides)
    content = slide_header(slide, "CPU pipeline 101")
    content.box(height=400).image("images/branch-miss-pipeline.svg")

    slide = new_slide(slides)
    content = slide_header(slide, "Branch predictor")
    list_wrapper = content.box()
    list_item(list_wrapper).text("CPU tries to predict results of branches")
    list_item(list_wrapper,
              show="next+").text("Misprediction can cost ~15-20 cycles!",
                                 escape_char="#")

    slide = new_slide(slides)
    content = slide_header(slide, "Simple branch predictor - unsorted array")
    code(content.box(p_bottom=40), """if (data[i] < 6) {
    ...
}""")

    row = content.box(horizontal=True)
    box_dimension = 100

    def array(numbers, predictions, start=1, needle=6):
        stroke_width = 2
        size = 36
        for i in range(len(numbers)):
            box = row.box(width=box_dimension,
                          height=box_dimension).rect(color="black",
                                                     stroke_width=stroke_width)
            number = str(numbers[i])
            box.text(number, s(bold=True, size=size))

            predicted_correctly = (predictions[i] and numbers[i] < needle) or (
                not predictions[i] and numbers[i] >= needle)
            prediction = "green" if predicted_correctly else "red"
            show_overlay = "{}+".format(start + i * 2 + 1)
            overlay = box.overlay(show=show_overlay).rect(
                color="black", bg_color=prediction, stroke_width=stroke_width)
            overlay.text(number, s(color="white", bold=True, size=size))
            show_text = start + i * 2
            row.box(x=i * box_dimension,
                    y=box_dimension,
                    width=box_dimension,
                    show="{}-{}".format(show_text,
                                        show_text + 1)).text("{} < {}?".format(
                                            number, needle))

    values = [6, 2, 1, 7, 4, 8, 3, 9]
    text_style = s(align="left", size=42)
    width = 400

    def predict_sequence(wrapper: Box, values, start=1):
        for i in range(len(values)):
            value = "Taken" if values[i] else "Not taken"
            show_start = start + i * 2
            wrapper.overlay(
                show="{}-{}".format(show_start, show_start + 1)).rect(
                    bg_color="white").text("Prediction: {}".format(value),
                                           style=text_style)

    def predict_value(index):
        if index == 0:
            return False
        return values[index - 1] < 6

    predictions = [predict_value(i) for i in range(len(values))]

    array(values, predictions, start=2)
    prediction_wrapper = content.box(p_top=60,
                                     width=width).text("Prediction: Not taken",
                                                       style=text_style)
    predict_sequence(prediction_wrapper, predictions, start=1)

    content.box(show="next+", p_top=40).text("2 hits, 6 misses (25% hit rate)")

    slide = new_slide(slides)
    content = slide_header(slide, "Simple branch predictor - sorted array")
    code(content.box(p_bottom=40), """if (data[i] < 6) {
    ...
}""")

    row = content.box(horizontal=True)
    values = [1, 2, 3, 4, 6, 7, 8, 9]
    predictions = [predict_value(i) for i in range(len(values))]

    array(values, predictions, start=2)
    prediction_wrapper = content.box(p_top=60,
                                     width=width).text("Prediction: Not taken",
                                                       style=text_style)
    predict_sequence(prediction_wrapper, predictions, start=1)

    content.box(show="next+", p_top=40).text("6 hits, 2 misses (75% hit rate)")

    if backup:
        size = 40
        slide = new_slide(slides)
        content = slide_header(slide, "How can the compiler help?")
        row = content.box(horizontal=True)
        row.box(y=0, p_right=50, width=400).image("images/bm-float-code.png")
        row.box(y=0, width=600).image("images/bm-float-bin.png")
        content.box(p_top=20).text(
            "With ~tt{float}, there are two branches per iteration",
            style=s(size=size))

        slide = new_slide(slides)
        content = slide_header(slide, "How can the compiler help?")
        row = content.box(horizontal=True)
        row.box(y=0, p_right=50, width=400).image("images/bm-int-code.png")
        row.box(y=0, width=600).image("images/bm-int-bin.png")
        content.box(p_top=20).text(
            "With ~tt{int}, one branch is removed (using ~tt{cmov})",
            style=s(size=size))

    slide = new_slide(slides)
    content = slide_header(slide, "How to measure?")
    content.box().text("~tt{branch-misses}", style=s(size=48))
    content.box(p_top=20).text("How many times was a branch mispredicted?")

    if backup:
        bash(content.box(p_top=40, show="next+"),
             """$ perf stat -e branch-misses ./example0a
with    sort ->     383 902
without sort -> 101 652 009""",
             text_style=s(align="left"))

    slide = new_slide(slides)
    slide.update_style("code", s(size=40))
    content = slide_header(slide, "How to help the branch predictor?")
    list_wrapper = content.box()
    list_item(list_wrapper).text("More predictable data")
    list_item(list_wrapper, show="next+").text("Profile-guided optimization")
    list_item(list_wrapper,
              show="next+").text("Remove (unpredictable) branches")
    list_item(list_wrapper,
              show="next+").text("Compiler hints (use with caution)")
    code(
        list_wrapper.box(show="last+", p_top=20, p_bottom=20),
        """if (__builtin_expect(will_it_blend(), 0)) {
    // this branch is not likely to be taken
}""")

    slide = new_slide(slides)
    content = slide_header(slide, "Branch target prediction")
    list_wrapper = content.box()
    list_item(list_wrapper).text(
        "Target of a jump is not known at compile time:")
    list_item(list_wrapper, show="next+", level=1).text("Function pointer")
    list_item(list_wrapper, show="next+",
              level=1).text("Function return address")
    list_item(list_wrapper, show="next+", level=1).text("Virtual method")

    if backup:
        slide = new_slide(slides)
        slide.update_style("code", s(size=26))
        content = slide_header(slide, "Code (backup)")
        code(
            content,
            """struct A { virtual void handle(size_t* data) const = 0; };
struct B: public A { void handle(size_t* data) const final { *data += 1; } };
struct C: public A { void handle(size_t* data) const final { *data += 2; } };

std::vector<std::unique_ptr<A>> data = /* 4K random B/C instances */;
// std::sort(data.begin(), data.end(), /* sort by instance type */);
size_t sum = 0;
for (auto& x : data)
{
    x->handle(&sum);
}""")

        slide = new_slide(slides)
        content = slide_header(slide, "Result (backup)")
        content.box(width="90%").image("images/example0b-time.png")

        slide = new_slide(slides)
        content = slide_header(slide, "perf (backup)")
        bash(content.box(),
             """$ perf stat -e branch-misses ./example0b
with sort   ->     337 274
without sort -> 84 183 161""",
             text_style=s(align="left"))
예제 #20
0
def unsafe(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=32))
    content = slide_header(slide, "Where's the catch?")
    content.box().text("We have seen things that mutate through a shared borrow")

    code_width = 800
    code_step(content.box(width=code_width, height=400), """
// Arc::clone
fn clone(&self) -> Arc<T>;
// Mutex::lock
fn lock(&self) -> &mut T;
// AtomicU64::store
fn store(&self, val: u64, order: Ordering);
""", 2, [
        (0, 1, None, None, None, None),
        (0, 1, 2, 3, None, None),
        (0, 1, 2, 3, 4, 5)
    ], width=code_width)

    content.box(show="5+").text("This is called ~tt{interior mutability} and requires unsafe Rust",
                                s(size=32))

    slide = slides.new_slide()
    content = slide_header(slide, "Enter unsafe Rust")
    content.box().text("Some scenarios are not expressible in (safe) Rust")

    content.box(height=20)
    content.box(show="next+").text("In some cases, something more is required to:")

    list = content.box()
    list_item(list, show="next+").text("Express inherently unsafe paradigms")
    list_item(list, show="next+").text("Improve performance")
    list_item(list, show="next+").text("Interact with I/O, OS, hardware, network")

    slide = slides.new_slide()
    content = slide_header(slide, "Unsafe Rust")

    content.box().text("You can mark parts of code with the ~tt{unsafe} keyword")
    content.box(show="next+").text("Unsafe Rust is a ~emph{superset} of Rust")

    def unsafe_slide(header, code_body, content_show="1", code_size=36):
        slide = slides.new_slide()
        slide.update_style("code", s(size=code_size))
        content = slide_header(slide, "Unsafe Rust")
        content.box(y=0).text("Unsafe Rust allows:")

        content.box(height=20)
        content.box(show=content_show).text(header)
        content.box(height=10)
        code(content.box(show=content_show), code_body)

    unsafe_slide("Accessing a global mutable variable", """
static mut COUNTER: u32 = 0;

fn increment_count() {
    unsafe {
        COUNTER += 1;
    }
}""", content_show="2+")
    unsafe_slide("Dereferencing a raw pointer", """
let ptr = 0xCAFECAFE as *mut u32;
unsafe {
    *ptr = 5;
}""")
    unsafe_slide("Calling an unsafe function", """
unsafe {
    zlib_compress(&buffer, buffer.len());
}""")
    unsafe_slide("Implementing an unsafe trait", """
unsafe impl Send for MySuperSafeType {
    ...
}""")

    slide = slides.new_slide()
    slide.update_style("code", s(size=18))
    content = slide_header(slide, "Finding unsafe code - C++")
    code_box = code(content, """
std::atomic<LifecycleId> ArenaImpl::lifecycle_id_generator_;
GOOGLE_THREAD_LOCAL ArenaImpl::ThreadCache ArenaImpl::thread_cache_ = {-1, NULL};

void ArenaImpl::Init() {
  lifecycle_id_ =
      lifecycle_id_generator_.fetch_add(1, std::memory_order_relaxed);
  hint_.store(nullptr, std::memory_order_relaxed);
  threads_.store(nullptr, std::memory_order_relaxed);

  if (initial_block_) {
    // Thread which calls Init() owns the first block. This allows the
    // single-threaded case to allocate on the first block without having to
    // perform atomic operations.
    new (initial_block_) Block(options_.initial_block_size, NULL);
    SerialArena* serial =
        SerialArena::New(initial_block_, &thread_cache(), this);
    serial->set_next(NULL);
    threads_.store(serial, std::memory_order_relaxed);
    space_allocated_.store(options_.initial_block_size,
                           std::memory_order_relaxed);
    CacheSerialArena(serial);
  } else {
    space_allocated_.store(0, std::memory_order_relaxed);
  }
}
""")
    code_box.overlay(show="2+", z_level=99).rect(bg_color=CODE_HIGHLIGHT_COLOR)

    slide = slides.new_slide()
    content = slide_header(slide, "Finding unsafe code - Rust")
    bash(content.box(), '$ grep "unsafe" main.rs', text_style=s(size=40))

    slide = slides.new_slide()
    slide.box().text("""Rust builds safe abstractions
on top of unsafe foundations""", s(size=50))
예제 #21
0
def qol(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=50))
    content = slide_header(slide, "Type inference")

    code_width = 900
    code_step(content.box(width=code_width, height=400),
              """
let elem = 5u8;
let mut vec = Vec::new();
vec.push(elem);
// vec is now Vec<u8>
""",
              "1", ((0, None, None, None), (0, 1, None, None), (0, 1, 2, None),
                    (0, 1, 2, 3)),
              width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=50))
    content = slide_header(slide, "Iterators")

    code_step(content.box(width=code_width, height=400),
              """
vec.iter()
   .zip(iter2)
   .filter(|(a, b)| a > b)
   .map(|(a, b)| a * b)
   .sum::<i32>();
""",
              "1",
              ((0, None, None, None, None), (0, 1, None, None, None),
               (0, 1, 2, None, None), (0, 1, 2, 3, None), (0, 1, 2, 3, 4)),
              width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=32))
    content = slide_header(slide, "Generators")

    code(
        content.box(), """
let mut fibonacci = || {
    yield 1;

    let mut a = 0;
    let mut b = 1;
    loop {
        yield a + b;
        a = b;
        b = a + b;
    }
};
let f = fibonacci().iter().take(5).collect();
""")

    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(slide, "Async/await")

    code(
        content.box(), """
async fn compute_job(job: Job) -> Result<Data, Error> {
    let worker = await!(query_broker());
    match worker {
        Some(worker) => await!(send_job(worker)),
        None => await!(process_job_locally(job))
    }
}
""")
예제 #22
0
def macros(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=22))
    content = slide_header(slide, "Macros")

    code_width = 920
    code_step(content.box(width=code_width, height=200),
              """
macro_rules! find_min {
    ($x:expr) => ($x);
    ($x:expr, $($y:expr),+) => (
        std::cmp::min($x, find_min!($($y),+))
    )
}    
""",
              "1", ((0, 1, None, None, None, 5), (0, 1, 2, 3, 4, 5)),
              width=code_width)
    code(content.box(show="next+"),
         """
find_min!(5);       // 5
find_min!(2, 1, 3); // 1    
""",
         width=code_width)

    content.box(height=20)
    code(content.box(show="next+"),
         """
macro_rules! create_function {
    ($func_name:ident) => (
        fn $func_name() {
            println!("You called {:?}()", stringify!($func_name));
        }
    )
}""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=22))
    content = slide_header(slide, "Procedural macros")

    code_width = 920
    code(content.box(),
         """
fn my_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
    ...
}""",
         width=code_width)

    code(content.box(show="next+"),
         """
#[derive(my_macro)]
struct Record {
    #[my_macro]
    pub id: u32
}    
""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=26))
    content = slide_header(slide, "Procedural macros")

    code_width = 700
    code(content.box(),
         """
#[derive(Serialize, Deserialize)]
struct Person { name: String, age: u32 }""",
         width=code_width)
    code(content.box(show="next+"),
         """
json::to_string(person);
yaml::to_string(person);
let person = json::parse(person_str);""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         """
#[derive(CmdArgs)]
struct Args {
    #[arg(short = "d", long = "debug")]
    debug: bool,
    #[arg(parse(from_os_str))]
    path: PathBuf,
}
""",
         width=code_width)

    content.box(height=10)
    code(content.box(show="next+"),
         """
#[derive(Debug)]
struct Person { name: String, age: u32 }

println!("{:?}", person);
""",
         width=code_width)
예제 #23
0
def borrowing(slides: Slides):
    slide = slides.new_slide()
    content = slide_header(slide, "Where's the aliasing?")
    content.box().text("So far, we only have mutability, there's no aliasing:")

    content.box(height=20)
    list = content.box()
    list_item(list, show="next+").text(
        "After a move, the original value is not accessible")
    list_item(list, show="next+").text("After a copy, a new value is created")

    slide = slides.new_slide()
    content = slide_header(slide, "Borrowing")
    content.box().text(
        "Aliasing happens when you create a reference to a value")
    content.box(show="next+").text("This is called borrowing in Rust")

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Shared borrows")

    code_width = 800

    code(content,
         """
let value = Bitmap::load(...);
let a = &value;
let b = &value;
""",
         width=code_width)

    content.box(height=20)
    list = content.box()
    list_item(
        list,
        show="next+").text("Multiple shared borrows of a value may exist")

    list.box(height=10)
    list_item(list,
              show="next+").text("You can't mutate using a shared borrow")
    list.box(height=10)
    code(list.box(show="last+"),
         "a.width = 10; // does not compile",
         width=code_width)

    list.box(height=10)
    list_item(list, show="next+").text("You can't move out of a shared borrow")
    list.box(height=10)
    code(list.box(show="last+"),
         """
fn foo(bitmap: Bitmap) { }
foo(a); // does not compile""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Unique borrows")

    code_width = 800

    code(content,
         """
let value = Bitmap::load(...);
let c = &mut value;
""",
         width=code_width)

    content.box(height=20)
    list = content.box()
    list_item(list, show="next+").text(
        """If a unique borrow exists, there are no other references
to the same value""",
        style=s(align="left"))
    list_item(list, show="next+").text(
        "You can only create a unique borrow if you own the value")

    list.box(height=10)
    list_item(list, show="next+").text("You can mutate using a unique borrow")
    list.box(height=10)
    code(list.box(show="last+"), "c.width = 10;", width=code_width)

    list.box(height=10)
    list_item(list, show="next+").text("You can't move out of a unique borrow")
    list.box(height=10)
    code(list.box(show="last+"),
         """
fn foo(bitmap: Bitmap) { }
foo(c); // does not compile""",
         width=code_width)

    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Vector example (Rust)")

    code_width = 800
    code(content.box(show="3+"),
         """
// Vec::push
fn push(&mut self, value: T)
""",
         width=code_width)
    content.box(height=10)
    code_step(content.box(width=code_width, height=300),
              """
let vec = vec!(1, 2, 3);
let p = &vec[0];
vec.push(4);
println!("{}", p);
""",
              "1", ((0, None, None, None), (0, 1, None, None), (0, 1, 2, None),
                    (0, 1, 2, 3)),
              width=code_width)

    content.box(height=10)
    content.box(height=220, show="next+").image("imgs/borrowck-error.png")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    content = slide_header(slide, "What if compile time is not enough?")
    content.box().text(
        """If you can't prove to the compiler that your borrows are safe,
borrow checking can be done at runtime.""")
    content.box(
        show="next+").text("If any rules are broken, the program panics.")

    content.box(height=20)
    code_box = code(
        content.box(show="next+"), """
let value = RefCell::new(5);
let a = value.borrow();     // shared borrow
let b = value.borrow_mut(); // unique borrow""")
    pointer_to_line(slide,
                    code_box,
                    2,
                    100,
                    600,
                    "4+",
                    textbox_pos=("40%", 0),
                    code_pos=("40%", "100%")).text(
                        """This would panic, since there already is
a shared borrow""",
                        style=s(color="orange", align="left"))
예제 #24
0
def cache_conflicts(slides: SlideDeck, backup: bool):
    if backup:
        slide = new_slide(slides)
        content = slide_header(slide, "Code (backup)")
        code(
            content.box(),
            """// Addresses of N integers, each `offset` bytes apart
std::vector<int*> data = ...;
for (auto ptr: data)
{
    *ptr += 1;
}
// Offsets: 4, 64, 4000, 4096, 4128""")
        slide = new_slide(slides)
        content = slide_header(slide, "Result (backup)")
        content.box(height=600).image("images/example1-time.png")

    slide = new_slide(slides)
    content = slide_header(slide, "Cache memory")
    content.box(height=600).image("images/haswell-diagram.png")
    content.box(width=230, height=40, x=816, y=530).rect(color=COLOR_BACKEND,
                                                         stroke_width=3)

    slide = new_slide(slides)
    content = slide_header(slide, "How are (L1) caches implemented")
    list_wrapper = content.box()
    list_item(list_wrapper).text("N-way set associative table")
    list_item(list_wrapper, level=1, show="last+").text("Hardware hash table")
    list_item(list_wrapper, show="next+").text("Key = address (8B)")
    list_item(list_wrapper, show="next+").text("Entry = cache line (64B)")

    slide = new_slide(slides)
    content = slide_header(slide, "N-way set associative cache")
    hash_size = 8
    hash_dimension = 60

    def table(wrapper: Box,
              size,
              dimension,
              buckets=None,
              bucket_indices=True):
        htable = wrapper.box(horizontal=True)
        items = []
        for i in range(size):
            cell = htable.box(width=dimension,
                              height=dimension,
                              horizontal=True).rect("black", stroke_width=2)
            items.append(cell)

        if buckets:
            bucket_width = int((size / buckets) * dimension)
            for i in range(buckets):
                pos = i * bucket_width
                htable.box(x=pos, y=0, width=bucket_width,
                           height=dimension).rect("black", stroke_width=6)
                if bucket_indices:
                    htable.box(x=pos, y=dimension - 5,
                               width=bucket_width).text(str(i))

        return (htable, items)

    content.box().text("Size = {} cache lines".format(hash_size),
                       style="notice")
    (htable, hitems) = table(content.box(p_top=20), hash_size, hash_dimension)
    arrow_wrapper = content.box()
    arrow = Arrow(20)
    arrow_wrapper.box().line([
        hitems[0].p("50%", 0).add(0, -20),
        hitems[-1].p("50%", 0).add(0, -20),
    ],
                             start_arrow=arrow,
                             end_arrow=arrow,
                             stroke_width=5,
                             color=COLOR_NOTE)

    content.box(
        p_top=20,
        show="next+").text("Associativity (N) - # of cache lines per bucket")
    content.box(p_top=10, show="next+").text("# of buckets = Size / N")

    row = content.box(horizontal=True, p_top=20)
    lcol = row.box(y=0)
    rcol = row.box(y=0, p_left=20)

    def htable_row(text, block_count):
        padding = 20
        height = 110
        lcol.box(show="next+", p_top=padding, height=height).text(text)
        return table(rcol.box(show="last+", p_top=padding, height=height),
                     hash_size, hash_dimension, block_count)

    htable_row("N = 1 (direct mapped)", hash_size)
    htable_row("N = {} (fully associative)".format(hash_size), 1)
    htable_row("N = 2", hash_size // 2)

    slide = new_slide(slides)
    content = slide_header(slide, "How are addresses hashed?")

    content.box().text("64-bit address:")
    row = content.box(horizontal=True, width=800)
    widths = ["60%", "25%", "15%"]
    address_colors = ["#B22222", "#007944", "#0018AE"]
    labels = ["Tag", "Index", "Offset"]

    for i in range(3):
        wrapper = row.box(width=widths[i]).rect(color=address_colors[i],
                                                stroke_width=4)
        wrapper.box(padding=4).text(labels[i])
    labelrow = content.box(horizontal=True, x=220)
    labelrow.box().text("63")
    labelrow.box(p_left=770).text("0")

    list_wrapper = content.box(p_top=20)
    list_item(list_wrapper, show="next+").text("Offset", "bold")
    list_item(list_wrapper, level=1,
              show="last+").text("Selects byte within a cache line")
    list_item(list_wrapper, level=1,
              show="last+").text("log2(cache line size) bits")
    list_item(list_wrapper, show="next+").text("Index", "bold")
    list_item(list_wrapper, level=1,
              show="last+").text("Selects bucket within the cache")
    list_item(list_wrapper, level=1,
              show="last+").text("log2(bucket count) bits")
    list_item(list_wrapper, show="next+").text("Tag", "bold")
    list_item(list_wrapper, level=1, show="last+").text("Used for matching")

    slide = new_slide(slides)
    content = slide_header(slide, "N-way set associative cache")

    queue = content.box(x="55%", y=40, horizontal=True)
    queue.box(p_right=40).text("Cache lines:")
    colors = ("#F0134D", "#FF6F5E", "#F0134D")
    cacheline_labels = ("A", "B", "C")
    for i in range(3):
        queue.box(width=hash_dimension,
                  height=hash_dimension).rect(color="black",
                                              bg_color=colors[i],
                                              stroke_width=5).text(
                                                  cacheline_labels[i],
                                                  style=s(bold=True,
                                                          color="white"))

    index = content.box(x="55%", y=90, horizontal=True)
    index.box(p_right=75).text("Index bits:")
    index_bits = (0, 1, 0)
    for i in range(3):
        index.box(width=hash_dimension,
                  height=hash_dimension).text(str(index_bits[i]))

    def insert(slot, show, item):
        wrapper = slot.overlay(show=show)
        wrapper.rect(bg_color=colors[item])
        wrapper.text(cacheline_labels[item], style=s(bold=True, color="white"))

    row = content.box(horizontal=True, p_top=20)
    lcol = row.box(y=0)
    rcol = row.box(y=0, p_left=20)

    def htable_row(text, block_count):
        padding = 20
        height = 140
        lcol.box(show="next+", p_top=padding, height=height).text(text)
        return table(rcol.box(show="last+", p_top=padding, height=height),
                     hash_size, hash_dimension, block_count)

    (_, hitems) = htable_row("N = 1", hash_size)
    insert(hitems[0], "next+", 0)
    insert(hitems[1], "next+", 1)
    insert(hitems[0], "next+", 2)

    (_, hitems) = htable_row("N = {}".format(hash_size), 1)
    insert(hitems[0], "next+", 0)
    insert(hitems[1], "next+", 1)
    insert(hitems[2], "next+", 2)

    (_, hitems) = htable_row("N = 2", hash_size // 2)
    insert(hitems[0], "next+", 0)
    insert(hitems[2], "next+", 1)
    insert(hitems[1], "next+", 2)

    slide = new_slide(slides)
    slide.update_style("default", s(size=46))
    slide.update_style("bold", s(size=46))
    content = slide_header(slide, "Intel L1 cache")
    bash(content.box(),
         """$ getconf -a | grep LEVEL1_DCACHE
LEVEL1_DCACHE_SIZE      32768
LEVEL1_DCACHE_ASSOC     8
LEVEL1_DCACHE_LINESIZE  64""",
         text_style=s(align="left"))
    list_wrapper = content.box(p_top=20)
    list_item(
        list_wrapper,
        show="next+").text("~bold{Cache line size} - 64 B (6 offset bits)")
    list_item(list_wrapper, show="next+").text("~bold{Associativity} (N) - 8")
    list_item(list_wrapper, show="next+").text("~bold{Size} - 32768 B")
    list_item(list_wrapper, show="next+").text("32768 / 64 => 512 cache lines")
    list_item(list_wrapper,
              show="next+").text("512 / 8 => 64 buckets (6 index bits)")

    slides.set_style("tag", s(color=address_colors[0]))
    tag = slides.get_style("tag")
    slides.set_style("index", tag.compose(s(color=address_colors[1])))
    slides.set_style("offset", tag.compose(s(color=address_colors[2])))

    styles = ["tag", "index", "offset"]
    colors = ["#F0134D", "#FF6F5E", "#1F6650", "#40BFC1"]

    def address(cols, content, next=True, use_style=True, row=0):
        for i, col in enumerate(cols):
            show = "1+"
            if next:
                show = "next+" if i == 0 else "last+"

            style = "default"
            if use_style:
                if i == 0:
                    style = s(color=colors[row])
                else:
                    style = styles[i - 1]
            col.box(show=show).text(content[i], style=style)

    slide = new_slide(slides)
    content = slide_header(slide, "Offset = 4B")

    width = 700
    columns = 4
    row = content.box(horizontal=True)
    cols = [row.box(width=width // columns) for _ in range(columns)]

    address(cols, ("Number", "Tag", "Index", "Offset"),
            next=False,
            use_style=False)
    address(cols, ("A", "..100000", "000000", "000000"), next=False)
    address(cols, ("B", "..100000", "000000", "000100"), row=1)
    address(cols, ("C", "..100000", "000000", "001000"), row=2)
    address(cols, ("D", "..100000", "000000", "001100"), row=3)

    hash_dimension = 80
    (htable, hitems) = table(content.box(p_top=20), hash_size, hash_dimension,
                             hash_size // 2)
    for i in range(4):
        hitems[0].box(show="{}+".format(i + 1),
                      width=hash_dimension // 4,
                      height=hash_dimension).rect(bg_color=colors[i])

    list_wrapper = content.box(p_top=40)
    list_item(
        list_wrapper,
        show="next+").text("Same bucket, same cache line for each number")
    list_item(list_wrapper,
              show="next+").text("Most efficient, no space is wasted")

    slide = new_slide(slides)
    content = slide_header(slide, "Offset = 64B")

    row = content.box(horizontal=True)
    cols = [row.box(width=width // columns) for _ in range(columns)]

    address(cols, ("Number", "Tag", "Index", "Offset"),
            next=False,
            use_style=False)
    address(cols, ("A", "..100000", "000000", "000000"), next=False)
    address(cols, ("B", "..100000", "000001", "000000"), row=1)
    address(cols, ("C", "..100000", "000010", "000000"), row=2)
    address(cols, ("D", "..100000", "000011", "000000"), row=3)

    (htable, hitems) = table(content.box(p_top=20), hash_size, hash_dimension,
                             hash_size // 2)
    for i in range(4):
        hitems[i * 2].box(show="{}+".format(i + 1),
                          width=hash_dimension // 4,
                          height=hash_dimension,
                          x=0).rect(bg_color=colors[i])

    list_wrapper = content.box(p_top=40)
    list_item(list_wrapper,
              show="next+").text("Different bucket for each number")
    list_item(list_wrapper, show="next+").text("Wastes 60B in each cache line")
    list_item(list_wrapper,
              show="next+").text("Equally distributed among buckets")

    slide = new_slide(slides)
    content = slide_header(slide, "Offset = 4096B")

    row = content.box(horizontal=True)
    cols = [row.box(width=width // columns) for _ in range(columns)]

    address(cols, ("Number", "Tag", "Index", "Offset"),
            next=False,
            use_style=False)
    address(cols, ("A", "..100000", "000000", "000000"), next=False)
    address(cols, ("B", "..100001", "000000", "000000"), row=1)
    address(cols, ("C", "..100010", "000000", "000000"), row=2)
    address(cols, ("D", "..100011", "000000", "000000"), row=3)

    (htable, hitems) = table(content.box(p_top=20), hash_size, hash_dimension,
                             hash_size // 2)
    for i in range(4):
        hitems[i % 2].box(show="{}+".format(i + 1),
                          width=hash_dimension // 4,
                          height=hash_dimension,
                          x=0).rect(bg_color=colors[i])

    list_wrapper = content.box(p_top=40)
    list_item(list_wrapper, show="next+").text(
        "Same bucket, but different cache lines for each number!")
    list_item(list_wrapper,
              show="next+").text("Bucket full => evictions necessary")

    slide = new_slide(slides)
    content = slide_header(slide, "How to measure?")
    content.box().text("~tt{l1d.replacement}", style=s(size=48))
    content.box(
        p_top=20).text("How many times was a cache line loaded into L1?")

    if backup:
        bash(content.box(p_top=40, show="next+"),
             """$ perf stat -e l1d.replacement ./example1
4B    offset ->     149 558
4096B offset -> 426 218 383""",
             text_style=s(align="left"))
예제 #25
0
def shared_state(slides: Slides):
    slide = slides.new_slide()
    slide.update_style("code", s(size=38))
    content = slide_header(slide, "Spawning a thread")

    code(content.box(), "fn spawn<F: Fn + Send>(f: F)")

    content.box(height=20)
    content.box(show="next+").text("""Ownership of T can be transferred to another thread
only if T implements the ~emph{Send} trait""")

    content.box(height=20)
    content.box(show="next+").text("""Send is implemented automatically, unless the type
contains values that are not safe to be transferred between threads""", style=s(size=30))

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    content = slide_header(slide, "Shared state concurrency")
    content.box().text("Goal:", style=s(bold=True))
    list = content.box()
    list_item(list, show="next+").text("Spawn a thread")
    list_item(list, show="next+").text("Send a reference to some value to it")
    list_item(list, show="next+").text("Modify the value in the spawned thread")
    list_item(list, show="next+").text("Read the value in the original thread")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    (content, header) = slide_header(slide, "Shared state concurrency", True)
    box = header.box(width=160, y=80)
    box.image("imgs/meme-face-1.jpg")
    box.overlay(show="4").image("imgs/meme-face-2.jpg")

    code_step(content.box(width=800, height=350), """
let value = 5;
let p = &value;
thread::spawn(|| {
    println!("{}", *p);
});
""", 1, [(0, None, None, None, None),
         (0, 1, None, None, None),
         (0, 1, 2, 3, 4)], width=500)

    content.box(height=10)
    with_border(content, show="4+").box(height=220).image("imgs/concurrent-error-1.png")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    (content, header) = slide_header(slide, "Shared state concurrency", True)
    box = header.box(width=160, y=80)
    box.image("imgs/meme-face-2.jpg")
    box.overlay(show="4-5").image("imgs/meme-face-3.png")
    box.overlay(show="6+").image("imgs/meme-face-4.png")

    code_step(content.box(width=800, height=350), """
let p = Rc::new(5);
thread::spawn(|| {
    println!("{}", *p);
});
""", 1, [(0, None, None, None),
         (0, 1, None, None),
         (0, 1, 2, 3),
         (0, 1, 2, 3),
         (0, "thread::spawn(move || {", 2, 3)], width=500)

    border_box = content.box(width=1000, height=220)
    box = with_border(border_box.overlay(), show="4").box(width=800, height=180)
    box.box(show="4", height=220).image("imgs/concurrent-error-2.png")
    box = with_border(border_box.overlay(), show="6+").box(width=800, height=180)
    box.box(show="6+", width=900).image("imgs/concurrent-error-3.png")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    (content, header) = slide_header(slide, "Shared state concurrency", True)
    box = header.box(width=160, y=80)
    box.overlay(show="1-2").image("imgs/meme-face-6.jpg")
    box.overlay(show="3+").image("imgs/meme-face-5.jpg")

    content.box(height=60)
    code_step(content.box(width=800, height=260), """
let p = Arc::new(5);
thread::spawn(move || {
    println!("{}", *p);
});
println!("{}", *p);
""", 1, [(0, 1, 2, 3, None),
         (0, 1, 2, 3, 4),
         ], width=500)
    with_border(content, show="3+").box(width=1000).image(
        "imgs/concurrent-error-4.png")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    (content, header) = slide_header(slide, "Shared state concurrency", True)
    box = header.box(width=160, y=80, show="3+")
    box.image("imgs/meme-face-7.png")

    code_width = 800
    code(content.box(show="2+"), "fn clone(&self) -> Arc<T>;", width=code_width)

    content.box(height=20)
    code(content.box(), """
let p = Arc::new(5);
let tp = p.clone();
thread::spawn(move || {
    println!("{}", *tp);
});
println!("{}", *p);
""", width=code_width)

    content.box(height=10)
    content.box(show="2+").text("""Clone() creates a new Arc.
Multiple variables remove aliasing.""")
    content.box(show="3+").text("Arc only provides ~emph{read-only} access (shared borrow).")

    slide = slides.new_slide()
    slide.update_style("code", s(size=34))
    slide.set_style("code2", slide.get_style("code").compose(s(size=32)))
    content = slide_header(slide, "Shared state concurrency")

    code_width = 800
    code(content.box(show="4+"), """
// Mutex::lock
fn lock(&self) -> &mut T;""", code_style="code2", width=code_width)

    content.box(height=20)
    code_step(content.box(width=code_width, height=320), """
let p = Arc::new(Mutex::new(5));
let tp = p.clone();
thread::spawn(move || {
    *tp.lock() = 10;
});
println!("{}", *p.lock());""", "1", (
        ("                 Mutex::new(5)  ", None, None, None, None, None),
        (0, None, None, None, None, None),
        (0, 1, 2, 3, 4, 5)
    ), width=code_width)
예제 #26
0
def project(slides: Slides):
    slide = slides.new_slide()
    content = slide_header(slide, "Project management (Cargo)")
    content.box(height=400).image("imgs/cargo.png")

    slide = slides.new_slide()
    slide.update_style("code", s(size=30))

    content = slide_header(slide, "Using libraries")

    line = content.box(width="fill", horizontal=True)

    cargo = line.box(width="50%", p_right=50)
    cargo.box().text("Cargo.toml")
    cargo_code = code(
        cargo.box(), """
[package]
name = "hello_world"
version = "0.1.0"

[dependencies]
ibverbs = "0.4"
json = "1.0"
protobuf = "2.0"
""", "toml")

    main = line.box(width="50%", show="2+", y=0)
    main.box().text("main.rs")
    main_code = code(
        main.box(), """
use json::parse;

fn main() {
    parse("data.json");
}""")

    arrow = Arrow(20)
    p1 = cargo_code.line_box(5).p("100%", "50%")
    p2 = main_code.line_box(0).p(0, "50%")
    slide.box(show="2+").line(
        [p1.add(-40, 0), p1, p2.add(-10, 0)],
        stroke_width=5,
        color="orange",
        end_arrow=arrow)

    content.box(height=10)
    content.box(show="3+").text("More than 26k libraries available")

    slide = slides.new_slide()
    content = slide_header(slide, "Unified documentation")
    content.box(width=900).image("imgs/rust-docs.png")

    slide = slides.new_slide()
    content = slide_header(slide, "Integrated tooling")

    def cargo_line(parent, text, code, show="1+", **text_args):
        wrapper = parent.box(width="fill", horizontal=True, show=show)
        textbox = wrapper.box(width="50%")
        textbox = textbox.text(text, **text_args)

        codebox = wrapper.box(width="40%")
        bash(codebox, code, x=0, width="fill")
        return textbox

    wrapper = content.box(width="fill")
    cargo_line(wrapper, "Build", "$ cargo build", "1+")
    cargo_line(wrapper, "Run", "$ cargo run", "2+")

    slide = slides.new_slide()
    slide.update_style("code", s(size=40))
    content = slide_header(slide, "Integrated tooling (tests)")

    code(content.box(), """
#[test]
fn test_add() {
    assert_eq!(add(1, 2), 3);
}
""")

    content.box(height=20)
    bash(content.box(show="2+"), "$ cargo test")

    slide = slides.new_slide()
    slide.update_style("code", s(size=40))
    content = slide_header(slide, "Integrated tooling (benchmarks)")

    code(
        content.box(), """
#[bench]
fn bench_add_two(b: &mut Bencher) {
    b.iter(|| add_two(2));
}
""")

    content.box(height=20)
    bash(content.box(show="2+"), "$ cargo bench")

    slide = slides.new_slide()
    content = slide_header(slide, "Integrated tooling")
    wrapper = content.box(width="fill")
    cargo_line(wrapper, "Format", "$ cargo fmt")
    cargo_line(wrapper, "Lint", "$ cargo clippy", "next+")
    box = cargo_line(wrapper, "Publish to SC", "$ cargo publish", "next+")
    box = box.line_box(0)
    box.line([box.p("53%", "55%"), box.p("73%", "55%")], stroke_width=3)

    slide = slides.new_slide()
    slide.update_style("code", s(size=24))
    content = slide_header(slide, "Build scripts")
    content.box().text("build.rs")

    code_width = 940
    code_step(content.box(width=code_width, height=400),
              """
fn main() {
    // generate Protobuf objects
    protoc_rust::run("protobuf/message.proto", "src/protos");

    // generate C headers
    cbindgen::Builder::new()
      .generate()
      .write_to_file("bindings.h");
}
""",
              "1", ((0, 1, 2, 3, None, None, None, None, 8),
                    (0, 1, 2, 3, 4, 5, 6, 7, 8)),
              width=code_width)

    slide = slides.new_slide()
    content = slide_header(slide, "(interlude)")
    content.box(height=600).image("imgs/meme-cargo.jpg")
예제 #27
0
def denormals(slides: SlideDeck, backup: bool):
    if backup:
        slide = new_slide(slides)
        content = slide_header(slide, "Code (backup)")
        code(content.box(), """float F = static_cast<float>(std::stof(argv[1]));
std::vector<float> data(4 * 1024 * 1024, 1);

for (int r = 0; r < 100; r++)
{
    for (auto& item: data)
    {
        item *= F;
    }
}""")
        slide = new_slide(slides)
        content = slide_header(slide, "Result (backup)")
        content.box(width="50%").image("images/example2-time.png")

    slide = new_slide(slides)
    colors = ["#B22222", "#007944", "#0018AE"]
    styles = ["sign", "exponent", "significand"]
    for i, style in enumerate(styles):
        slide.set_style(style, s(color=colors[i], size=42))
    content = slide_header(slide, "Denormal floating point numbers")

    row = content.box(horizontal=True)
    box_dimension = 60

    def floating_point(wrapper: Box, colors, values):
        for i in range(len(colors)):
            box = wrapper.box(width=box_dimension, height=box_dimension)
            box.rect(color="black", bg_color=colors[i], stroke_width=2)
            box.text(str(values[i]), s(color="white", bold=True))

    floating_point(row, [colors[0]] + [colors[1]] * 5 + [colors[2]] * 10, [
        0,
        0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 1
    ])
    row_label = content.box(x=0, horizontal=True, show="next+")
    row_label.box(x=250, y=0).text("Zero exponent")
    row_label.box(x=670, y=0).text("Non-zero significand")

    content.box(p_top=80, width=700, height=50).image("images/float.svg")

    list_wrapper = content.box(p_top=20)
    list_item(list_wrapper, show="next+").text("Numbers close to zero")
    list_item(list_wrapper, show="last+").text("Hidden bit = 0, smaller bias")

    content.box(p_top=40, show="next+").text("Operations on denormal numbers are slow!", style=s(size=46))

    slide = new_slide(slides)
    content = slide_header(slide, "Floating point handling")
    content.box(height=600).image("images/haswell-diagram.png")
    content.box(width=130, height=32, x=800, y=52).rect(color=COLOR_FRONTEND, stroke_width=3)
    content.box(width=35, height=100, x=988, y=80).rect(color=COLOR_FRONTEND, stroke_width=3)
    content.box(width=80, height=165, x=212, y=335).rect(color=COLOR_BACKEND, stroke_width=3)

    slide = new_slide(slides)
    content = slide_header(slide, "How to measure?")
    content.box().text("~tt{fp_assist.any}", style=s(size=48))
    content.box(p_top=20).text("How many times the CPU switched to the microcode FP handler?")

    if backup:
        bash(content.box(p_top=40, show="next+"), """$ perf stat -e fp_assist.any ./example2
0   ->          0
0.3 -> 15 728 640""", text_style=s(align="left"))

    slide = new_slide(slides)
    slide.update_style("code", s(size=40))
    content = slide_header(slide, "How to fix it?")
    list_wrapper = content.box()
    list_item(list_wrapper).text("The nuclear option: ~tt{-ffast-math}")
    list_item(list_wrapper, level=1).text("Sacrifice correctness to gain more FP performance")
    list_item(list_wrapper, show="next+").text("Set CPU flags:")
    list_item(list_wrapper, level=1, show="last+").text("Flush-to-zero - treat denormal outputs as 0")
    list_item(list_wrapper, level=1, show="last+").text("Denormals-to-zero - treat denormal inputs as 0")

    code(list_wrapper.box(p_top=40, show="next+"), """_mm_setcsr(_mm_getcsr() | 0x8040);
// or
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
""")
예제 #28
0
def hw_complexity(slides: SlideDeck):
    slide = new_slide(slides)
    content = slide_header(slide, "Why should we care?")
    list_wrapper = content.box()
    list_item(list_wrapper).text("We write code for the C++ abstract machine")
    list_item(list_wrapper, show="next+").text(
        "Intel CPUs fulfill the contract of this abstract machine")
    list_item(list_wrapper, level=1,
              show="next+").text("But inside they can do whatever they want")
    list_item(list_wrapper, show="next+").text(
        "Understanding CPU trade-offs can get us more performance")

    slide = new_slide(slides)
    slide.update_style("code", s(size=50))
    content = slide_header(slide, "C++ abstract machine example")
    code(
        content.box(), """void foo(int* arr, int count)
{
    for (int i = 0; i < count; i++)
    {
        arr[i]++;
    }
}""")
    content.box(p_top=20).text("How fast are the individual array increments?",
                               s(size=40))

    slide = new_slide(slides)
    content = slide_header(slide, "Hardware effects")
    list_wrapper = content.box()
    list_item(list_wrapper).text(
        "Performance effects caused by a specific CPU/memory implementation")
    list_item(list_wrapper, show="next+").text(
        "Demonstrate some CPU/memory trade-off or assumption")
    list_item(list_wrapper,
              show="next+").text("Impossible to predict from (C++) code alone")

    slide = new_slide(slides)
    content = slide_header(slide, "Hardware is getting more and more complex")
    content.box(height=600).image("images/moores-law.png")
    content.box().text("Source: karlrupp.net", s(size=30))

    slide = new_slide(slides)
    content = slide_header(slide, "Microarchitecture (Haswell)")
    content.box(height=600).image("images/haswell-diagram.svg")
    # Heuristics, assumptions, fast paths/slow paths
    content.box().text(
        "Source: Intel Architectures Optimization Reference Manual",
        s(size=30))

    slide = new_slide(slides)
    content = slide_header(slide, "How bad is it?")
    list_wrapper = content.box()
    cpp = list_item(list_wrapper).box(horizontal=True)
    cpp.box().text("C++ 17 final draft: ")
    cpp.box(show="next+").text(" 1622 pages")
    intel = list_item(list_wrapper, show="next+").box(horizontal=True)
    intel.box().text("Intel x86 manual: ")
    intel.box(show="next+").text(" ~bold{5764} pages!")
    content.box(y=580).text(
        """http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
https://software.intel.com/sites/default/files/managed/9e/bc/64-ia-32-architectures-optimization-manual.pdf""",
        s(size=14, align="left"))

    slide = new_slide(slides)
    content = slide_header(slide, "Plan of attack")
    list_wrapper = content.box()
    list_item(list_wrapper).text("Show example C++ programs")
    list_item(list_wrapper, level=1,
              show="next+").text("short, (hopefully) comprehensible")
    list_item(list_wrapper, level=1,
              show="next+").text("compiled with ~tt{-O3}")
    list_item(list_wrapper,
              show="next+").text("Demonstrate weird performance behaviour")
    list_item(list_wrapper,
              show="next+").text("Let you guess what might cause it")
    list_item(list_wrapper, show="next+").text("Explain (a possible) cause")
    list_item(list_wrapper,
              show="next+").text("Show how to measure and fix it")
    list_item(
        list_wrapper, show="next+",
        p_top=20).text("Disclaimer #1: Everything will be Intel x86 specific")
    list_item(list_wrapper, show="next+").text(
        "Disclaimer #2: I'm not an expert on this and I may be wrong :-)")

    slide = new_slide(slides)
    slide.box().text("""Let's see some examples...""", s(bold=True, size=40))