CppCon 2019: Getting Allocators out of Our Way

Alisdair Meredith and Pablo Halpern

Allocators are great

std::pmr::monotoic_buffer_resource src(buffer, sizeof (buffer);
pmr::set. uniq(&src);

What if we could reduce the cost

goal: reduce cost of developign an allocator aware

What’s our problem?

Allocators bloat class interfaces

Performance in practice

  • Memory hiearchy: Cache; main, virtual
  • objects used together should be together

C++ allocatros

allocator_traits simplify allocators; mostly for users. Containers now have problems in their face

What about nested containers

  • In a vector<string> vector has one and each string potentiallly different allocator
  • scoped allocator model: container passes allocator into each item. Solves memory diffisuion
  • scoped_allocator_adapter
  • Allocator template policies interfere with vocabulary types

C++17: PMR: a simpler allocator model

  • Non-template std::pmr::memoryresource
  • The wrapper
  • class memory_resource
  • polomorphic_allocator
  • polymorphic_allocator<byte>
    • “One true” allocator vocabulary type
    • Only a template to be compatible for C++11
  • std::pmr::new_delete_resource()
    • Uses new/delete
    • always availabel
    • thread safe
  • unsynchronized_pool_resource
    • Pools of similar sized objects
    • good for dynamic data strucures
    • Single-threaded
    • avoids concurrency lock overhead
  • monootonic_buffer_resource
    • Ultra fast; single threaded, contiguous
    • for containers that grow monotonically
  • test resource
    • GitHub bloomberg/p1160

How to use allocators

class Student {
  pmr::string d_name;
pmr::string d_email
pmr::vector<int d_grades;

using allocator_type = pmr::polymovic_allocator<>; // Important

}

  • Overload each constructor having default_arguments
  • Variadic argument lists: allocator goes at the beginning

Rewriting std::unordered_map to use pmr allocators

  • Example with copying constructors. Lots of duplicated code

What about a language solution

  • Build on a strong foundation built up to C++17

HashMap<int,int> x {} using myAllocator;

  • using myAllocator

Bloomberg

  • Bloomberg vision 2020

CppCon 2019: Converting to C++20 Modules

Nathan Sidwell

Background

gcc.gnu.org/wiki/cxx-modules

g++ -fmodules-ts <a href="http://foo.cc">foo.cc</a>

  • Using timyxml2 library as example
  • On header, one .cpp, one makefile, one test program
  • When you compile a module you get a compiled module interface (CMI)
  • CME filenames related to module names is implementation defined as is where they are placed

A header unit

  • step through telling bild system: mapping file with rules for .h.gcm

CppCon 2019: Maintainability and Refactoring Impact of Higher-Level Design Features

Titus Winter

Multi-step refactoring

  • Basic atoms of refactoring

Rename a type

namespace oldns {class StringPiece ...}
namespace absl {class string_view ...copy StringPiece }
problems

or

namespace oldns {using StringPiece = absl::string_view}


break with forward declaration. forward declarations no real performance improement

also, ADL causes it to break

instead

namespace oldns {class StringPiece...}
namespace absl { using string_view = oldns::StringPiece;}

then deal with ADL problems

Find a way tso pre/post syntax/semancs can co-exist

Can we change functions?

  • small atoms: weaken preconditions; stregthen postconditions
  • large atomes: opposite

Changing return types

  • void to anything is doable
  • into to int64_t?

Can we change templates

Can we change concepts

  • Single-step: sure
  • Multi-step: No

CppCon 2019: Speed Is Found In The Minds of People

Andrei Alexandrescu

This was the best talk I saw at CppCon 2019

Sorting

  • What is quick-select?

Naive Implementations

  • qsort()

Better

  • qsort() with fallback to small_sort()

Investigating small_sort

  • Challenge: increase the THRESHOLD so sorting for 1000 elements work
  • binary search is slower. Branch prediction is reader
  • branchless even slower than binary
  • middle-out insertion support: better comparison and better moves (13%) but no performance change
  • stupid_small_sort(): make_heap(); insertion_sort(). Improvment in comparison, swaps; 9% slower
  • Blended cost: (C(N) + M(n) + k(D(n))/n. (compares + swaps + distance)

Timeline

  • 1990’s OOP
    • Inheritance and virtuals
  • 2000’s Generic Programing
    • Iterators and algos
  • 2020’s: design by introspection
    • Inspect and customize everything
    • We can’t implement the best sort with generic programming

Questions

  • Parametric complexity theory???

CppCon 2019: RAII and the Rule of Zero

Arthur O’Dwyer

Naive Vector

  • Use copy and swap for assignment. Example of recursive shared_ptr in Naivevector

NaiveVector &operator=(const NaiveVector &op2)
{
     NaiveVectory copy = op2;
     copy.swap(*this);
     return *this;
}

  • RAII and exception safety
    • struct RAIIPtr{} example
    • rule of three
    • We don’t want RAIIPtr to be copyable or assignamle

RAIIPTR(const RAIIPTR &) = delete;
RAIIPtr&operator=(const RAIIPtr &op2) = delete;

  • Defaulted special member functions
    • help your code to be self documenting

Rule of Three

If need a destructor then need copy and assignment operators

Rule of Zero

If your class does not directly manage any resource then strive to write no special member functions

But your own swap might improve performance

Introducing rvalue references

As a general rule, lvalue reference parameters do not bind to rvalues and rvalue reference parameters do not bind to lvalues. A const ref will bind to rvalue

Move constructor

Rule of Five

  • A destructor
  • copy constructor
  • move constructor to transfer ownership (for performance)
  • copy assignment operator
  • move assignment operator (for performance)

Copy-and-swap leads to duplication

NaiveVector& NaiveVector:operator=(NaiveVector &&rhs){
    NaiveVector copy(std::move(rhs));
    copy.swap(*this);
    return *this
}

By-value assignment operator

NaiveVector &operator=(NaiveVector copy)
{
   copy.swap(*this);
   return *this;
}

Rule of Four (and a half)

  • destructor
  • copy constructor
  • move constructor
  • by-value assignment operator
  • 1/2 (A nonmember swap() function, and idealy a member version too)

No longer

Cluster to rule of zero

  • Use std::unique_ptr() and we can default the move constructor
  • Other constructors are fine (including Allocator)

Examples of resource management

  • unique_ptr
  • shared_ptr
  • unique_lock
  • ifstream

CppCon 2019: C++ Modules: Guides for The Working Software developer

Gabriel Dos Reis

<h1>existing practice and Challenges</h1>
  • build time scalability of idiomatic C++

Fragile and Outdated Linking Mode

Program = collection of independently translated source files

Macros are a big problem

Modules accepted for C++20

What did we accomplish

  • Languate notions and constructs
    • Componentization
    • Isolation
    • Build throughput
    • Semantics-aware developer tools
  • With transition paths
    • Global module fragments
    • Header units

Module Definition (101)

export module BasicPlane.Figures;
export struct X ...

#include <iostream>
import BasicPlane.Figures;



How does it work?

  • The generated module generates .o plus metadata
  • If name is not exported it is not available

Efficient Pimpl

  • module :private;

Spreading Module Definition

  • “export module” gives module definition
  • “module X” is implementation

Spreading Module Interface

  • Primary module interface


export module BasicPlane.Figures;
export import :PointPart;
export import :RectPart;


export module BasicPlane.Figures:PointPart;
export struct Point {...};

export module BasicPlane.Figures:RectPart;
import :PointPart;
export struct Rectangle {...};
export int width(const Rectangle &); ...



Module Aggregation

export module BasicPlane;

export import BasicPlane.Figures;
export import BasicPlane.Transformations;
export import BasicPlane.Canvas;

Transitioning into modular world, and preparing for modules

module;
#include <sys/stat.h>

export module System.File.Ops;
namespace Sys {
    export stat stat_dierctory(const char *);

    export using ::chmod;
}

Heder Unit: An almost module

import <iostream>
import BasicPlane.Figures;

Kicking the Tires with MSVC, GCC, and clang

gcc.gnu.org/wiki/cxx-modules

Questions

  • Does
    import <iostream>

    obsolete pre-compiled headers? Yes

  • PCH vs usual include. 10 times faster

CppCon 2019: The basics of Move Semantics

Klaus Iglberger

  • Value semantics example with vector
  • v2 = createVector()
  • vs = std::move(v1);
  • simple defn of rvalue, lvalue: lvalue has a name you give it
  • operator=(vector&&rhs) rvalue reference
  • Xvalue: expiring value
  • do not use the moved from value (the xvalue)

New speicial member functions

  • Move constructor
    • Widget (Widget &&w) = default
  • Move assignment operator
    • Widget &operator=(Widget &&w) = default
  • “Rule of zero” if you can avoid defining default operations, good

Move constructor

Widget(Widget &&w)
: i(w.i)
, s (w.s) // bad!
{}

Widget(Widget &&w) noexcept
: i(std::move(w.i)) // not necessary but consistent
, s(std::move(w.s))
, pi(std::move (w.pi))
{
    w.pi = nullptr;
}

, pi(std::exchange(w.pi, nullptr))


  • Make move operations noexcept (Core Guidelines)

Move assignment operator

  • clean up all visible resources
  • Transfer the content of w into this
  • Leave w in a valid but undefined state
  • w = std::move(w);
  • phase 1: cleanup
  • phase 2: member-wise move
  • phase 3:

Generated

  • The default move operations are generate if no copy operation or destructor is user-define
  • The default copy operations are generated if no move operation is user-defined
  • Note: = default and =delete count as user-defined
  • Rule of five or rule of six: if you define or =delete any default operation define or =delete them all.