├── debian ├── compat ├── docs ├── source │ └── format ├── changelog ├── control ├── rules └── copyright ├── autogen.sh ├── scripts ├── .gitignore ├── Makefile.am ├── utils.py ├── list_plots.py └── remy_tool_runner.py ├── tests ├── RemyCC-2014-100x.dna ├── RemyCC-2013-delta0.1.dna ├── RemyCC-2013-delta1.dna ├── RemyCC-2013-delta10.dna ├── verify-2014-10.test ├── verify-2014-2.test ├── verify-2014-29.test ├── verify-2014-401.test ├── verify-2014-802.test ├── verify-2014-185.test ├── verify-2014-300.test ├── Makefile.am ├── verify-2014-100x-results.pm ├── maintain-2013-results └── run-plot-script.py ├── Makefile.am ├── src ├── random.hh ├── random.cc ├── link-templates.cc ├── poisson.hh ├── packet.hh ├── exponential.hh ├── aimd-templates.cc ├── fishbreeder.hh ├── fish-templates.cc ├── receiver.hh ├── receiver.cc ├── inspect-configrange.cc ├── inspect-simulationsdata.cc ├── aimd.hh ├── rat-templates.cc ├── ratbreeder.hh ├── fintree.hh ├── configrange.hh ├── whiskertree.hh ├── rat.hh ├── stochastic-loss.hh ├── sendergangofgangs.hh ├── evaluator.hh ├── fish.hh ├── fin.hh ├── senderdatapoint.hh ├── breeder.hh ├── link.hh ├── fin.cc ├── configrange.cc ├── Makefile.am ├── memoryrange.hh ├── utility.hh ├── rat.cc ├── fish.cc ├── fishbreeder.cc ├── aimd.cc ├── simulationresults.cc ├── whisker.hh ├── simulationresults.hh ├── ratbreeder.cc ├── memory.hh ├── sendergangofgangs.cc ├── delay.hh ├── action.hh ├── memoryrange.cc ├── whisker.cc ├── network.hh ├── memory.cc ├── network.cc ├── sendergang.hh ├── sender-runner.cc ├── remy-poisson.cc ├── breeder.cc ├── fintree.cc ├── scoring-example.cc ├── whiskertree.cc └── remy.cc ├── protobufs ├── problem.proto ├── answer.proto ├── Makefile.am ├── simulationresults.proto └── dna.proto ├── .gitignore ├── graph ├── image.cc ├── image.hh ├── Makefile.am ├── fader-templates.cc ├── fader.hh ├── display.hh ├── graph.hh ├── fader.cc ├── cairo_objects.hh ├── gl_objects.hh ├── cairo_objects.cc └── gl_objects.cc ├── .travis.yml ├── README.md └── configure.ac /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec autoreconf -fi 4 | -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | last 2 | results 3 | last-plots 4 | log-plots 5 | *.data 6 | *.out 7 | 8 | -------------------------------------------------------------------------------- /tests/RemyCC-2014-100x.dna: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tcpexmachina/remy/HEAD/tests/RemyCC-2014-100x.dna -------------------------------------------------------------------------------- /tests/RemyCC-2013-delta0.1.dna: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tcpexmachina/remy/HEAD/tests/RemyCC-2013-delta0.1.dna -------------------------------------------------------------------------------- /tests/RemyCC-2013-delta1.dna: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tcpexmachina/remy/HEAD/tests/RemyCC-2013-delta1.dna -------------------------------------------------------------------------------- /tests/RemyCC-2013-delta10.dna: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tcpexmachina/remy/HEAD/tests/RemyCC-2013-delta10.dna -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AM_DISTCHECK_CONFIGURE_FLAGS = --enable-graph --disable-silent-rules 2 | 3 | SUBDIRS = protobufs src tests scripts 4 | 5 | if BUILD_GRAPH 6 | SUBDIRS += graph 7 | endif 8 | -------------------------------------------------------------------------------- /src/random.hh: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_HH 2 | #define RANDOM_HH 3 | 4 | #include 5 | 6 | typedef std::default_random_engine PRNG; 7 | 8 | extern PRNG & global_PRNG(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /scripts/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_noinst_SCRIPTS = plot.py remy_tool_runner.py plot_log.py utils.py datautils.py 2 | 3 | EXTRA_DIST = originals/linkspeed100x.plot originals/linkspeedcubic.plot originals/linkspeedcubicsfqCoDel.plot 4 | -------------------------------------------------------------------------------- /src/random.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "random.hh" 6 | 7 | PRNG & global_PRNG( void ) 8 | { 9 | static PRNG generator( time( NULL ) ^ getpid() ); 10 | return generator; 11 | } 12 | -------------------------------------------------------------------------------- /tests/verify-2014-10.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 9.93, 6.0, 165 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /tests/verify-2014-2.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 2.21, 1.8, 346 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /tests/verify-2014-29.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 29.41, 18.0, 174 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /tests/verify-2014-401.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 401.4, 146, 151 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /tests/verify-2014-802.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 801.5, 156, 150 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /src/link-templates.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "link.hh" 4 | 5 | using namespace std; 6 | 7 | template 8 | void Link::tick( NextHop & next, const double & tickno ) 9 | { 10 | _pending_packet.tick( next, tickno ); 11 | 12 | if ( _pending_packet.empty() ) { 13 | if ( not _buffer.empty() ) { 14 | _pending_packet.accept( _buffer.front(), tickno ); 15 | _buffer.pop_front(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/verify-2014-185.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 185.04, 91.1, 157 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /tests/verify-2014-300.test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | BEGIN { push @INC, $ENV{ srcdir } } 4 | 5 | use strict; 6 | require q{verify-2014-100x-results.pm}; 7 | 8 | # these triplets of link speed => { raw throughput, raw delay } 9 | # are taken from the 2014 learnability paper Figure 2 for the "100x" RemyCC, 10 | # http://web.mit.edu/remy/learnability/resultslogarithmic.tar.gz 11 | 12 | enforce_constraint( 300.25, 133.1, 152 ); 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /protobufs/problem.proto: -------------------------------------------------------------------------------- 1 | import "dna.proto"; 2 | import "answer.proto"; 3 | 4 | package ProblemBuffers; 5 | 6 | message Problem { 7 | optional ProblemSettings settings = 1; 8 | repeated RemyBuffers.NetConfig configs = 2; 9 | optional RemyBuffers.WhiskerTree whiskers = 3; 10 | optional RemyBuffers.FinTree fins = 4; 11 | } 12 | 13 | message ProblemSettings { 14 | optional uint32 prng_seed = 11; 15 | optional uint32 tick_count = 12; 16 | } 17 | -------------------------------------------------------------------------------- /protobufs/answer.proto: -------------------------------------------------------------------------------- 1 | import "dna.proto"; 2 | 3 | package AnswerBuffers; 4 | 5 | message SenderResults { 6 | optional double throughput = 11; 7 | optional double delay = 12; 8 | } 9 | 10 | message ThroughputsDelays { 11 | optional RemyBuffers.NetConfig config = 21; 12 | repeated SenderResults results = 22; 13 | } 14 | 15 | message Outcome { 16 | repeated ThroughputsDelays throughputs_delays = 32; 17 | optional double score = 33; 18 | } 19 | -------------------------------------------------------------------------------- /src/poisson.hh: -------------------------------------------------------------------------------- 1 | #ifndef POISSON_HH 2 | #define POISSON_HH 3 | 4 | #include 5 | 6 | #include "random.hh" 7 | 8 | class Poisson 9 | { 10 | private: 11 | std::poisson_distribution<> distribution; 12 | 13 | PRNG & prng; 14 | 15 | public: 16 | Poisson( const double & rate, PRNG & s_prng ) : distribution( rate ), prng( s_prng ) {} 17 | 18 | int sample( void ) { return distribution( prng ); } 19 | 20 | Poisson & operator=( const Poisson & ) { assert( false ); return *this; } 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/packet.hh: -------------------------------------------------------------------------------- 1 | #ifndef PACKET_HH 2 | #define PACKET_HH 3 | 4 | class Packet 5 | { 6 | public: 7 | unsigned int src; 8 | unsigned int flow_id; 9 | double tick_sent, tick_received; 10 | int seq_num; 11 | 12 | Packet( const unsigned int & s_src, 13 | const unsigned int & s_flow_id, 14 | const double & s_tick_sent, 15 | const int & s_seq_num ) 16 | : src( s_src ), 17 | flow_id( s_flow_id ), tick_sent( s_tick_sent ), 18 | tick_received( -1 ), 19 | seq_num( s_seq_num ) 20 | {} 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/exponential.hh: -------------------------------------------------------------------------------- 1 | #ifndef EXPONENTIAL_HH 2 | #define EXPONENTIAL_HH 3 | 4 | #include 5 | 6 | #include "random.hh" 7 | 8 | class Exponential 9 | { 10 | private: 11 | std::exponential_distribution<> distribution; 12 | 13 | public: 14 | Exponential( const double & rate ) : distribution( rate ) {} 15 | 16 | void set_lambda( const double & rate ) 17 | { 18 | distribution = std::exponential_distribution<> ( rate ); 19 | } 20 | 21 | double sample( PRNG & prng ) { return distribution( prng ); } 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | ratatouille (0.1.2-1) trusty; urgency=low 2 | 3 | * Explicitly invoke rectangle exception in GLSL 1.3 4 | 5 | -- Keith Winstein Mon, 15 Sep 2014 13:31:37 -0500 6 | 7 | ratatouille (0.1.1-1) trusty; urgency=low 8 | 9 | * Downgrade OpenGL requirements for use under VMware Fusion 10 | 11 | -- Keith Winstein Mon, 15 Sep 2014 04:04:15 -0500 12 | 13 | ratatouille (0.1-1) trusty; urgency=low 14 | 15 | * First packaging. 16 | 17 | -- Keith Winstein Mon, 15 Sep 2014 02:35:00 -0500 18 | -------------------------------------------------------------------------------- /src/aimd-templates.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "aimd.hh" 5 | 6 | using namespace std; 7 | 8 | /* I am assuming this is going to be called as soon as packets_received 9 | is called */ 10 | template 11 | void Aimd::send( const unsigned int id, NextHop & next, const double & tickno ) 12 | { 13 | assert( int(_packets_sent) >= _largest_ack + 1 ); 14 | while ( int(_packets_sent) < _largest_ack + 1 + _the_window ) { 15 | Packet p( id, _flow_id, tickno, _packets_sent ); 16 | _packets_sent++; 17 | next.accept( p, tickno ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | .deps 4 | Makefile 5 | Makefile.in 6 | *.log 7 | *.trs 8 | 9 | /INSTALL 10 | /aclocal.m4 11 | /ar-lib 12 | /autom4te.cache 13 | /config.h 14 | /config.h.in 15 | /config.h.in~ 16 | /config.log 17 | /config.status 18 | /configure 19 | /depcomp 20 | /install-sh 21 | /missing 22 | /alfalfa-*.tar.gz 23 | /stamp-h1 24 | /test-driver 25 | 26 | /protobufs/*.pb.cc 27 | /protobufs/*.pb.h 28 | /protobufs/*_pb2.py 29 | /src/configuration 30 | /src/inspect-config 31 | /src/sender-runner 32 | /src/sender-logger 33 | /src/inspect-simulationsdata 34 | /src/remy 35 | /src/remy-poisson 36 | /graph/ratatouille 37 | /src/scoring-example 38 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: ratatouille 2 | Section: net 3 | Priority: optional 4 | Maintainer: Keith Winstein 5 | Build-Depends: debhelper (>= 7.0.50), autotools-dev, dh-autoreconf, pkg-config, protobuf-compiler, libprotobuf-dev, libgl-dev, libglfw3-dev, libglew-dev, libglu-dev, libcairo2-dev, libpango1.0-dev, libgtkmm-3.0-dev, libboost1.55-dev 6 | Standards-Version: 3.9.4.0 7 | Vcs-Git: git://github.com/keithw/remy.git 8 | Vcs-Browser: https://github.com/keithw/remy 9 | 10 | Package: ratatouille 11 | Architecture: any 12 | Depends: ${shlibs:Depends}, ${misc:Depends} 13 | Description: Demo of the Remy toolkit (http://mit.edu/remy) 14 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_check_SCRIPTS = verify-2014-100x-results.pm maintain-2013-results run-plot-script.py \ 2 | verify-2014-2.test \ 3 | verify-2014-10.test \ 4 | verify-2014-29.test \ 5 | verify-2014-185.test \ 6 | verify-2014-300.test \ 7 | verify-2014-401.test \ 8 | verify-2014-802.test 9 | 10 | EXTRA_DIST = RemyCC-2013-delta0.1.dna \ 11 | RemyCC-2013-delta10.dna \ 12 | RemyCC-2013-delta1.dna \ 13 | RemyCC-2014-100x.dna 14 | 15 | TESTS = maintain-2013-results \ 16 | verify-2014-2.test \ 17 | verify-2014-10.test \ 18 | verify-2014-29.test \ 19 | verify-2014-185.test \ 20 | verify-2014-300.test \ 21 | verify-2014-401.test \ 22 | verify-2014-802.test \ 23 | run-plot-script.py 24 | -------------------------------------------------------------------------------- /src/fishbreeder.hh: -------------------------------------------------------------------------------- 1 | #ifndef FISHBREEDER_HH 2 | #define FISHBREEDER_HH 3 | 4 | #include "breeder.hh" 5 | 6 | class FinImprover : public ActionImprover< FinTree, Fin > 7 | { 8 | protected: 9 | std::vector< Fin > get_replacements( Fin & action_to_improve ); 10 | 11 | public: 12 | FinImprover( const Evaluator< FinTree > & evaluator, const FinTree & fish, const double score_to_beat ) 13 | : ActionImprover< FinTree, Fin >( evaluator, fish, score_to_beat) {}; 14 | }; 15 | 16 | class FishBreeder : public Breeder< FinTree > 17 | { 18 | public: 19 | FishBreeder( const BreederOptions & s_options ) : Breeder( s_options ) {}; 20 | 21 | Evaluator< FinTree >::Outcome improve( FinTree & fins ); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /protobufs/Makefile.am: -------------------------------------------------------------------------------- 1 | source = dna.proto problem.proto answer.proto simulationresults.proto 2 | 3 | AM_CPPFLAGS = $(CXX11_FLAGS) $(protobuf_CFLAGS) 4 | 5 | SUFFIXES = .proto .pb.cc _pb2.py 6 | 7 | .proto.pb.cc: 8 | $(AM_V_GEN)$(PROTOC) --cpp_out=. -I$(srcdir) $< 9 | 10 | .proto_pb2.py: 11 | $(AM_V_GEN)$(PROTOC) --python_out=. -I$(srcdir) $< 12 | 13 | noinst_LIBRARIES = libremyprotos.a 14 | noinst_SCRIPTS = $(source:.proto=_pb2.py) 15 | 16 | libremyprotos_a_SOURCES = $(source) 17 | nodist_libremyprotos_a_SOURCES = $(source:.proto=.pb.cc) $(source:.proto=.pb.h) 18 | 19 | BUILT_SOURCES = $(source:.proto=.pb.cc) 20 | CLEANFILES = $(source:.proto=.pb.cc) $(source:.proto=.pb.h) $(source:.proto=_pb2.py) $(source:.proto=_pb2.pyc) 21 | -------------------------------------------------------------------------------- /src/fish-templates.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "fish.hh" 5 | 6 | using namespace std; 7 | 8 | template 9 | void Fish::send( const unsigned int id, NextHop & next, const double & tickno ) 10 | { 11 | if ( tickno < _next_send_time ) { return; } 12 | 13 | assert( _packets_sent >= _largest_ack + 1 ); 14 | 15 | if ( _lambda == 0 ) { 16 | /* initial lambda */ 17 | const Fin & current_fin( _fins.use_fin( _memory, _track ) ); 18 | _update_lambda( current_fin.lambda() ); 19 | } 20 | 21 | for ( unsigned int i = 0; i < _batch_size; i++ ) { 22 | Packet p( id, _flow_id, tickno, _packets_sent ); 23 | _packets_sent++; 24 | next.accept( p, tickno ); 25 | } 26 | _update_send_time( tickno ); 27 | } 28 | -------------------------------------------------------------------------------- /src/receiver.hh: -------------------------------------------------------------------------------- 1 | #ifndef RECEIVER_HH 2 | #define RECEIVER_HH 3 | 4 | #include 5 | 6 | #include "packet.hh" 7 | 8 | class Receiver 9 | { 10 | private: 11 | std::vector< std::vector< Packet > > _collector; 12 | void autosize( const unsigned int index ); 13 | 14 | public: 15 | Receiver(); 16 | 17 | void accept( const Packet & p, const double & tickno ) noexcept; 18 | const std::vector< Packet > & packets_for( const unsigned int src ) { return _collector[ src ]; } 19 | void clear( const unsigned int src ) { _collector[ src ].clear(); } 20 | bool readable( const unsigned int src ) const noexcept 21 | { return (src < _collector.size()) && (!_collector[ src ].empty()); } 22 | 23 | double next_event_time( const double & tickno ) const; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/receiver.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "receiver.hh" 5 | 6 | Receiver::Receiver() 7 | : _collector() 8 | { 9 | } 10 | 11 | void Receiver::accept( const Packet & p, const double & tickno ) noexcept 12 | { 13 | autosize( p.src ); 14 | 15 | _collector[ p.src ].push_back( p ); 16 | _collector[ p.src ].back().tick_received = tickno; 17 | } 18 | 19 | void Receiver::autosize( const unsigned int index ) 20 | { 21 | if ( index >= _collector.size() ) { 22 | _collector.resize( index + 1 ); 23 | } 24 | } 25 | 26 | double Receiver::next_event_time( const double & tickno ) const 27 | { 28 | for ( const auto & x : _collector ) { 29 | if ( not x.empty() ) { 30 | return tickno; 31 | } 32 | } 33 | return std::numeric_limits::max(); 34 | } 35 | -------------------------------------------------------------------------------- /graph/image.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "image.hh" 5 | 6 | using namespace std; 7 | 8 | Image::Image( const unsigned int width, 9 | const unsigned int height, 10 | const unsigned int stride_pixels ) 11 | : width_( width ), 12 | height_( height ), 13 | stride_pixels_( stride_pixels ), 14 | pixels_() 15 | { 16 | if ( stride_pixels_ < width ) { 17 | throw runtime_error( "invalid stride in Image constructor" ); 18 | } 19 | pixels_.reserve( stride_pixels * height ); 20 | for ( unsigned int y = 0; y < height; y++ ) { 21 | for ( unsigned int x = 0; x < stride_pixels; x++ ) { 22 | pixels_.emplace_back(); 23 | } 24 | } 25 | } 26 | 27 | void Image::clear( void ) 28 | { 29 | memset( raw_pixels(), 255, pixels_.size() * sizeof( Pixel ) ); 30 | } 31 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | # Through Autoconf we set hardening flags that are actually more aggressive 13 | # than the Ubuntu defaults, but they conflict with same. 14 | export DEB_BUILD_MAINT_OPTIONS = hardening=-stackprotector 15 | -include /usr/share/dpkg/buildflags.mk 16 | 17 | %: 18 | dh $@ --with autoreconf 19 | 20 | override_dh_auto_configure: 21 | dh_auto_configure -- \ 22 | --disable-silent-rules 23 | -------------------------------------------------------------------------------- /graph/image.hh: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_HH 2 | #define IMAGE_HH 3 | 4 | #include 5 | #include 6 | 7 | typedef uint32_t Pixel; 8 | 9 | class Image 10 | { 11 | unsigned int width_, height_, stride_pixels_; 12 | 13 | std::vector pixels_; 14 | 15 | public: 16 | Image( const unsigned int width, 17 | const unsigned int height, 18 | const unsigned int stride_pixels ); 19 | 20 | std::pair size( void ) const { return std::make_pair( width_, height_ ); } 21 | const std::vector & pixels( void ) const { return pixels_; } 22 | unsigned char * raw_pixels( void ) { return reinterpret_cast( &pixels_.front() ); } 23 | 24 | unsigned int stride_pixels( void ) const { return stride_pixels_; } 25 | unsigned int stride_bytes( void ) const { return stride_pixels_ * sizeof( Pixel ); } 26 | void clear( void ); 27 | }; 28 | 29 | #endif /* IMAGE_HH */ 30 | -------------------------------------------------------------------------------- /src/inspect-configrange.cc: -------------------------------------------------------------------------------- 1 | #include "dna.pb.h" 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | int main( int argc, char *argv[] ) 8 | { 9 | ifstream file; 10 | 11 | if ( argc != 2 ) { 12 | cerr << "This tool prints the contents of an input configuration file." << endl; 13 | cerr << "There must be exactly one argument, the name of the config file." << endl; 14 | exit( 1 ); 15 | } 16 | 17 | string filename( argv[1] ); 18 | file.open( filename ); 19 | if ( !file.is_open() ) { 20 | cerr << "Could not open file " << filename << endl; 21 | exit( 1 ); 22 | } 23 | 24 | RemyBuffers::ConfigRange configrange; 25 | if ( !configrange.ParseFromIstream( &file ) ) { 26 | cerr << "Could not parse file " << filename << endl; 27 | exit( 1 ); 28 | } 29 | 30 | file.close(); 31 | 32 | cout << configrange.DebugString() << endl; 33 | return 0; 34 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | services: docker 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-4.9 11 | - protobuf-compiler 12 | - libprotobuf-dev 13 | - libgl1-mesa-dev 14 | - libglew-dev 15 | - libcairo2-dev 16 | - libpango1.0-dev 17 | - libgtkmm-3.0-dev 18 | - libboost-dev 19 | - libglu1-mesa-dev 20 | - python3-matplotlib 21 | - python3-numpy 22 | 23 | before_script: 24 | - sudo -E add-apt-repository -y 'deb http://ppa.launchpad.net/keithw/glfw3/ubuntu trusty main' 25 | - sudo apt-get update -qq 26 | - sudo apt-get install -y --force-yes libglfw3-dev 27 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 99 28 | 29 | script: 30 | - ./autogen.sh 31 | - ./configure 32 | - make -j4 distcheck || (cat remy-0.1/_build/tests/test-suite.log && exit 1) 33 | 34 | notifications: 35 | slack: keithw:pcPsG04nsEuSMDabFhPDOyYq 36 | -------------------------------------------------------------------------------- /src/inspect-simulationsdata.cc: -------------------------------------------------------------------------------- 1 | #include "simulationresults.pb.h" 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | int main( int argc, char *argv[] ) 8 | { 9 | ifstream file; 10 | 11 | if ( argc != 2 ) { 12 | cerr << "This tool prints the contents of a simulations data file." << endl; 13 | cerr << "There must be exactly one argument, the name of the simulations data file." << endl; 14 | exit( 1 ); 15 | } 16 | 17 | string filename( argv[1] ); 18 | file.open( filename ); 19 | if ( !file.is_open() ) { 20 | cerr << "Could not open file " << filename << endl; 21 | exit( 1 ); 22 | } 23 | 24 | SimulationResultBuffers::SimulationsData simulations_data; 25 | if ( !simulations_data.ParseFromIstream( &file ) ) { 26 | cerr << "Could not parse file " << filename << endl; 27 | exit( 1 ); 28 | } 29 | 30 | file.close(); 31 | 32 | cout << simulations_data.DebugString() << endl; 33 | return 0; 34 | } -------------------------------------------------------------------------------- /graph/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = $(CXX11_FLAGS) -I$(srcdir)/../src -I../protobufs $(GL_CFLAGS) $(GLFW_CFLAGS) $(GLEW_CFLAGS) $(GLU_CFLAGS) $(PANGOCAIRO_CFLAGS) $(GTKMM_CFLAGS) 2 | 3 | noinst_LIBRARIES = libfader.a 4 | libfader_a_SOURCES = fader.cc 5 | libfader_a_CXXFLAGS = $(PICKY_CXXFLAGS) -Wno-effc++ -Wno-deprecated-declarations 6 | 7 | noinst_LIBRARIES += libgraphics.a 8 | libgraphics_a_CXXFLAGS = $(PICKY_CXXFLAGS) 9 | 10 | libgraphics_a_SOURCES = gl_objects.hh gl_objects.cc \ 11 | display.hh display.cc \ 12 | image.hh image.cc \ 13 | cairo_objects.hh cairo_objects.cc \ 14 | graph.hh graph.cc \ 15 | fader.hh fader-templates.cc bb100.hh 16 | 17 | bin_PROGRAMS = 18 | 19 | if BUILD_GRAPH 20 | bin_PROGRAMS += ratatouille 21 | ratatouille_LDADD = libfader.a libgraphics.a ../src/libremycore.a ../protobufs/libremyprotos.a -lm $(protobuf_LIBS) $(GL_LIBS) $(GLFW_LIBS) $(GLEW_LIBS) $(GLU_LIBS) $(PANGOCAIRO_LIBS) $(GTKMM_LIBS) 22 | ratatouille_CXXFLAGS = $(PICKY_CXXFLAGS) 23 | ratatouille_SOURCES = ratatouille.cc 24 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) 25 | endif 26 | 27 | -------------------------------------------------------------------------------- /src/aimd.hh: -------------------------------------------------------------------------------- 1 | #ifndef AIMD_HH 2 | #define AIMD_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "packet.hh" 9 | 10 | class Aimd 11 | { 12 | private: 13 | unsigned int _packets_sent, _packets_received; 14 | 15 | /* _the_window is the congestion window */ 16 | double _the_window; 17 | unsigned int _flow_id; 18 | 19 | /* Largest ACK so far */ 20 | int _largest_ack; 21 | 22 | /* Are we in Slow Start? */ 23 | bool _slow_start; 24 | 25 | /* Track multiple losses within a window, DCCP style (RFC 4341 Page 6) */ 26 | double _last_loss; 27 | double _rtt_at_loss; 28 | 29 | public: 30 | Aimd(); 31 | 32 | void packets_received( const std::vector< Packet > & packets ); 33 | void reset( const double & tickno ); /* start new flow */ 34 | 35 | template 36 | void send( const unsigned int id, NextHop & next, const double & tickno ); 37 | 38 | Aimd & operator=( const Aimd & ) { assert( false ); return *this; } 39 | 40 | double next_event_time( const double & tickno ) const; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/rat-templates.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rat.hh" 5 | 6 | using namespace std; 7 | 8 | template 9 | void Rat::send( const unsigned int id, NextHop & next, const double & tickno, 10 | const unsigned int packets_sent_cap ) 11 | { 12 | assert( int( _packets_sent ) >= _largest_ack + 1 ); 13 | 14 | if ( _the_window == 0 ) { 15 | /* initial window and intersend time */ 16 | const Whisker & current_whisker( _whiskers.use_whisker( _memory, _track ) ); 17 | _the_window = current_whisker.window( _the_window ); 18 | _intersend_time = current_whisker.intersend(); 19 | } 20 | 21 | if ( (int( _packets_sent ) < _largest_ack + 1 + _the_window) 22 | and (_last_send_time + _intersend_time <= tickno) ) { 23 | 24 | /* Have we reached the end of the flow for now? */ 25 | if ( _packets_sent >= packets_sent_cap ) { 26 | return; 27 | } 28 | 29 | Packet p( id, _flow_id, tickno, _packets_sent ); 30 | _packets_sent++; 31 | _memory.packet_sent( p ); 32 | next.accept( p, tickno ); 33 | _last_send_time = tickno; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /graph/fader-templates.cc: -------------------------------------------------------------------------------- 1 | #include "fader.hh" 2 | 3 | using namespace std; 4 | 5 | template 6 | void GTKFader::update( NetworkType & network ) 7 | { 8 | unique_lock ul( mutex_ ); 9 | 10 | for ( unsigned int i = 0; i < network.senders().gang1().count_senders(); i++ ) { 11 | if ( remy_.get()[ i ] == network.senders().gang1().sender( i ).sending ) { 12 | continue; 13 | } 14 | 15 | if ( remy_.get()[ i ] ) { 16 | network.mutable_senders().mutable_gang1().mutable_sender( i ).switch_on( network.tickno() ); 17 | } else { 18 | network.mutable_senders().mutable_gang1().mutable_sender( i ).switch_off( network.tickno(), 1 ); 19 | } 20 | } 21 | 22 | for ( unsigned int i = 0; i < network.senders().gang2().count_senders(); i++ ) { 23 | if ( aimd_.get()[ i ] == network.senders().gang2().sender( i ).sending ) { 24 | continue; 25 | } 26 | 27 | if ( aimd_.get()[ i ] ) { 28 | network.mutable_senders().mutable_gang2().mutable_sender( i ).switch_on( network.tickno() ); 29 | } else { 30 | network.mutable_senders().mutable_gang2().mutable_sender( i ).switch_off( network.tickno(), 1 ); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ratbreeder.hh: -------------------------------------------------------------------------------- 1 | #ifndef RATBREEDER_HH 2 | #define RATBREEDER_HH 3 | 4 | #include "breeder.hh" 5 | 6 | struct WhiskerImproverOptions 7 | { 8 | bool optimize_window_increment = true; 9 | bool optimize_window_multiple = true; 10 | bool optimize_intersend = true; 11 | }; 12 | 13 | class WhiskerImprover : public ActionImprover< WhiskerTree, Whisker > 14 | { 15 | protected: 16 | WhiskerImproverOptions _options; 17 | 18 | std::vector< Whisker > get_replacements( Whisker & action_to_improve ); 19 | 20 | public: 21 | WhiskerImprover( const Evaluator< WhiskerTree > & evaluator, const WhiskerTree & rat, const WhiskerImproverOptions & options, 22 | const double score_to_beat ) 23 | : ActionImprover< WhiskerTree, Whisker >( evaluator, rat, score_to_beat), 24 | _options( options ) {}; 25 | }; 26 | 27 | class RatBreeder : public Breeder< WhiskerTree > 28 | { 29 | private: 30 | WhiskerImproverOptions _whisker_options; 31 | 32 | public: 33 | RatBreeder( const BreederOptions & s_options, const WhiskerImproverOptions & s_whisker_options ) 34 | : Breeder( s_options ), _whisker_options( s_whisker_options ) {}; 35 | 36 | Evaluator< WhiskerTree >::Outcome improve( WhiskerTree & whiskers ); 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/fintree.hh: -------------------------------------------------------------------------------- 1 | #ifndef FINTREE_HH 2 | #define FINTREE_HH 3 | 4 | #include 5 | 6 | #include "configrange.hh" 7 | #include "fin.hh" 8 | #include "memoryrange.hh" 9 | #include "dna.pb.h" 10 | 11 | class FinTree { 12 | private: 13 | MemoryRange _domain; 14 | 15 | std::vector< FinTree > _children; 16 | std::vector< Fin > _leaf; 17 | 18 | const Fin * fin( const Memory & _memory ) const; 19 | 20 | public: 21 | FinTree(); 22 | 23 | FinTree( const Fin & fin, const bool bisect ); 24 | 25 | const Fin & use_fin( const Memory & _memory, const bool track ) const; 26 | 27 | bool replace( const Fin & w ); 28 | bool replace( const Fin & src, const FinTree & dst ); 29 | const Fin * most_used( const unsigned int max_generation ) const; 30 | 31 | void reset_counts( void ); 32 | void promote( const unsigned int generation ); 33 | void reset_generation( void ); 34 | 35 | std::string str( void ) const; 36 | std::string str( const unsigned int total ) const; 37 | unsigned int total_fin_queries( void ) const; 38 | 39 | unsigned int num_children( void ) const; 40 | 41 | bool is_leaf( void ) const; 42 | 43 | RemyBuffers::FinTree DNA( void ) const; 44 | FinTree( const RemyBuffers::FinTree & dna ); 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /scripts/utils.py: -------------------------------------------------------------------------------- 1 | # This file needs to be compatible with both Python 2 and Python 3. 2 | 3 | import os 4 | import time 5 | from socket import gethostname 6 | import subprocess 7 | import json 8 | 9 | def log_arguments(argsfile, args): 10 | if os.path.isdir(argsfile): 11 | dir_given = True 12 | argsfile = open(os.path.join(argsfile, "args.json"), "w") 13 | else: 14 | dir_given = False 15 | 16 | jsondict = { 17 | "start-time": time.asctime(), 18 | "machine-name": gethostname(), 19 | "args": vars(args), 20 | "git": {}, 21 | } 22 | try: 23 | jsondict["git"]["describe"] = subprocess.check_output(['git', 'describe', '--always', '--dirty']).decode().strip() 24 | except subprocess.CalledProcessError: 25 | pass 26 | json.dump(jsondict, argsfile, indent=2, sort_keys=True) 27 | 28 | if dir_given: 29 | argsfile.close() 30 | 31 | def make_output_dir(dirname, default_parent, default_child, symlink): 32 | if dirname is None: 33 | dirname = os.path.join(default_parent, default_child) 34 | if os.path.islink(symlink): 35 | os.unlink(symlink) 36 | os.symlink(dirname, symlink) 37 | if not os.path.exists(dirname): 38 | os.makedirs(dirname) 39 | return dirname 40 | -------------------------------------------------------------------------------- /src/configrange.hh: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_RANGE_HH 2 | #define CONFIG_RANGE_HH 3 | 4 | #include "dna.pb.h" 5 | class Range 6 | { 7 | public: 8 | double low; 9 | double high; 10 | double incr; 11 | Range ( void ) 12 | : low( 1 ), 13 | high ( 1 ), 14 | incr ( 0 ) 15 | {} 16 | Range( double lo, double hi, double inc ) 17 | : low( lo ), 18 | high( hi ), 19 | incr( inc ) 20 | {} 21 | Range & set_low( const double lo ) { low = lo; return *this; } 22 | Range & set_high( const double hi ) { high = hi; return *this; } 23 | Range & set_incr( const double inc ) { incr = inc; return *this; } 24 | 25 | Range( RemyBuffers::Range range ) 26 | : low( range.low() ), 27 | high( range.high() ), 28 | incr ( range.incr() ) 29 | {} 30 | bool isOne( void ) const 31 | { 32 | return ( ( low == high ) || ( incr == 0 ) ); 33 | } 34 | }; 35 | 36 | class ConfigRange 37 | { 38 | public: 39 | Range link_ppt; 40 | Range rtt; 41 | Range mean_on_duration; 42 | Range mean_off_duration; 43 | Range num_senders; 44 | Range buffer_size; 45 | unsigned int simulation_ticks; 46 | Range stochastic_loss_rate; 47 | 48 | ConfigRange( void ); 49 | ConfigRange( RemyBuffers::ConfigRange configrange ); 50 | RemyBuffers::ConfigRange DNA( void ) const; 51 | }; 52 | 53 | #endif // CONFIG_RANGE_HH 54 | -------------------------------------------------------------------------------- /src/whiskertree.hh: -------------------------------------------------------------------------------- 1 | #ifndef WHISKERTREE_HH 2 | #define WHISKERTREE_HH 3 | 4 | #include 5 | 6 | #include "configrange.hh" 7 | #include "whisker.hh" 8 | #include "memoryrange.hh" 9 | #include "dna.pb.h" 10 | 11 | class WhiskerTree { 12 | private: 13 | MemoryRange _domain; 14 | 15 | std::vector< WhiskerTree > _children; 16 | std::vector< Whisker > _leaf; 17 | 18 | const Whisker * whisker( const Memory & _memory ) const; 19 | 20 | public: 21 | WhiskerTree(); 22 | 23 | WhiskerTree( const Whisker & whisker, const bool bisect ); 24 | 25 | const Whisker & use_whisker( const Memory & _memory, const bool track ) const; 26 | 27 | void use_window( const unsigned int win ) const; 28 | 29 | bool replace( const Whisker & w ); 30 | bool replace( const Whisker & src, const WhiskerTree & dst ); 31 | const Whisker * most_used( const unsigned int max_generation ) const; 32 | 33 | void reset_counts( void ); 34 | void promote( const unsigned int generation ); 35 | void reset_generation( void ); 36 | 37 | std::string str( void ) const; 38 | std::string str( const unsigned int total ) const; 39 | unsigned int total_whisker_queries( void ) const; 40 | 41 | unsigned int num_children( void ) const; 42 | 43 | bool is_leaf( void ) const; 44 | 45 | RemyBuffers::WhiskerTree DNA( void ) const; 46 | WhiskerTree( const RemyBuffers::WhiskerTree & dna ); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/rat.hh: -------------------------------------------------------------------------------- 1 | #ifndef RAT_HH 2 | #define RAT_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "packet.hh" 9 | #include "whiskertree.hh" 10 | #include "memory.hh" 11 | #include "simulationresults.pb.h" 12 | 13 | class Rat 14 | { 15 | private: 16 | const WhiskerTree & _whiskers; 17 | Memory _memory; 18 | 19 | unsigned int _packets_sent, _packets_received; 20 | 21 | bool _track; 22 | 23 | double _last_send_time; 24 | 25 | int _the_window; 26 | double _intersend_time; 27 | 28 | unsigned int _flow_id; 29 | int _largest_ack; 30 | 31 | public: 32 | Rat( WhiskerTree & s_whiskers, const bool s_track=false ); 33 | 34 | void packets_received( const std::vector< Packet > & packets ); 35 | void reset( const double & tickno ); /* start new flow */ 36 | 37 | template 38 | void send( const unsigned int id, NextHop & next, const double & tickno, 39 | const unsigned int packets_sent_cap = std::numeric_limits::max() ); 40 | 41 | const WhiskerTree & whiskers( void ) const { return _whiskers; } 42 | 43 | Rat & operator=( const Rat & ) { assert( false ); return *this; } 44 | 45 | double next_event_time( const double & tickno ) const; 46 | 47 | const unsigned int & packets_sent( void ) const { return _packets_sent; } 48 | 49 | SimulationResultBuffers::SenderState state_DNA() const; 50 | 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/stochastic-loss.hh: -------------------------------------------------------------------------------- 1 | #ifndef STOCHASTICLOSS_HH 2 | #define STOCHASTICLOSS_HH 3 | 4 | #include "packet.hh" 5 | #include 6 | #include 7 | #include 8 | #include "exponential.hh" 9 | 10 | class StochasticLoss 11 | { 12 | private: 13 | std::deque< std::tuple< double, Packet > > _buffer; 14 | double _loss_rate; 15 | PRNG & _prng; 16 | std::bernoulli_distribution _distr; 17 | 18 | public: 19 | StochasticLoss( const double & rate, PRNG &prng ) : _buffer(), _loss_rate( rate ), _prng( prng ), _distr() { _distr = std::bernoulli_distribution( rate );} 20 | template 21 | void tick( NextHop & next, const double & tickno ) 22 | { 23 | // pops items off buffer and sends them to the next item 24 | while ( (!_buffer.empty())) { 25 | next.accept(std::get< 1 >(_buffer.front()), tickno); 26 | _buffer.pop_front(); 27 | } 28 | } 29 | void accept( const Packet & p, const double & tickno ) noexcept 30 | { 31 | if (!(_distr( _prng ))) { 32 | _buffer.emplace_back( tickno, p ); 33 | } 34 | } 35 | 36 | 37 | double next_event_time( const double & tickno ) const 38 | { 39 | if ( _buffer.empty() ) { 40 | return std::numeric_limits::max(); 41 | } 42 | assert( std::get< 0 >( _buffer.front() ) >= tickno ); 43 | return std::get< 0 >( _buffer.front() ); 44 | } 45 | 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /graph/fader.hh: -------------------------------------------------------------------------------- 1 | #ifndef FADER_HH 2 | #define FADER_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class GTKFader 9 | { 10 | std::atomic link_rate_ { 1 }; 11 | std::atomic rtt_ { 150 }; 12 | std::atomic time_increment_ { 0.65 / 60.0 }; /* a little slower than real time by default */ 13 | std::atomic horizontal_size_ { 10 }; 14 | std::atomic buffer_size_ { 1000 }; 15 | std::atomic autoscale_ { false }; 16 | std::atomic autoscale_all_ { false }; 17 | std::atomic quit_ { false }; 18 | 19 | std::unique_ptr[]> remy_; 20 | std::unique_ptr[]> aimd_; 21 | 22 | std::mutex mutex_ {}; 23 | 24 | public: 25 | GTKFader( const unsigned int & num_senders ); 26 | 27 | template 28 | void update( NetworkType & network ); 29 | 30 | double link_rate( void ) const { return link_rate_; } 31 | double rtt( void ) const { return rtt_; } 32 | double time_increment( void ) const { return time_increment_; } 33 | double horizontal_size( void ) const { return horizontal_size_; } 34 | double buffer_size( void ) const { return buffer_size_; } 35 | bool autoscale( void ) { bool ret = autoscale_; autoscale_ = false; return ret; } 36 | bool autoscale_all( void ) { bool ret = autoscale_all_; autoscale_all_ = false; return ret; } 37 | bool quit( void ) const { return quit_; } 38 | }; 39 | 40 | #endif /* FADER_HH */ 41 | -------------------------------------------------------------------------------- /src/sendergangofgangs.hh: -------------------------------------------------------------------------------- 1 | #ifndef SENDERGANGOFGANGS_HH 2 | #define SENDERGANGOFGANGS_HH 3 | 4 | #include 5 | #include 6 | #include "receiver.hh" 7 | #include "senderdatapoint.hh" 8 | 9 | template 10 | class SenderGangofGangs 11 | { 12 | private: 13 | Gang1Type gang1_; 14 | Gang2Type gang2_; 15 | 16 | public: 17 | SenderGangofGangs( const Gang1Type & gang1, 18 | const Gang2Type & gang2 ); 19 | 20 | unsigned int count_active_senders( void ) const; 21 | 22 | unsigned int count_senders( void ) const { return gang1_.count_senders() + gang2_.count_senders(); } 23 | 24 | void switch_senders( const unsigned int num_sending, const double & tickno ); 25 | 26 | template 27 | void run_senders( NextHop & next, Receiver & rec, 28 | const unsigned int num_sending, 29 | const double & tickno ); 30 | 31 | template 32 | void tick( NextHop & next, Receiver & rec, const double & tickno ); 33 | 34 | double utility( void ) const; 35 | 36 | std::vector< std::pair< double, double > > throughputs_delays( void ) const; 37 | std::vector< SenderDataPoint > statistics_for_log( void ) const; 38 | 39 | double next_event_time( const double & tickno ) const; 40 | 41 | const Gang1Type & gang1( void ) const { return gang1_; } 42 | const Gang2Type & gang2( void ) const { return gang2_; } 43 | 44 | Gang1Type & mutable_gang1( void ) { return gang1_; } 45 | Gang2Type & mutable_gang2( void ) { return gang2_; } 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/evaluator.hh: -------------------------------------------------------------------------------- 1 | #ifndef EVALUATOR_HH 2 | #define EVALUATOR_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "random.hh" 8 | #include "whiskertree.hh" 9 | #include "fintree.hh" 10 | #include "network.hh" 11 | #include "problem.pb.h" 12 | #include "answer.pb.h" 13 | 14 | template 15 | class Evaluator 16 | { 17 | public: 18 | class Outcome 19 | { 20 | public: 21 | double score; 22 | std::vector< std::pair< NetConfig, std::vector< std::pair< double, double > > > > throughputs_delays; 23 | T used_actions; 24 | 25 | Outcome() : score( 0 ), throughputs_delays(), used_actions() {} 26 | 27 | Outcome( const AnswerBuffers::Outcome & dna ); 28 | 29 | AnswerBuffers::Outcome DNA( void ) const; 30 | }; 31 | 32 | private: 33 | const unsigned int _prng_seed; 34 | unsigned int _tick_count; 35 | 36 | std::vector< NetConfig > _configs; 37 | 38 | ProblemBuffers::Problem _ProblemSettings_DNA ( void ) const; 39 | 40 | public: 41 | Evaluator( const ConfigRange & range ); 42 | 43 | ProblemBuffers::Problem DNA( const T & actions ) const; 44 | 45 | Outcome score( T & run_actions, 46 | const bool trace = false, 47 | const double carefulness = 1) const; 48 | 49 | static Evaluator::Outcome parse_problem_and_evaluate( const ProblemBuffers::Problem & problem ); 50 | 51 | static Outcome score( T & run_actions, 52 | const unsigned int prng_seed, 53 | const std::vector & configs, 54 | const bool trace, 55 | const unsigned int ticks_to_run ); 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/fish.hh: -------------------------------------------------------------------------------- 1 | #ifndef FISH_HH 2 | #define FISH_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "packet.hh" 9 | #include "memory.hh" 10 | #include "random.hh" 11 | #include "exponential.hh" 12 | #include "fintree.hh" 13 | #include "simulationresults.pb.h" 14 | 15 | class Fish 16 | { 17 | private: 18 | const FinTree & _fins; 19 | Memory _memory; 20 | 21 | int _packets_sent, _packets_received; 22 | double _last_send_time; 23 | double _next_send_time; 24 | unsigned int _flow_id; 25 | int _largest_ack; 26 | bool _track; 27 | 28 | double _lambda; 29 | double _max_intersend; 30 | unsigned int _batch_size; 31 | 32 | PRNG _prng; 33 | Exponential _distribution; 34 | 35 | void _update_send_time( const double tickno ); 36 | 37 | void _update_lambda( const double lambda ); 38 | 39 | public: 40 | Fish( const FinTree & fins, const unsigned int s_prng_seed, const bool s_track ); 41 | 42 | void packets_received( const std::vector< Packet > & packets ); 43 | void reset( const double & tickno ); /* start new flow */ 44 | 45 | const FinTree & fins( void ) const { return _fins; } 46 | 47 | template 48 | void send( const unsigned int id, NextHop & next, const double & tickno ); 49 | 50 | Fish & operator=( const Fish & ) { assert( false ); return *this; } 51 | 52 | double next_event_time( const double & tickno ) const; 53 | 54 | const int & packets_sent( void ) const { return _packets_sent; } 55 | 56 | SimulationResultBuffers::SenderState state_DNA() const; 57 | 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/fin.hh: -------------------------------------------------------------------------------- 1 | #ifndef FIN_HH 2 | #define FIN_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "memoryrange.hh" 8 | #include "action.hh" 9 | #include "dna.pb.h" 10 | 11 | class Fin : public Action { 12 | private: 13 | double _lambda; 14 | public: 15 | Fin( const Fin & other ); 16 | Fin( const double s_rate, const MemoryRange & s_domain ) : Action( s_domain ), _lambda( s_rate) {}; 17 | Fin( const MemoryRange & s_domain ) : Fin( get_optimizer().lambda.default_value, s_domain ) {}; 18 | virtual ~Fin() {}; 19 | 20 | const double & lambda( void ) const { return _lambda; } 21 | 22 | std::vector< Fin > next_generation( void ) const; 23 | 24 | std::string str( const unsigned int total=1 ) const; 25 | 26 | RemyBuffers::Fin DNA( void ) const; 27 | Fin( const RemyBuffers::Fin & dna ); 28 | 29 | void round( void ); 30 | 31 | bool operator==( const Fin & other ) const { return (_lambda == other._lambda) && (_domain == other._domain); } 32 | 33 | friend size_t hash_value( const Fin & Fin ); 34 | 35 | struct OptimizationSettings 36 | { 37 | OptimizationSetting< double > lambda; 38 | 39 | RemyBuffers::OptimizationSettings DNA( void ) const 40 | { 41 | RemyBuffers::OptimizationSettings ret; 42 | 43 | ret.mutable_lambda()->CopyFrom( lambda.DNA() ); 44 | 45 | return ret; 46 | } 47 | }; 48 | 49 | static const OptimizationSettings & get_optimizer( void ) { 50 | static OptimizationSettings default_settings { 51 | { 0.01, 30, 0.01, 3, 4, 5 }, /* lambda */ 52 | }; 53 | return default_settings; 54 | } 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/senderdatapoint.hh: -------------------------------------------------------------------------------- 1 | #ifndef SENDER_DATA_POINT_HH 2 | #define SENDER_DATA_POINT_HH 3 | 4 | #include "simulationresults.pb.h" 5 | #include "dna.pb.h" 6 | #include "memory.hh" 7 | 8 | /** 9 | * Represents the data that is logged relating to a single sender at a single 10 | * point in time. Since this class is intended to do nothing than hold data, 11 | * it just takes protobuf objects directly from relevant classes. 12 | */ 13 | class SenderDataPoint 14 | { 15 | 16 | public: 17 | 18 | SimulationResultBuffers::SenderDataPoint DNA( void ) const 19 | { 20 | SimulationResultBuffers::SenderDataPoint ret; 21 | ret.mutable_sender_state()->CopyFrom( sender_state ); 22 | ret.mutable_utility_data()->CopyFrom( utility_data ); 23 | ret.set_sending( sending ); 24 | ret.set_packets_in_flight( packets_in_flight ); 25 | return ret; 26 | } 27 | 28 | SenderDataPoint( SimulationResultBuffers::SenderState sender_state, 29 | SimulationResultBuffers::UtilityData utility_data, 30 | bool sending ) : 31 | sender_state( sender_state ), 32 | utility_data( utility_data ), 33 | sending( sending ), 34 | packets_in_flight( 0 ) {}; 35 | 36 | void set_packets_in_flight( unsigned int _packets ) 37 | { 38 | packets_in_flight = _packets; 39 | } 40 | 41 | private: 42 | 43 | // composite protobufs from other objects 44 | SimulationResultBuffers::SenderState sender_state; 45 | SimulationResultBuffers::UtilityData utility_data; 46 | 47 | // information directly stored in this object 48 | bool sending = false; 49 | unsigned int packets_in_flight = 0; 50 | }; 51 | 52 | #endif // SENDER_DATA_POINT_HH 53 | -------------------------------------------------------------------------------- /src/breeder.hh: -------------------------------------------------------------------------------- 1 | #ifndef BREEDER_HH 2 | #define BREEDER_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "configrange.hh" 10 | #include "evaluator.hh" 11 | 12 | struct BreederOptions 13 | { 14 | ConfigRange config_range = ConfigRange(); 15 | }; 16 | 17 | template 18 | class ActionImprover 19 | { 20 | protected: 21 | const double MAX_PERCENT_ERROR = 0.05; 22 | const Evaluator< T > eval_; 23 | 24 | T tree_; 25 | 26 | std::unordered_map< A, double, boost::hash< A > > eval_cache_ {}; 27 | 28 | double score_to_beat_; 29 | 30 | virtual std::vector< A > get_replacements( A & action_to_improve ) = 0; 31 | 32 | void evaluate_replacements(const std::vector< A > &replacements, 33 | std::vector< std::pair< const A &, std::future< std::pair< bool, double > > > > &scores, 34 | const double carefulness); 35 | 36 | std::vector< A > early_bail_out(const std::vector< A > &replacements, 37 | const double carefulness, const double quantile_to_keep); 38 | 39 | public: 40 | ActionImprover( const Evaluator< T > & evaluator, const T & tree, 41 | const double score_to_beat ); 42 | virtual ~ActionImprover() {}; 43 | 44 | double improve( A & action_to_improve ); 45 | }; 46 | 47 | template 48 | class Breeder 49 | { 50 | protected: 51 | BreederOptions _options; 52 | 53 | void apply_best_split( T & tree, const unsigned int generation ) const; 54 | 55 | public: 56 | Breeder( const BreederOptions & s_options ) : _options( s_options ) {}; 57 | virtual ~Breeder() {}; 58 | 59 | virtual typename Evaluator< T >::Outcome improve( T & whiskers ) = 0; 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/link.hh: -------------------------------------------------------------------------------- 1 | #ifndef LINK_HH 2 | #define LINK_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "packet.hh" 8 | #include "delay.hh" 9 | 10 | class Link 11 | { 12 | private: 13 | std::deque< Packet > _buffer; 14 | 15 | Delay _pending_packet; 16 | 17 | unsigned int _limit; 18 | 19 | public: 20 | Link( const double s_rate, 21 | const unsigned int s_limit ) 22 | : _buffer(), _pending_packet( 1.0 / s_rate ), _limit( s_limit ) {} 23 | 24 | void accept( const Packet & p, const double & tickno ) noexcept { 25 | if ( _pending_packet.empty() ) { 26 | _pending_packet.accept( p, tickno ); 27 | } else { 28 | if ( _limit and _buffer.size() < _limit ) { 29 | _buffer.push_back( p ); 30 | } 31 | } 32 | } 33 | 34 | template 35 | void tick( NextHop & next, const double & tickno ); 36 | 37 | double next_event_time( const double & tickno ) const { return _pending_packet.next_event_time( tickno ); } 38 | 39 | std::vector packets_in_flight( const unsigned int num_senders ) const 40 | { 41 | std::vector ret( num_senders ); 42 | for ( const auto & x : _buffer ) { 43 | ret.at( x.src )++; 44 | } 45 | std::vector propagating = _pending_packet.packets_in_flight( num_senders ); 46 | for ( unsigned int i = 0; i < num_senders; i++ ) { 47 | ret.at( i ) += propagating.at( i ); 48 | } 49 | return ret; 50 | } 51 | 52 | void set_rate( const double rate ) { _pending_packet.set_delay( 1.0 / rate ); } 53 | double rate( void ) const { return 1.0 / _pending_packet.delay(); } 54 | void set_limit( const unsigned int limit ) 55 | { 56 | _limit = limit; 57 | while ( _buffer.size() > _limit ) { 58 | _buffer.pop_back(); 59 | } 60 | } 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: remy 3 | Source: http://github.com/keithw/remy 4 | 5 | Files: * 6 | Copyright: 2013 Keith Winstein 7 | License: GPL-3+ 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | . 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | . 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | . 21 | In addition, as a special exception, the copyright holders give 22 | permission to link the code of portions of this program with the 23 | OpenSSL library under certain conditions as described in each 24 | individual source file, and distribute linked combinations including 25 | the two. 26 | . 27 | You must obey the GNU General Public License in all respects for all 28 | of the code used other than OpenSSL. If you modify file(s) with this 29 | exception, you may extend this exception to your version of the 30 | file(s), but you are not obligated to do so. If you do not wish to do 31 | so, delete this exception statement from your version. If you delete 32 | this exception statement from all source files in the program, then 33 | also delete it here. 34 | . 35 | On Debian systems, the complete text of the GNU General 36 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 37 | -------------------------------------------------------------------------------- /tests/verify-2014-100x-results.pm: -------------------------------------------------------------------------------- 1 | use strict; 2 | 3 | sub score { 4 | my ( $link_speed ) = @_; 5 | 6 | my $packets_per_tick = $link_speed / 10; 7 | my $RTT = 150; 8 | my $number_of_sources = 2; 9 | 10 | my @result = qx{../src/sender-runner if=$ENV{'srcdir'}/RemyCC-2014-100x.dna nsrc=$number_of_sources link=$packets_per_tick rtt=$RTT on=1000 off=1000} or die q{Can't exec sender-runner}; 11 | 12 | my @raw_throughputs_and_delays; 13 | 14 | for ( @result ) { 15 | if ( m{^sender: } ) { 16 | print; 17 | my ( $normalized_throughput, $delay_ratio ) = m{\[tp=(.*?), del=(.*?)\]}; 18 | my $raw_throughput = $normalized_throughput * .75 * $link_speed; 19 | # factor of 0.75 is to reverse effect of normalization to an equal share of the link 20 | # (half the time, the sender is running with no competition, so factor=1, 21 | # and half the time running with one competitor, so factor=0.5) 22 | my $raw_delay = $delay_ratio * $RTT; 23 | push @raw_throughputs_and_delays, [ $raw_throughput, $raw_delay ]; 24 | } 25 | } 26 | 27 | if ( scalar @raw_throughputs_and_delays != $number_of_sources ) { 28 | die qq{sender-runner did not give $number_of_sources results}; 29 | } 30 | 31 | return @raw_throughputs_and_delays; 32 | } 33 | 34 | sub assert_in_range { 35 | my ( $val, $min, $max ) = @_; 36 | unless ( $min < $val and $val < $max ) { 37 | die qq{Constraint violated: value $val not in interval [$min..$max]}; 38 | } 39 | } 40 | 41 | sub enforce_constraint { 42 | my ( $link_speed, $expected_throughput, $expected_delay ) = @_; 43 | 44 | my @raw_throughputs_and_delays = score( $link_speed ); 45 | 46 | for ( @raw_throughputs_and_delays ) { 47 | assert_in_range( $_->[ 0 ], $expected_throughput * .95, $expected_throughput * 1.05 ); 48 | assert_in_range( $_->[ 1 ], $expected_delay * .95, $expected_delay * 1.05 ); 49 | } 50 | } 51 | 52 | 1; 53 | -------------------------------------------------------------------------------- /src/fin.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "fin.hh" 7 | 8 | using namespace std; 9 | 10 | Fin::Fin( const Fin & other ) 11 | : Action( other ), 12 | _lambda( other._lambda ) 13 | { 14 | } 15 | 16 | Fin::Fin( const RemyBuffers::Fin & dna ) 17 | : Action ( dna.domain() ), 18 | _lambda( dna.lambda() ) 19 | { 20 | } 21 | 22 | RemyBuffers::Fin Fin::DNA( void ) const 23 | { 24 | RemyBuffers::Fin ret; 25 | 26 | ret.set_lambda( _lambda ); 27 | ret.mutable_domain()->CopyFrom( _domain.DNA() ); 28 | 29 | return ret; 30 | } 31 | 32 | vector< Fin > Fin::next_generation( void ) const 33 | { 34 | vector< Fin> ret; 35 | 36 | auto lambda_alternatives = get_optimizer().lambda.alternatives( _lambda, true ); 37 | 38 | printf("Alternatives: lambda %f to %f\n", 39 | *(min_element(lambda_alternatives.begin(), lambda_alternatives.end())), 40 | *(max_element(lambda_alternatives.begin(), lambda_alternatives.end())) 41 | ); 42 | 43 | for ( const auto & alt_lambda : lambda_alternatives ) { 44 | Fin new_fin{ *this }; 45 | new_fin._generation++; 46 | 47 | new_fin._lambda = alt_lambda; 48 | new_fin.round(); 49 | 50 | ret.push_back( new_fin ); 51 | } 52 | 53 | return ret; 54 | } 55 | 56 | string Fin::str( const unsigned int total ) const 57 | { 58 | char tmp[ 256 ]; 59 | snprintf( tmp, 256, "{%s} gen=%u usage=%.4f => (lambda=%f)", 60 | _domain.str().c_str(), _generation, double( _domain.count() ) / double( total ), _lambda ); 61 | return tmp; 62 | } 63 | 64 | void Fin::round( void ) 65 | { 66 | _lambda = (1.0/10000.0) * int( 10000 * _lambda ); 67 | } 68 | 69 | size_t hash_value( const Fin & fin ) 70 | { 71 | size_t seed = 0; 72 | boost::hash_combine( seed, fin._lambda ); 73 | boost::hash_combine( seed, fin._domain ); 74 | 75 | return seed; 76 | } -------------------------------------------------------------------------------- /src/configrange.cc: -------------------------------------------------------------------------------- 1 | #include "configrange.hh" 2 | 3 | using namespace std; 4 | 5 | static RemyBuffers::Range pair_to_range( const Range &p ) 6 | { 7 | RemyBuffers::Range ret; 8 | ret.set_low( p.low ); 9 | ret.set_high( p.high ); 10 | ret.set_incr( p.incr ); 11 | return ret; 12 | } 13 | 14 | ConfigRange::ConfigRange( void ) : 15 | link_ppt( Range() ), 16 | rtt( Range() ), 17 | mean_on_duration( Range() ), 18 | mean_off_duration( Range() ), 19 | num_senders( Range() ), 20 | buffer_size( Range() ), 21 | simulation_ticks( 1000000 ), 22 | stochastic_loss_rate( Range().set_low(0).set_high(0).set_incr(0) ) 23 | { 24 | } 25 | 26 | ConfigRange::ConfigRange( RemyBuffers::ConfigRange input_config ) : 27 | link_ppt( Range( input_config.link_packets_per_ms() ) ), 28 | rtt( Range( input_config.rtt() ) ), 29 | mean_on_duration( Range( input_config.mean_on_duration() ) ), 30 | mean_off_duration( Range( input_config.mean_off_duration() ) ), 31 | num_senders( Range( input_config.num_senders() ) ), 32 | buffer_size( Range( input_config.buffer_size() ) ), 33 | simulation_ticks( input_config.simulation_ticks() ), 34 | stochastic_loss_rate( Range( input_config.stochastic_loss_rate() ) ) 35 | { 36 | } 37 | 38 | RemyBuffers::ConfigRange ConfigRange::DNA( void ) const 39 | { 40 | RemyBuffers::ConfigRange ret; 41 | ret.mutable_link_packets_per_ms()->CopyFrom( pair_to_range( link_ppt) ); 42 | ret.mutable_rtt()->CopyFrom( pair_to_range( rtt ) ); 43 | ret.mutable_num_senders()->CopyFrom( pair_to_range( num_senders ) ); 44 | ret.mutable_mean_on_duration()->CopyFrom( pair_to_range( mean_on_duration ) ); 45 | ret.mutable_mean_off_duration()->CopyFrom( pair_to_range( mean_off_duration ) ); 46 | ret.mutable_buffer_size()->CopyFrom( pair_to_range( buffer_size ) ); 47 | ret.set_simulation_ticks( simulation_ticks ); 48 | ret.mutable_stochastic_loss_rate()->CopyFrom( pair_to_range( stochastic_loss_rate ) ); 49 | return ret; 50 | } 51 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = $(CXX11_FLAGS) -I../protobufs 2 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) 3 | LDADD = ../protobufs/libremyprotos.a -lm $(protobuf_LIBS) 4 | 5 | bin_PROGRAMS = remy remy-poisson sender-runner sender-logger scoring-example configuration inspect-config inspect-simulationsdata 6 | 7 | common_source = delay.hh evaluator.cc evaluator.hh \ 8 | exponential.hh link.hh link-templates.cc stochastic-loss.hh \ 9 | memory.cc memory.hh memoryrange.cc memoryrange.hh \ 10 | network.cc network.hh packet.hh poisson.hh \ 11 | random.cc random.hh rat.cc rat.hh rat-templates.cc \ 12 | receiver.cc receiver.hh sendergang.cc sendergang.hh \ 13 | senderdatapoint.hh \ 14 | sendergangofgangs.cc sendergangofgangs.hh \ 15 | utility.hh whisker.cc whisker.hh whiskertree.cc whiskertree.hh \ 16 | aimd-templates.cc aimd.cc aimd.hh \ 17 | configrange.hh configrange.cc \ 18 | simulationresults.hh simulationresults.cc \ 19 | action.hh fin.hh fin.cc fintree.cc fintree.hh \ 20 | fish.hh fish.cc fish-templates.cc 21 | 22 | noinst_LIBRARIES = libremycore.a 23 | libremycore_a_SOURCES = $(common_source) 24 | 25 | remy_SOURCES = $(common_source) remy.cc ratbreeder.cc ratbreeder.hh \ 26 | breeder.cc breeder.hh 27 | 28 | remy_poisson_SOURCES = $(common_source) remy-poisson.cc breeder.cc breeder.hh fishbreeder.cc fishbreeder.hh 29 | 30 | sender_runner_SOURCES = $(common_source) sender-runner.cc 31 | 32 | sender_logger_SOURCES = $(common_source) sender-logger.cc 33 | 34 | scoring_example_SOURCES = $(common_source) scoring-example.cc 35 | 36 | configuration_SOURCES = input-configrange.cc 37 | 38 | inspect_config_SOURCES = inspect-configrange.cc 39 | 40 | inspect_simulationsdata_SOURCES = inspect-simulationsdata.cc 41 | 42 | -------------------------------------------------------------------------------- /graph/display.hh: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_HH 2 | #define DISPLAY_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "gl_objects.hh" 8 | 9 | class Display 10 | { 11 | static const std::string shader_source_scale_from_pixel_coordinates; 12 | static const std::string shader_source_passthrough_texture; 13 | static const std::string shader_source_solid_color; 14 | 15 | struct CurrentContextWindow 16 | { 17 | GLFWContext glfw_context_ = {}; 18 | Window window_; 19 | 20 | CurrentContextWindow( const unsigned int width, const unsigned int height, 21 | const std::string & title ); 22 | } current_context_window_; 23 | 24 | VertexShader scale_from_pixel_coordinates_ = { shader_source_scale_from_pixel_coordinates }; 25 | FragmentShader passthrough_texture_ = { shader_source_passthrough_texture }; 26 | FragmentShader solid_color_ = { shader_source_solid_color }; 27 | 28 | Program texture_shader_program_ = {}; 29 | Program solid_color_shader_program_ = {}; 30 | 31 | Texture texture_; 32 | 33 | VertexArrayObject texture_shader_array_object_ = {}; 34 | VertexArrayObject solid_color_array_object_ = {}; 35 | 36 | VertexBufferObject screen_corners_ = {}; 37 | VertexBufferObject other_vertices_ = {}; 38 | 39 | public: 40 | Display( const unsigned int width, const unsigned int height, 41 | const std::string & title ); 42 | 43 | void draw( const Image & image ); 44 | void draw( const float red, const float green, const float blue, const float alpha, 45 | const float width, 46 | const float cutoff, 47 | const std::deque> & vertices, 48 | const std::function(const std::pair &)> & transform ); 49 | void clear( void ); 50 | 51 | void repaint( void ); 52 | 53 | void swap( void ); 54 | 55 | const Window & window( void ) const { return current_context_window_.window_; } 56 | 57 | void resize( const std::pair & target_size ); 58 | }; 59 | 60 | #endif /* DISPLAY_HH */ 61 | -------------------------------------------------------------------------------- /graph/graph.hh: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_HH 2 | #define GRAPH_HH 3 | 4 | #include "display.hh" 5 | #include "cairo_objects.hh" 6 | 7 | class Graph 8 | { 9 | Display display_; 10 | Cairo cairo_; 11 | Pango pango_; 12 | 13 | Pango::Font tick_font_; 14 | Pango::Font label_font_; 15 | 16 | struct YLabel 17 | { 18 | int height; 19 | Pango::Text text; 20 | float intensity; 21 | }; 22 | 23 | std::deque> x_tick_labels_; 24 | std::vector y_tick_labels_; 25 | std::vector> colors_; 26 | std::vector>> data_points_; 27 | 28 | Pango::Text x_label_; 29 | Pango::Text y_label_; 30 | 31 | std::string info_string_; 32 | Pango::Text info_; 33 | 34 | float bottom_, top_; 35 | 36 | float project_height( const float x ) const { return ( x - bottom_ ) / ( top_ - bottom_ ); } 37 | float chart_height( const float x, const unsigned int window_height ) const 38 | { 39 | return (window_height - 40) * (.825*(1-project_height( x ))+.025) + (.825 * 40); 40 | } 41 | 42 | Cairo::Pattern horizontal_fadeout_; 43 | 44 | public: 45 | Graph( const unsigned int num_lines, 46 | const unsigned int initial_width, const unsigned int initial_height, const std::string & title, 47 | const float min_y, const float max_y ); 48 | 49 | void set_window( const float t, const float logical_width ); 50 | void add_data_point( const unsigned int num, const float t, const float y ) { 51 | if ( not data_points_.at( num ).empty() ) { 52 | if ( y == data_points_.at( num ).back().second ) { 53 | return; 54 | } 55 | } 56 | 57 | data_points_.at( num ).emplace_back( t, y ); 58 | } 59 | 60 | void set_color( const unsigned int num, const float red, const float green, const float blue, 61 | const float alpha ); 62 | 63 | bool blocking_draw( const float t, const float logical_width, const float min_y, const float max_y ); 64 | 65 | void set_info( const std::string & info ); 66 | }; 67 | 68 | #endif /* GRAPH_HH */ 69 | -------------------------------------------------------------------------------- /src/memoryrange.hh: -------------------------------------------------------------------------------- 1 | #ifndef MEMORYRANGE_HH 2 | #define MEMORYRANGE_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "memory.hh" 11 | #include "dna.pb.h" 12 | 13 | typedef RemyBuffers::MemoryRange::Axis Axis; 14 | 15 | class MemoryRange { 16 | private: 17 | Memory _lower, _upper; 18 | 19 | /* _active_axis specifies the group of signals in Memory used by the sender. 20 | For example, Fish only uses signal rtt_diff, while Rat uses four signals: 21 | rec_send_ewma, rec_rec_ewma, rtt_ratio and slow_rec_rec_rewma. */ 22 | std::vector< Axis > _active_axis; 23 | 24 | mutable std::vector< boost::accumulators::accumulator_set< Memory::DataType, 25 | boost::accumulators::stats< 26 | boost::accumulators::tag::median > > > _acc; 27 | mutable unsigned int _count; 28 | 29 | public: 30 | MemoryRange( const Memory & s_lower, const Memory & s_upper, 31 | std::vector< Axis > s_active = { RemyBuffers::MemoryRange::SEND_EWMA, RemyBuffers::MemoryRange::REC_EWMA, RemyBuffers::MemoryRange::RTT_RATIO, RemyBuffers::MemoryRange::SLOW_REC_EWMA } ) 32 | : _lower( s_lower ), _upper( s_upper ), _active_axis( s_active ), _acc( Memory::datasize ), _count( 0 ) 33 | {} 34 | 35 | std::vector< MemoryRange > bisect( void ) const; 36 | Memory range_median( void ) const; 37 | 38 | bool contains( const Memory & query ) const; 39 | 40 | void use( void ) const { _count++; } 41 | unsigned int count( void ) const { return _count; } 42 | void reset_count( void ) const { _count = 0; } 43 | 44 | void track( const Memory & query ) const; 45 | 46 | bool operator==( const MemoryRange & other ) const; 47 | 48 | std::string str( void ) const; 49 | 50 | RemyBuffers::MemoryRange DNA( void ) const; 51 | MemoryRange( const RemyBuffers::MemoryRange & dna ); 52 | 53 | friend size_t hash_value( const MemoryRange & mr ); 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/tcpexmachina/remy.svg?branch=master)](https://travis-ci.org/tcpexmachina/remy) 2 | 3 | Remy: TCP ex Machina (computer-generated congestion control) 4 | ============================================================ 5 | 6 | Remy is an optimization tool to develop new TCP congestion-control 7 | schemes, given prior knowledge about the network it will encounter 8 | and an objective to optimize for. 9 | 10 | It is described further at the Web site for [TCP ex 11 | Machina](http://web.mit.edu/remy). A research paper on Remy was 12 | published at the ACM SIGCOMM 2013 annual conference. 13 | 14 | Basic usage: 15 | 16 | * Remy requires a C++11 compiler to compile, e.g. gcc 4.6 or 17 | contemporary clang++. You will also need the Google 18 | protobuf-compiler and the Boost C++ libraries. 19 | 20 | * From the version-control repository checkout, run `./autogen.sh`, 21 | `./configure`, and `make` to build Remy. 22 | 23 | * Run `./remy` to design a RemyCC (congestion-control algorithm) for 24 | the default scenario, with link speed drawn uniformly between 10 and 25 | 20 Mbps, minRTT drawn uniformly between 100 and 200 ms, the maximum 26 | degree of multiplexing drawn uniformly between 1 and 32, and each 27 | sender "on" for an exponentially-distributed amount of time (mean 5 28 | s) and off for durations drawn from the same distribution. 29 | 30 | * Use the of= argument to have Remy save its RemyCCs to disk. It will 31 | save every step of the iteration. 32 | 33 | * Use the if= argument to get Remy to read previous RemyCCs as the 34 | starting point for optimization. 35 | 36 | * The `sender-runner` tool will execute saved RemyCCs. The filename 37 | should be set with a `if=` argument. It also accepts `link=` to set 38 | the link speed (in packets per millisecond), `rtt=` to set the RTT, 39 | and `nsrc=` to set the maximum degree of multiplexing. 40 | 41 | If you have any questions, please visit [Remy's Web 42 | site](http://web.mit.edu/remy) or e-mail `remy at mit dot edu`. 43 | 44 | -- Keith Winstein -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | AC_INIT([remy], [0.1], [remy@mit.edu]) 6 | AM_INIT_AUTOMAKE([foreign]) 7 | AC_CONFIG_SRCDIR([src/remy.cc]) 8 | AC_CONFIG_HEADERS([config.h]) 9 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 10 | 11 | # Checks for programs. 12 | AC_PROG_CXX 13 | AC_PROG_RANLIB 14 | 15 | AC_PATH_PROG([PROTOC], [protoc], []) 16 | AS_IF([test x"$PROTOC" = x], 17 | [AC_MSG_ERROR([cannot find protoc, the Protocol Buffers compiler])]) 18 | 19 | # Checks for libraries. 20 | PKG_CHECK_MODULES([protobuf], [protobuf]) 21 | 22 | # Add compiler flags 23 | CXX11_FLAGS="-std=c++11 -pthread" 24 | AC_SUBST([CXX11_FLAGS]) 25 | 26 | PICKY_CXXFLAGS="-Wall -Wpedantic -Wextra -Weffc++ -Werror" 27 | AC_SUBST([PICKY_CXXFLAGS]) 28 | 29 | AC_ARG_ENABLE([graph], 30 | [AS_HELP_STRING([--enable-graph], 31 | [Enable live graph (ratatouille) @<:@no@:>@])], 32 | [build_graph="yes"], 33 | [build_graph="no"]) 34 | AM_CONDITIONAL([BUILD_GRAPH], [test x"$build_graph" != xno]) 35 | 36 | # Checks for libraries. 37 | AM_COND_IF( [BUILD_GRAPH], 38 | [ 39 | PKG_CHECK_MODULES([GL], [gl]) 40 | PKG_CHECK_MODULES([GLFW], [glfw3]) 41 | PKG_CHECK_MODULES([GLEW], [glew]) 42 | PKG_CHECK_MODULES([GLU], [glu]) 43 | PKG_CHECK_MODULES([PANGOCAIRO], [pangocairo]) 44 | PKG_CHECK_MODULES([GTKMM], [gtkmm-3.0]) 45 | ] ) 46 | 47 | # Checks for header files. 48 | AC_LANG_PUSH(C++) 49 | 50 | AC_CHECK_HEADERS([boost/accumulators/accumulators.hpp \ 51 | boost/accumulators/statistics/median.hpp \ 52 | boost/accumulators/statistics/stats.hpp boost/functional/hash.hpp], [], [AC_MSG_ERROR([Missing boost headers (need to install libboost-dev?)])]) 53 | 54 | # Checks for typedefs, structures, and compiler characteristics. 55 | AC_CHECK_HEADER_STDBOOL 56 | AC_TYPE_SIZE_T 57 | AC_TYPE_UINT64_T 58 | AC_LANG_POP(C++) 59 | 60 | # Checks for library functions. 61 | 62 | AC_CONFIG_FILES([Makefile protobufs/Makefile graph/Makefile src/Makefile tests/Makefile scripts/Makefile]) 63 | AC_OUTPUT 64 | -------------------------------------------------------------------------------- /src/utility.hh: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_HH 2 | #define UTILITY_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include "simulationresults.pb.h" 8 | 9 | class Utility 10 | { 11 | private: 12 | double _tick_share_sending; 13 | unsigned int _packets_received; 14 | double _total_delay; 15 | 16 | public: 17 | Utility( void ) : _tick_share_sending( 0 ), _packets_received( 0 ), _total_delay( 0 ) {} 18 | 19 | void sending_duration( const double & duration, const unsigned int num_sending ) { _tick_share_sending += duration / double( num_sending ); } 20 | void packets_received( const std::vector< Packet > & packets ) { 21 | _packets_received += packets.size(); 22 | 23 | for ( auto &x : packets ) { 24 | assert( x.tick_received >= x.tick_sent ); 25 | _total_delay += x.tick_received - x.tick_sent; 26 | } 27 | } 28 | 29 | /* returns throughput normalized to equal share of link */ 30 | double average_throughput_normalized_to_equal_share( void ) const 31 | { 32 | if ( _tick_share_sending == 0 ) { 33 | return 0.0; 34 | } 35 | return double( _packets_received ) / _tick_share_sending; 36 | } 37 | 38 | double average_delay( void ) const 39 | { 40 | if ( _packets_received == 0 ) { 41 | return 0.0; 42 | } 43 | return double( _total_delay ) / double( _packets_received ); 44 | } 45 | 46 | double utility( void ) const 47 | { 48 | if ( _tick_share_sending == 0 ) { 49 | return 0.0; 50 | } 51 | 52 | if ( _packets_received == 0 ) { 53 | return -INT_MAX; 54 | } 55 | 56 | const double throughput_utility = log2( average_throughput_normalized_to_equal_share() ); 57 | const double delay_penalty = log2( average_delay() / 100.0 ); 58 | 59 | return throughput_utility - delay_penalty; 60 | } 61 | 62 | SimulationResultBuffers::UtilityData DNA() const { 63 | SimulationResultBuffers::UtilityData ret; 64 | ret.set_sending_duration( _tick_share_sending ); 65 | ret.set_packets_received( _packets_received ); 66 | ret.set_total_delay( _total_delay ); 67 | return ret; 68 | } 69 | 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/rat.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rat.hh" 5 | 6 | using namespace std; 7 | 8 | Rat::Rat( WhiskerTree & s_whiskers, const bool s_track ) 9 | : _whiskers( s_whiskers ), 10 | _memory(), 11 | _packets_sent( 0 ), 12 | _packets_received( 0 ), 13 | _track( s_track ), 14 | _last_send_time( 0 ), 15 | _the_window( 0 ), 16 | _intersend_time( 0 ), 17 | _flow_id( 0 ), 18 | _largest_ack( -1 ) 19 | { 20 | } 21 | 22 | void Rat::packets_received( const vector< Packet > & packets ) { 23 | _packets_received += packets.size(); 24 | /* Assumption: There is no reordering */ 25 | _memory.packets_received( packets, _flow_id, _largest_ack ); 26 | _largest_ack = max( packets.at( packets.size() - 1 ).seq_num, _largest_ack ); 27 | 28 | const Whisker & current_whisker( _whiskers.use_whisker( _memory, _track ) ); 29 | 30 | _the_window = current_whisker.window( _the_window ); 31 | _intersend_time = current_whisker.intersend(); 32 | } 33 | 34 | void Rat::reset( const double & ) 35 | { 36 | _memory.reset(); 37 | _last_send_time = 0; 38 | _the_window = 0; 39 | _intersend_time = 0; 40 | _flow_id++; 41 | _largest_ack = _packets_sent - 1; /* Assume everything's been delivered */ 42 | assert( _flow_id != 0 ); 43 | 44 | /* initial window and intersend time */ 45 | const Whisker & current_whisker( _whiskers.use_whisker( _memory, _track ) ); 46 | _the_window = current_whisker.window( _the_window ); 47 | _intersend_time = current_whisker.intersend(); 48 | } 49 | 50 | double Rat::next_event_time( const double & tickno ) const 51 | { 52 | if ( int(_packets_sent) < _largest_ack + 1 + _the_window ) { 53 | if ( _last_send_time + _intersend_time <= tickno ) { 54 | return tickno; 55 | } else { 56 | return _last_send_time + _intersend_time; 57 | } 58 | } else { 59 | /* window is currently closed */ 60 | return std::numeric_limits::max(); 61 | } 62 | } 63 | 64 | SimulationResultBuffers::SenderState Rat::state_DNA() const 65 | { 66 | SimulationResultBuffers::SenderState ret; 67 | ret.mutable_memory()->CopyFrom( _memory.DNA() ); 68 | ret.set_window_size( _the_window ); 69 | ret.set_intersend_time( _intersend_time ); 70 | return ret; 71 | } -------------------------------------------------------------------------------- /src/fish.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "fish.hh" 5 | 6 | using namespace std; 7 | 8 | Fish::Fish( const FinTree & fins, const unsigned int s_prng_seed, const bool s_track ) 9 | : _fins( fins ), 10 | _memory(), 11 | _packets_sent( 0 ), 12 | _packets_received( 0 ), 13 | _last_send_time( 0 ), 14 | _next_send_time( 0 ), 15 | _flow_id( 0 ), 16 | _largest_ack( -1 ), 17 | _track( s_track ), 18 | _lambda( 0 ), 19 | _max_intersend( 0 ), 20 | _batch_size( 5 ), 21 | _prng( s_prng_seed ), 22 | _distribution( 1 ) 23 | { 24 | } 25 | 26 | void Fish::packets_received( const vector< Packet > & packets ) { 27 | _packets_received += packets.size(); 28 | _memory.packets_received( packets, _flow_id, _largest_ack ); 29 | _largest_ack = max( packets.at( packets.size() - 1 ).seq_num, _largest_ack ); 30 | 31 | const Fin & current_fin( _fins.use_fin( _memory, _track ) ); 32 | _update_lambda( current_fin.lambda() ); 33 | _update_send_time( _last_send_time ); 34 | } 35 | 36 | void Fish::reset( const double & ) 37 | { 38 | _memory.reset(); 39 | _last_send_time = 0; 40 | _next_send_time = 0; 41 | _lambda = 0; 42 | _max_intersend = 0; 43 | _flow_id++; 44 | /* Give up on everything sent so far that hasn't been acked, 45 | Fixes the problem of tail losses */ 46 | _largest_ack = _packets_sent - 1; 47 | assert( _flow_id != 0 ); 48 | } 49 | 50 | double Fish::next_event_time( const double & tickno ) const 51 | { 52 | if (_next_send_time == 0) { 53 | return tickno; 54 | } else { 55 | return _next_send_time; 56 | } 57 | } 58 | 59 | void Fish::_update_send_time( const double tickno ) 60 | { 61 | _last_send_time = tickno; 62 | _next_send_time = _last_send_time + _batch_size * min( _distribution.sample( _prng ), _max_intersend ); 63 | } 64 | 65 | void Fish::_update_lambda( const double lambda ) 66 | { 67 | _lambda = lambda; 68 | _max_intersend = 2.0 / lambda; 69 | _distribution.set_lambda( lambda ); 70 | } 71 | 72 | SimulationResultBuffers::SenderState Fish::state_DNA() const 73 | { 74 | SimulationResultBuffers::SenderState ret; 75 | ret.mutable_memory()->CopyFrom( _memory.DNA() ); 76 | ret.set_packets_sent( _packets_sent ); 77 | ret.set_lambda( _lambda ); 78 | return ret; 79 | } 80 | -------------------------------------------------------------------------------- /src/fishbreeder.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fishbreeder.hh" 4 | 5 | using namespace std; 6 | 7 | Evaluator< FinTree >::Outcome FishBreeder::improve( FinTree & fins ) 8 | { 9 | /* back up the original fintree */ 10 | /* this is to ensure we don't regress */ 11 | FinTree input_fintree( fins ); 12 | 13 | /* evaluate the fins we have */ 14 | fins.reset_generation(); 15 | unsigned int generation = 0; 16 | 17 | while ( generation < 5 ) { 18 | const Evaluator< FinTree > eval( _options.config_range ); 19 | 20 | auto outcome( eval.score( fins ) ); 21 | 22 | /* is there a fin at this generation that we can improve? */ 23 | auto most_used_fin_ptr = outcome.used_actions.most_used( generation ); 24 | 25 | /* if not, increase generation and promote all fins */ 26 | if ( !most_used_fin_ptr ) { 27 | generation++; 28 | fins.promote( generation ); 29 | 30 | continue; 31 | } 32 | 33 | FinImprover improver( eval, fins, outcome.score ); 34 | 35 | Fin fin_to_improve = *most_used_fin_ptr; 36 | 37 | double score_to_beat = outcome.score; 38 | 39 | while ( 1 ) { 40 | double new_score = improver.improve( fin_to_improve ); 41 | assert( new_score >= score_to_beat ); 42 | if ( new_score == score_to_beat ) { 43 | cerr << "Ending search." << endl; 44 | break; 45 | } else { 46 | cerr << "Score jumps from " << score_to_beat << " to " << new_score << endl; 47 | score_to_beat = new_score; 48 | } 49 | } 50 | 51 | fin_to_improve.demote( generation + 1 ); 52 | 53 | const auto result __attribute((unused)) = fins.replace( fin_to_improve ); 54 | assert( result ); 55 | } 56 | 57 | /* Split most used whisker */ 58 | apply_best_split( fins, generation ); 59 | 60 | /* carefully evaluate what we have vs. the previous best */ 61 | const Evaluator< FinTree > eval2( _options.config_range ); 62 | const auto new_score = eval2.score( fins, false, 10 ); 63 | const auto old_score = eval2.score( input_fintree, false, 10 ); 64 | 65 | if ( old_score.score >= new_score.score ) { 66 | fprintf( stderr, "Regression, old=%f, new=%f\n", old_score.score, new_score.score ); 67 | fins = input_fintree; 68 | return old_score; 69 | } 70 | 71 | return new_score; 72 | } 73 | 74 | vector< Fin > FinImprover::get_replacements( Fin & action_to_improve ) 75 | { 76 | return action_to_improve.next_generation(); 77 | } 78 | -------------------------------------------------------------------------------- /src/aimd.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "aimd.hh" 5 | 6 | using namespace std; 7 | 8 | static constexpr double INITIAL_WINDOW = 1.0; /* INITIAL WINDOW OF 1 */ 9 | 10 | Aimd::Aimd() 11 | : _packets_sent( 0 ), 12 | _packets_received( 0 ), 13 | _the_window( INITIAL_WINDOW ), 14 | _flow_id( 0 ), 15 | _largest_ack( -1 ), 16 | _slow_start( true ), 17 | _last_loss( -1 ), 18 | _rtt_at_loss( -1 ) 19 | { 20 | } 21 | 22 | void Aimd::packets_received( const vector< Packet > & packets ) { 23 | bool loss_detected = false; 24 | for ( auto & packet : packets ) { 25 | loss_detected = ( not loss_detected ) ? 26 | ( packet.seq_num > _largest_ack + 1 ): /* At least lost one packet */ 27 | ( true ); /* Bypass seq_num check if you saw a loss already */ 28 | _largest_ack = max( _largest_ack, packet.seq_num ); 29 | 30 | _packets_received++; 31 | if ( packet.flow_id != _flow_id ) { 32 | /* This was from the previous flow, ignore it for congestion control */ 33 | continue; 34 | } 35 | 36 | /* If in slow_start, exit slow start on detecting loss, else don't bother */ 37 | _slow_start = ( _slow_start ) ? ( loss_detected ? false : _slow_start ) 38 | : _slow_start; 39 | if ( loss_detected ) { 40 | if ( packet.tick_received > _last_loss + _rtt_at_loss ) { 41 | /* The DCCP approximation, reduce cwnd at most once per RTT */ 42 | _the_window = _the_window / 2.0; 43 | _last_loss = packet.tick_received; 44 | _rtt_at_loss = packet.tick_received - packet.tick_sent; 45 | } 46 | } else { 47 | if ( _slow_start ) { 48 | _the_window += 1.0; 49 | } else { 50 | _the_window += 1.0 / _the_window; 51 | } 52 | } 53 | _the_window = max( INITIAL_WINDOW, _the_window ); 54 | } 55 | } 56 | 57 | void Aimd::reset( const double & ) 58 | { 59 | _the_window = INITIAL_WINDOW; 60 | _flow_id++; 61 | _slow_start = true; 62 | /* Give up on everything sent so far that hasn't been acked, 63 | Fixes the problem of tail losses */ 64 | _largest_ack = _packets_sent - 1; 65 | assert( _flow_id != 0 ); 66 | } 67 | 68 | double Aimd::next_event_time( const double & tickno ) const 69 | { 70 | if ( _packets_sent < _largest_ack + 1 + _the_window ) { 71 | return tickno; 72 | } else { 73 | return std::numeric_limits::max(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/simulationresults.cc: -------------------------------------------------------------------------------- 1 | #include "simulationresults.hh" 2 | 3 | template 4 | SimulationRunData & SimulationResults< ActionTree >::add_run_data( const NetConfig & config, double interval ) 5 | { 6 | 7 | run_data.emplace_back( config, interval ); 8 | return run_data.back(); 9 | } 10 | 11 | SimulationRunDataPoint & SimulationRunData::add_datum( double seconds ) 12 | { 13 | _data.emplace_back( seconds ); 14 | return _data.back(); 15 | } 16 | 17 | void SimulationRunDataPoint::add_sender_data( std::vector< SenderDataPoint > new_data ) 18 | { 19 | _sender_data.insert( _sender_data.end(), new_data.begin(), new_data.end() ); 20 | } 21 | 22 | void SimulationRunDataPoint::add_network_data( std::vector< unsigned int > packets_in_flight ) 23 | { 24 | assert( packets_in_flight.size() == _sender_data.size() ); 25 | for (unsigned int i = 0; i < packets_in_flight.size(); i++) { 26 | _sender_data[i].set_packets_in_flight( packets_in_flight[i] ); 27 | } 28 | } 29 | 30 | template <> 31 | void SimulationResults< WhiskerTree >::_populate_actions( SimulationResultBuffers::SimulationsData & pb ) const 32 | { 33 | pb.mutable_whiskers()->CopyFrom( actions.DNA() ); 34 | } 35 | 36 | template <> 37 | void SimulationResults< FinTree >::_populate_actions( SimulationResultBuffers::SimulationsData & pb ) const 38 | { 39 | pb.mutable_fins()->CopyFrom( actions.DNA() ); 40 | } 41 | 42 | template 43 | SimulationResultBuffers::SimulationsData SimulationResults< ActionTree >::DNA( void ) const 44 | { 45 | SimulationResultBuffers::SimulationsData ret; 46 | 47 | _populate_actions(ret); 48 | 49 | ProblemBuffers::ProblemSettings settings; 50 | settings.set_prng_seed( prng_seed ); 51 | settings.set_tick_count( tick_count ); 52 | ret.mutable_settings()->CopyFrom( settings ); 53 | 54 | for ( const auto &run : run_data ) { 55 | SimulationResultBuffers::SimulationRunData * run_data_pb = ret.add_run_data(); 56 | run_data_pb->set_log_interval_ticks( run.interval() ); 57 | run_data_pb->mutable_config()->CopyFrom( run.config().DNA() ); 58 | 59 | for ( const auto &datum : run.data() ) { 60 | SimulationResultBuffers::SimulationRunDataPoint * data_pb = run_data_pb->add_point(); 61 | data_pb->set_seconds( datum.seconds() ); 62 | 63 | for (const auto &sender_datum : datum.sender_data() ) { 64 | SimulationResultBuffers::SenderDataPoint * sender_data_pb = data_pb->add_sender_data(); 65 | sender_data_pb->CopyFrom( sender_datum.DNA() ); 66 | } 67 | } 68 | } 69 | 70 | return ret; 71 | } 72 | -------------------------------------------------------------------------------- /tests/maintain-2013-results: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | use strict; 4 | 5 | # make sure that results with the 2013 RemyCCs don't drift 6 | # In this test, we don't compare against an absolute reference (e.g. 7 | # the ns-2 results reported in the paper); just against similar results 8 | # from the 2013 version of rat-runner (db1b3c3) 9 | 10 | sub score { 11 | my ( $remycc ) = @_; 12 | 13 | my $link_speed = 15; 14 | my $packets_per_tick = $link_speed / 10; 15 | my $RTT = 150; 16 | my $number_of_sources = 8; 17 | 18 | my @result = qx{../src/sender-runner if=$ENV{'srcdir'}/$remycc nsrc=$number_of_sources link=$packets_per_tick rtt=$RTT on=10000 off=10000} or die q{Can't exec sender-runner}; 19 | 20 | my @normalized_throughputs_and_delays; 21 | 22 | for ( @result ) { 23 | if ( m{^sender: } ) { 24 | my ( $normalized_throughput, $delay_ratio ) = m{\[tp=(.*?), del=(.*?)\]}; 25 | push @normalized_throughputs_and_delays, [ $normalized_throughput, $delay_ratio ]; 26 | } 27 | } 28 | 29 | if ( scalar @normalized_throughputs_and_delays != $number_of_sources ) { 30 | die qq{sender-runner did not give $number_of_sources results}; 31 | } 32 | 33 | return @normalized_throughputs_and_delays; 34 | } 35 | 36 | sub assert_in_range { 37 | my ( $val, $min, $max ) = @_; 38 | unless ( $min < $val and $val < $max ) { 39 | die qq{Constraint violated: value $val not in interval [$min..$max]}; 40 | } 41 | } 42 | 43 | sub enforce_constraint { 44 | my ( $link_speed, $expected_throughput, $expected_delay ) = @_; 45 | 46 | my @throughputs_and_delays = score( $link_speed ); 47 | 48 | for ( @throughputs_and_delays ) { 49 | assert_in_range( $_->[ 0 ], $expected_throughput * .95, $expected_throughput * 1.05 ); 50 | assert_in_range( $_->[ 1 ], $expected_delay * .95, $expected_delay * 1.05 ); 51 | } 52 | } 53 | 54 | # these triplets of link speed => { normalized throughput, normalized delay } 55 | # are taken by running rat-runner from the 2016 codebase 56 | # (commit 7692857), after making sure results were similar to 57 | # running rat-runner from the 2013 codebase (commit db1b3c3) 58 | 59 | # Note that because of some bugs that have been fixed in going to a 60 | # tickless simulator (the old code prioritized the "first" RemyCC 61 | # when sending during the same tick), rat-runner's results are 62 | # more consistent (but have less average throughput) post-2013 63 | # than in 2013. 64 | 65 | enforce_constraint( q{RemyCC-2013-delta10.dna}, 0.65, 1.0 ); 66 | enforce_constraint( q{RemyCC-2013-delta1.dna}, 0.81, 1.02 ); 67 | enforce_constraint( q{RemyCC-2013-delta0.1.dna}, 0.9, 1.11 ); 68 | 69 | 1; 70 | -------------------------------------------------------------------------------- /src/whisker.hh: -------------------------------------------------------------------------------- 1 | #ifndef WHISKER_HH 2 | #define WHISKER_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "memoryrange.hh" 8 | #include "action.hh" 9 | #include "dna.pb.h" 10 | 11 | class Whisker : public Action { 12 | private: 13 | int _window_increment; 14 | double _window_multiple; 15 | double _intersend; 16 | public: 17 | Whisker( const Whisker & other ); 18 | Whisker( const unsigned int s_window_increment, const double s_window_multiple, const double s_intersend, const MemoryRange & s_domain ); 19 | 20 | Whisker( const MemoryRange & s_domain ) : Whisker( get_optimizer().window_increment.default_value, 21 | get_optimizer().window_multiple.default_value, 22 | get_optimizer().intersend.default_value, s_domain ) {}; 23 | virtual ~Whisker() {}; 24 | 25 | unsigned int window( const unsigned int previous_window ) const { return std::min( std::max( 0, int( previous_window * _window_multiple + _window_increment ) ), 1000000 ); } 26 | const double & intersend( void ) const { return _intersend; } 27 | 28 | std::vector< Whisker > next_generation( bool optimize_window_increment, bool optimize_window_multiple, bool optimize_intersend ) const; 29 | 30 | std::string str( const unsigned int total=1 ) const; 31 | 32 | RemyBuffers::Whisker DNA( void ) const; 33 | Whisker( const RemyBuffers::Whisker & dna ); 34 | 35 | void round( void ); 36 | 37 | bool operator==( const Whisker & other ) const { return (_window_increment == other._window_increment) && (_window_multiple == other._window_multiple) && (_intersend == other._intersend) && (_domain == other._domain); } 38 | 39 | friend size_t hash_value( const Whisker & Whisker ); 40 | 41 | struct OptimizationSettings 42 | { 43 | OptimizationSetting< unsigned int > window_increment; 44 | OptimizationSetting< double > window_multiple; 45 | OptimizationSetting< double > intersend; 46 | 47 | RemyBuffers::OptimizationSettings DNA( void ) const 48 | { 49 | RemyBuffers::OptimizationSettings ret; 50 | 51 | ret.mutable_window_increment()->CopyFrom( window_increment.DNA() ); 52 | ret.mutable_window_multiple()->CopyFrom( window_multiple.DNA() ); 53 | ret.mutable_intersend()->CopyFrom( intersend.DNA() ); 54 | 55 | return ret; 56 | } 57 | }; 58 | 59 | static const OptimizationSettings & get_optimizer( void ) { 60 | static OptimizationSettings default_settings { 61 | { 0, 256, 1, 32, 4, 1 }, /* window increment */ 62 | { 0, 1, 0.01, 0.5, 4, 1 }, /* window multiple */ 63 | { 0.25, 3, 0.05, 1, 4, 3 } /* intersend */ 64 | }; 65 | return default_settings; 66 | } 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/simulationresults.hh: -------------------------------------------------------------------------------- 1 | #ifndef SIMULATIONRESULTS_HH 2 | #define SIMULATIONRESULTS_HH 3 | 4 | #include "network.hh" 5 | #include "whiskertree.hh" 6 | #include "fintree.hh" 7 | #include "simulationresults.pb.h" 8 | #include "senderdatapoint.hh" 9 | #include 10 | 11 | class SimulationRunData; 12 | class SimulationRunDataPoint; 13 | 14 | // top level results class 15 | template 16 | class SimulationResults 17 | { 18 | public: 19 | SimulationResults() : actions(), run_data() {}; 20 | SimulationResults( ActionTree actions ) : actions( actions ), run_data() {}; 21 | 22 | SimulationResultBuffers::SimulationsData DNA( void ) const; 23 | 24 | // Adds a run and returns a reference to it 25 | SimulationRunData & add_run_data( const NetConfig & config, double interval ); 26 | 27 | void set_prng_seed( unsigned int prng_seed ) { this->prng_seed = prng_seed; } 28 | void set_tick_count( unsigned int tick_count ) { this->tick_count = tick_count; } 29 | void set_log_interval_ticks( unsigned int log_interval_ticks ) { this->log_interval_ticks = log_interval_ticks; } 30 | 31 | private: 32 | void _populate_actions( SimulationResultBuffers::SimulationsData & pb ) const; 33 | 34 | ActionTree actions; 35 | std::vector< struct SimulationRunData > run_data; 36 | 37 | // problem settings 38 | unsigned int prng_seed = 0; 39 | unsigned int tick_count = 0; 40 | unsigned int log_interval_ticks = 0; 41 | }; 42 | 43 | template class SimulationResults< WhiskerTree >; 44 | template class SimulationResults< FinTree >; 45 | 46 | 47 | class SimulationRunData 48 | { 49 | public: 50 | SimulationRunData( NetConfig config, double interval ) : _config( config ), _interval( interval ), _data() {}; 51 | 52 | // Adds a data point and returns a reference to it 53 | struct SimulationRunDataPoint & add_datum( double seconds ); 54 | 55 | const NetConfig & config() const { return _config; } 56 | double interval() const { return _interval; } 57 | const std::vector< struct SimulationRunDataPoint > & data() const { return _data; } 58 | 59 | private: 60 | NetConfig _config; 61 | double _interval; 62 | std::vector< struct SimulationRunDataPoint > _data; 63 | }; 64 | 65 | class SimulationRunDataPoint 66 | { 67 | public: 68 | SimulationRunDataPoint( double seconds ) : _seconds( seconds ), _sender_data() {}; 69 | 70 | void add_sender_data( std::vector< SenderDataPoint > ); 71 | void add_network_data( std::vector< unsigned int > ); 72 | 73 | double seconds() const { return _seconds; } 74 | const std::vector< SenderDataPoint > & sender_data() const { return _sender_data; } 75 | 76 | private: 77 | double _seconds; 78 | std::vector< SenderDataPoint > _sender_data; 79 | }; 80 | 81 | #endif // SIMULATIONRESULTS_HH -------------------------------------------------------------------------------- /src/ratbreeder.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ratbreeder.hh" 4 | 5 | using namespace std; 6 | 7 | Evaluator< WhiskerTree >::Outcome RatBreeder::improve( WhiskerTree & whiskers ) 8 | { 9 | /* back up the original whiskertree */ 10 | /* this is to ensure we don't regress */ 11 | WhiskerTree input_whiskertree( whiskers ); 12 | 13 | /* evaluate the whiskers we have */ 14 | whiskers.reset_generation(); 15 | unsigned int generation = 0; 16 | 17 | while ( generation < 5 ) { 18 | const Evaluator< WhiskerTree > eval( _options.config_range ); 19 | 20 | auto outcome( eval.score( whiskers ) ); 21 | 22 | /* is there a whisker at this generation that we can improve? */ 23 | auto most_used_whisker_ptr = outcome.used_actions.most_used( generation ); 24 | 25 | /* if not, increase generation and promote all whiskers */ 26 | if ( !most_used_whisker_ptr ) { 27 | generation++; 28 | whiskers.promote( generation ); 29 | 30 | continue; 31 | } 32 | 33 | WhiskerImprover improver( eval, whiskers, _whisker_options, outcome.score ); 34 | 35 | Whisker whisker_to_improve = *most_used_whisker_ptr; 36 | 37 | double score_to_beat = outcome.score; 38 | 39 | while ( 1 ) { 40 | double new_score = improver.improve( whisker_to_improve ); 41 | assert( new_score >= score_to_beat ); 42 | if ( new_score == score_to_beat ) { 43 | cerr << "Ending search." << endl; 44 | break; 45 | } else { 46 | cerr << "Score jumps from " << score_to_beat << " to " << new_score << endl; 47 | score_to_beat = new_score; 48 | } 49 | } 50 | 51 | whisker_to_improve.demote( generation + 1 ); 52 | 53 | const auto result __attribute((unused)) = whiskers.replace( whisker_to_improve ); 54 | assert( result ); 55 | } 56 | 57 | /* Split most used whisker */ 58 | apply_best_split( whiskers, generation ); 59 | 60 | /* carefully evaluate what we have vs. the previous best */ 61 | const Evaluator< WhiskerTree > eval2( _options.config_range ); 62 | const auto new_score = eval2.score( whiskers, false, 10 ); 63 | const auto old_score = eval2.score( input_whiskertree, false, 10 ); 64 | 65 | if ( old_score.score >= new_score.score ) { 66 | fprintf( stderr, "Regression, old=%f, new=%f\n", old_score.score, new_score.score ); 67 | whiskers = input_whiskertree; 68 | return old_score; 69 | } 70 | 71 | return new_score; 72 | } 73 | 74 | vector< Whisker > WhiskerImprover::get_replacements( Whisker & whisker_to_improve ) 75 | { 76 | return whisker_to_improve.next_generation( _options.optimize_window_increment, 77 | _options.optimize_window_multiple, 78 | _options.optimize_intersend ); 79 | } 80 | -------------------------------------------------------------------------------- /tests/run-plot-script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import subprocess 4 | import csv 5 | import os 6 | import shutil 7 | import unittest 8 | 9 | class TestPlotScript(unittest.TestCase): 10 | 11 | RESULTS_DIR = "plot-results" 12 | REMYCC_NAME = os.path.join(os.environ["srcdir"], "RemyCC-2014-100x.dna") 13 | PLOT_SCRIPT = os.path.join(os.environ["srcdir"], "../scripts/plot.py") 14 | ORIGINALS_DIR = os.path.join(os.environ["srcdir"], "../scripts/originals") 15 | SENDERRUNNER = os.path.abspath("../src/sender-runner") 16 | 17 | def test_scripts(self): 18 | 19 | # Run as command line, not as Python import, to check command line interface 20 | subprocess.check_output([self.PLOT_SCRIPT, self.REMYCC_NAME, "-n=10", "-O", self.RESULTS_DIR, 21 | "--originals", self.ORIGINALS_DIR, "--sender-runner", self.SENDERRUNNER, "--newlines"]) 22 | 23 | # Check data matches what was expected 24 | EXPECTED_DATA = [ 25 | [0.1 , -3.652, 1.259, 15.58, 1.258, 16.08], 26 | [0.215443469003, -1.098, 1.078, 2.305, 1.074, 2.307], 27 | [0.464158883361, -0.493, 0.952, 1.339, 0.948, 1.337], 28 | [1.0 , -0.444, 0.808, 1.097, 0.805, 1.099], 29 | [2.15443469003 , -0.559, 0.876, 1.283, 0.865, 1.283], 30 | [4.64158883361 , -0.586, 0.812, 1.219, 0.811, 1.218], 31 | [10.0 , -0.533, 0.715, 1.038, 0.719, 1.038], 32 | [21.5443469003 , -0.672, 0.640, 1.021, 0.640, 1.020], 33 | [46.4158883361 , -1.119, 0.463, 1.000, 0.457, 1.000], 34 | [100.0 , -2.233, 0.212, 1.000, 0.212, 1.000], 35 | ] 36 | 37 | result_file = open(os.path.join(self.RESULTS_DIR, "data", "data-" + os.path.basename(self.REMYCC_NAME) + ".csv")) 38 | result_csv = csv.reader(result_file) 39 | 40 | for expected_row, actual_row_str in zip(EXPECTED_DATA, result_csv): 41 | actual_row = [float(x) for x in actual_row_str] 42 | 43 | expected_link_ppt = expected_row.pop(0) 44 | actual_link_ppt = actual_row.pop(0) 45 | self.assertAlmostEqual(expected_link_ppt, actual_link_ppt) 46 | 47 | expected_norm_score = expected_row.pop(0) 48 | actual_norm_score = actual_row.pop(0) 49 | self.assertAlmostEqual(expected_norm_score, actual_norm_score, delta=0.5) 50 | 51 | for expected, actual in zip(expected_row, actual_row): 52 | self.assertLess(abs((actual - expected)/expected), 0.2, msg="{} is not within 20% of {}".format(actual, expected)) 53 | self.assertEqual(actual > 0, expected > 0) 54 | 55 | # clean up 56 | os.unlink("last") 57 | shutil.rmtree(self.RESULTS_DIR) 58 | 59 | if __name__ == '__main__': 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /protobufs/simulationresults.proto: -------------------------------------------------------------------------------- 1 | import "dna.proto"; 2 | import "problem.proto"; 3 | 4 | package SimulationResultBuffers; 5 | 6 | /* 7 | * SimulationsDataBank stores results for lots of simulations which may or may 8 | * not have any relation to each other. It has no purpose other than to be a 9 | * container, to allow the serialization of lots of data into a single file. 10 | * [Not currently used.] 11 | message SimulationsDataBank { 12 | repeated SimulationsData = 31; 13 | }*/ 14 | 15 | /* 16 | * SimulationsData stores results for multiple simulations, each of which will 17 | * have different configurations, but all of which will have the same whiskers. 18 | */ 19 | message SimulationsData { 20 | optional ProblemBuffers.ProblemSettings settings = 21; 21 | optional RemyBuffers.WhiskerTree whiskers = 22; 22 | optional RemyBuffers.FinTree fins = 24; 23 | repeated SimulationRunData run_data = 23; // one for each config 24 | } 25 | 26 | /* 27 | * SimulationRunData stores the data relating to a single simulation, i.e. some 28 | * known configuration, lots of data points. 29 | */ 30 | message SimulationRunData { 31 | optional RemyBuffers.NetConfig config = 11; 32 | optional float log_interval_ticks = 13; 33 | repeated SimulationRunDataPoint point = 12; // one for each timestamp 34 | } 35 | 36 | /* 37 | * SimulationRunDataPoint stores the data associated with a single point (in 38 | * time). Currently, it only has fields for metrics calculated for the entire 39 | * time since the start of the simulation (as opposed to instantaneous or moving 40 | * average measures), because the evaluator only supports this calculation. 41 | * There are lots of these, so use small numbers. 42 | */ 43 | message SimulationRunDataPoint { 44 | repeated SenderDataPoint sender_data = 1; // one for each sender 45 | optional float seconds = 2; 46 | } 47 | 48 | /* 49 | * SenderDataPoint stores the data associated with a single point (in time) and 50 | * sender. 51 | */ 52 | message SenderDataPoint { 53 | optional SenderState sender_state = 1; 54 | optional UtilityData utility_data = 2; 55 | optional bool sending = 10; 56 | optional uint32 packets_in_flight = 11; 57 | } 58 | 59 | /** 60 | * SenderState stores the data associated with the current state of each sender 61 | * type (as a given point in time), and possibly other information about the 62 | * sender. Using a single class simplifies the code for handling this; since 63 | * protobufs doesn't store empty fields, there's no wasted memory. Sender 64 | * classes (Fish, Rat) just populate the fields that are relevant to them. 65 | */ 66 | message SenderState { 67 | optional RemyBuffers.Memory memory = 1; 68 | optional uint32 packets_sent = 2; 69 | optional uint32 window_size = 3; 70 | optional float intersend_time = 4; 71 | optional float lambda = 5; 72 | } 73 | 74 | message UtilityData { 75 | optional float sending_duration = 1; 76 | optional uint32 packets_received = 2; 77 | optional float total_delay = 3; 78 | } -------------------------------------------------------------------------------- /src/memory.hh: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_HH 2 | #define MEMORY_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "packet.hh" 8 | #include "dna.pb.h" 9 | 10 | class Memory { 11 | public: 12 | typedef double DataType; 13 | 14 | private: 15 | DataType _rec_send_ewma; 16 | DataType _rec_rec_ewma; 17 | DataType _rtt_ratio; 18 | DataType _slow_rec_rec_ewma; 19 | DataType _rtt_diff; 20 | DataType _queueing_delay; 21 | 22 | double _last_tick_sent; 23 | double _last_tick_received; 24 | double _min_rtt; 25 | 26 | public: 27 | Memory( const std::vector< DataType > & s_data ) 28 | : _rec_send_ewma( s_data.at( 0 ) ), 29 | _rec_rec_ewma( s_data.at( 1 ) ), 30 | _rtt_ratio( s_data.at( 2 ) ), 31 | _slow_rec_rec_ewma( s_data.at( 3 ) ), 32 | _rtt_diff( s_data.at(4) ), 33 | _queueing_delay( s_data.at(5) ), 34 | _last_tick_sent( 0 ), 35 | _last_tick_received( 0 ), 36 | _min_rtt( 0 ) 37 | {} 38 | 39 | Memory() 40 | : _rec_send_ewma( 0 ), 41 | _rec_rec_ewma( 0 ), 42 | _rtt_ratio( 0.0 ), 43 | _slow_rec_rec_ewma( 0 ), 44 | _rtt_diff( 0 ), 45 | _queueing_delay( 0 ), 46 | _last_tick_sent( 0 ), 47 | _last_tick_received( 0 ), 48 | _min_rtt( 0 ) 49 | {} 50 | 51 | void reset( void ) { _rec_send_ewma = _rec_rec_ewma = _rtt_ratio = _slow_rec_rec_ewma = _rtt_diff = _queueing_delay = _last_tick_sent = _last_tick_received = _min_rtt = 0; } 52 | 53 | static const unsigned int datasize = 6; 54 | 55 | const DataType & field( unsigned int num ) const { return num == 0 ? _rec_send_ewma : num == 1 ? _rec_rec_ewma : num == 2 ? _rtt_ratio : num == 3 ? _slow_rec_rec_ewma : num == 4 ? _rtt_diff : _queueing_delay ; } 56 | DataType & mutable_field( unsigned int num ) { return num == 0 ? _rec_send_ewma : num == 1 ? _rec_rec_ewma : num == 2 ? _rtt_ratio : num == 3 ? _slow_rec_rec_ewma : num == 4 ? _rtt_diff : _queueing_delay ; } 57 | 58 | void packet_sent( const Packet & packet __attribute((unused)) ) {} 59 | void packets_received( const std::vector< Packet > & packets, const unsigned int flow_id, const int largest_ack ); 60 | void advance_to( const unsigned int tickno __attribute((unused)) ) {} 61 | 62 | std::string str( void ) const; 63 | std::string str( unsigned int num ) const; 64 | 65 | bool operator>=( const Memory & other ) const { 66 | for (unsigned int i = 0; i < datasize; i ++) { if ( field(i) < other.field(i) ) return false; } 67 | return true; 68 | } 69 | bool operator<( const Memory & other ) const { 70 | for (unsigned int i = 0; i < datasize; i ++) { if ( field(i) >= other.field(i) ) return false; } 71 | return true; 72 | } 73 | bool operator==( const Memory & other ) const { 74 | for (unsigned int i = 0; i < datasize; i ++) { if ( field(i) != other.field(i) ) return false; } 75 | return true; 76 | } 77 | 78 | RemyBuffers::Memory DNA( void ) const; 79 | Memory( const bool is_lower_limit, const RemyBuffers::Memory & dna ); 80 | 81 | friend size_t hash_value( const Memory & mem ); 82 | }; 83 | 84 | extern const Memory & MAX_MEMORY( void ); 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/sendergangofgangs.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sendergangofgangs.hh" 3 | #include "sendergang.cc" 4 | 5 | using namespace std; 6 | 7 | template 8 | SenderGangofGangs::SenderGangofGangs( const Gang1Type & gang1, 9 | const Gang2Type & gang2 ) 10 | : gang1_( gang1 ), gang2_( gang2 ) 11 | { 12 | /* Make sure no addresses conflict and no gap in address range */ 13 | assert( gang1_.id_of_first_sender() == 0 ); 14 | assert( gang2_.count_senders() == 0 or gang2_.id_of_first_sender() == gang1_.count_senders() ); 15 | } 16 | 17 | template 18 | unsigned int SenderGangofGangs::count_active_senders( void ) const 19 | { 20 | return gang1_.count_active_senders() + gang2_.count_active_senders(); 21 | } 22 | 23 | template 24 | void SenderGangofGangs::switch_senders( const unsigned int num_sending, 25 | const double & tickno ) 26 | { 27 | gang1_.switch_senders( num_sending, tickno ); 28 | gang2_.switch_senders( num_sending, tickno ); 29 | } 30 | 31 | template 32 | template 33 | void SenderGangofGangs::run_senders( NextHop & next, Receiver & rec, 34 | const unsigned int num_sending, 35 | const double & tickno ) 36 | { 37 | gang1_.run_senders( next, rec, num_sending, tickno ); 38 | gang2_.run_senders( next, rec, num_sending, tickno ); 39 | } 40 | 41 | /* Same implementation as a SenderGang */ 42 | template 43 | template 44 | void SenderGangofGangs::tick( NextHop & next, Receiver & rec, const double & tickno ) 45 | { 46 | unsigned int num_sending = count_active_senders(); 47 | 48 | switch_senders( num_sending, tickno ); 49 | 50 | num_sending = count_active_senders(); 51 | 52 | run_senders( next, rec, num_sending, tickno ); 53 | } 54 | 55 | template 56 | double SenderGangofGangs::utility( void ) const 57 | { 58 | return gang1_.utility() + gang2_.utility(); 59 | } 60 | 61 | template 62 | vector< pair< double, double > > SenderGangofGangs::throughputs_delays( void ) const 63 | { 64 | auto ret = gang1_.throughputs_delays(); 65 | const auto gang2_tpd = gang2_.throughputs_delays(); 66 | 67 | ret.insert( ret.end(), gang2_tpd.begin(), gang2_tpd.end() ); 68 | 69 | return ret; 70 | } 71 | 72 | template 73 | vector < SenderDataPoint > SenderGangofGangs::statistics_for_log( void ) const 74 | { 75 | auto ret = gang1_.statistics_for_log(); 76 | const auto gang2_stats = gang2_.statistics_for_log(); 77 | ret.insert( ret.end(), gang2_stats.begin(), gang2_stats.end() ); 78 | return ret; 79 | } 80 | 81 | template 82 | double SenderGangofGangs::next_event_time( const double & tickno ) const 83 | { 84 | return min( gang1_.next_event_time( tickno ), gang2_.next_event_time( tickno ) ); 85 | } 86 | -------------------------------------------------------------------------------- /protobufs/dna.proto: -------------------------------------------------------------------------------- 1 | package RemyBuffers; 2 | 3 | message WhiskerTree { 4 | optional MemoryRange domain = 1; 5 | 6 | repeated WhiskerTree children = 2; 7 | 8 | optional Whisker leaf = 3; 9 | 10 | optional ConfigRange config = 4; 11 | 12 | optional OptimizationSettings optimizer = 5; 13 | 14 | optional ConfigVector configvector = 6; 15 | } 16 | 17 | message FinTree { 18 | optional MemoryRange domain = 90; 19 | 20 | repeated FinTree children = 91; 21 | 22 | optional Fin leaf = 92; 23 | 24 | optional ConfigRange config = 93; 25 | 26 | optional OptimizationSettings optimizer = 94; 27 | 28 | optional ConfigVector configvector = 95; 29 | } 30 | 31 | message MemoryRange { 32 | optional Memory lower = 11; 33 | optional Memory upper = 12; 34 | 35 | enum Axis { 36 | SEND_EWMA = 0; 37 | REC_EWMA = 1; 38 | RTT_RATIO = 2; 39 | SLOW_REC_EWMA = 3; 40 | RTT_DIFF = 4; 41 | QUEUEING_DELAY = 5; 42 | } 43 | 44 | repeated Axis active_axis = 13; 45 | } 46 | 47 | message Memory { 48 | optional double rec_send_ewma = 21; 49 | optional double rec_rec_ewma = 22; 50 | optional double rtt_ratio = 23; 51 | optional double slow_rec_rec_ewma = 24; 52 | optional double rtt_diff = 25; 53 | optional double queueing_delay = 26; 54 | } 55 | 56 | message Whisker { 57 | optional sint32 window_increment = 31; 58 | optional double window_multiple = 32; 59 | optional double intersend = 33; 60 | 61 | optional MemoryRange domain = 34; 62 | } 63 | 64 | message Fin { 65 | optional double lambda = 37; 66 | 67 | optional MemoryRange domain = 38; 68 | } 69 | 70 | message OptimizationSetting { 71 | optional double min_value = 41; 72 | optional double max_value = 42; 73 | 74 | optional double min_change = 43; 75 | optional double max_change = 44; 76 | 77 | optional double multiplier = 45; 78 | 79 | optional double default_value = 46; 80 | } 81 | 82 | message OptimizationSettings { 83 | optional OptimizationSetting window_increment = 51; 84 | optional OptimizationSetting window_multiple = 52; 85 | optional OptimizationSetting intersend = 53; 86 | optional OptimizationSetting lambda = 54; 87 | } 88 | 89 | message Range { 90 | optional double low = 61; 91 | optional double high = 62; 92 | optional double incr = 63; 93 | } 94 | 95 | message ConfigRange { 96 | optional Range link_packets_per_ms = 71; 97 | optional Range rtt = 72; 98 | optional Range num_senders = 73; 99 | optional Range buffer_size = 74; 100 | optional Range mean_off_duration = 75; 101 | optional Range mean_on_duration = 76; 102 | optional uint32 simulation_ticks = 77; 103 | optional Range stochastic_loss_rate = 78; 104 | } 105 | 106 | message NetConfig { 107 | optional double mean_on_duration = 1; 108 | optional double mean_off_duration = 2; 109 | optional uint32 num_senders = 3; 110 | optional double link_ppt = 4; 111 | optional double delay = 5; 112 | optional uint32 buffer_size = 6; 113 | optional double stochastic_loss_rate = 7; 114 | } 115 | 116 | message ConfigVector { 117 | repeated NetConfig config = 81; 118 | } 119 | -------------------------------------------------------------------------------- /scripts/list_plots.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """Lists all the results in the results directory.""" 3 | 4 | import os 5 | import argparse 6 | import json 7 | 8 | DEFAULT_RESULTS_DIR = "results" 9 | 10 | parser = argparse.ArgumentParser(description=__doc__) 11 | parser.add_argument("directory", type=str, default=DEFAULT_RESULTS_DIR, nargs="?", 12 | help="Directory to look in (default '{}')".format(DEFAULT_RESULTS_DIR)) 13 | parser.add_argument("-v", "--verbosity", type=int, default=0, 14 | help="Verbosity, 0=normal, 1=print errors, 2=print skipped") 15 | args = parser.parse_args() 16 | 17 | directory = args.directory 18 | entries = os.listdir(directory) 19 | entries.sort() 20 | 21 | for entry in entries: 22 | 23 | dirname = os.path.join(directory, entry) 24 | 25 | if not os.path.isdir(dirname): 26 | if args.verbosity >= 2: 27 | print("{} - not a directory".format(dirname)) 28 | continue 29 | 30 | argsfilename = os.path.join(dirname, "args.json") 31 | try: 32 | argsfile = open(argsfilename, "r") 33 | except IOError as e: 34 | if args.verbosity >= 1: 35 | print("{} - could not open args.json: {}".format(dirname, e)) 36 | continue 37 | 38 | try: 39 | jsondict = json.load(argsfile) 40 | except ValueError as e: 41 | if args.verbosity >= 1: 42 | print("{} - could not parse args.json: {}".format(dirname, e)) 43 | continue 44 | argsfile.close() 45 | git_dict = jsondict.get("git", {}) 46 | git_branch = git_dict.get("branch", "") 47 | git_commit = git_dict.get("commit", "")[:9] 48 | args_dict = jsondict.get("args", {}) 49 | remyccs = args_dict.get("remycc", []) 50 | remyccs = " ".join([os.path.basename(r) for r in remyccs]) 51 | if remyccs: 52 | npoints = args_dict.get("num_points", -1) 53 | else: 54 | npoints = "-" 55 | 56 | replots = args_dict.get("replot", []) 57 | if replots: 58 | replots_str = (remyccs and ", replots:" or "replots:") 59 | for replot in replots: 60 | replot_basename = os.path.basename(replot) 61 | try: 62 | replot_argsfile = open(os.path.join(dirname, "replots", replot_basename, "args.json")) 63 | replot_args = json.load(replot_argsfile)["args"] 64 | replot_argsfile.close() 65 | replot_remyccs = " ".join([os.path.basename(r) for r in replot_args["remycc"]]) 66 | replot_npoints = replot_args["num_points"] 67 | except (IOError, KeyError): 68 | replots_str += " {basename}".format(basename=replot_basename) 69 | else: 70 | if replot_remyccs: 71 | replots_str += " {basename}[{remyccs}/{npoints}]".format(basename=replot_basename, 72 | remyccs=replot_remyccs, npoints=replot_npoints) 73 | else: 74 | replots_str += " {basename}[]".format(basename=replot_basename) 75 | else: 76 | replots_str = "" 77 | 78 | plotsdirname = os.path.join(dirname, "plots") 79 | noplots = "[no plots] " if os.path.isdir(plotsdirname) and len(os.listdir(plotsdirname)) == 0 else "" 80 | 81 | print("{dirname:30} {branch:14} {commit:9} {npoints:>5} {noplots}{remyccs}{replots}".format( 82 | dirname=dirname, branch=git_branch, commit=git_commit, npoints=npoints, 83 | remyccs=remyccs, noplots=noplots, replots=replots_str)) 84 | -------------------------------------------------------------------------------- /src/delay.hh: -------------------------------------------------------------------------------- 1 | #ifndef DELAY_HH 2 | #define DELAY_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "packet.hh" 11 | 12 | class Delay 13 | { 14 | private: 15 | std::deque< std::tuple< double, Packet, bool > > _queue; 16 | /* queue members: release time, contents, whether release time was adjusted after-the-fact */ 17 | double _delay; 18 | bool _adjusted_packets_are_in_flight; 19 | 20 | void fixup_adjusted_packets( const double tickno ) 21 | { 22 | if ( not _adjusted_packets_are_in_flight ) { 23 | return; 24 | } 25 | 26 | /* for packets that were in-flight when delay was reduced, 27 | make sure that they get released asap */ 28 | for ( auto & p : _queue ) { 29 | if ( std::get< 2 >( p ) and std::get< 0 >( p ) < tickno ) { 30 | std::get< 0 >( p ) = tickno; 31 | std::get< 2 >( p ) = false; 32 | } 33 | } 34 | 35 | _adjusted_packets_are_in_flight = false; 36 | } 37 | 38 | public: 39 | Delay( const double s_delay ) : _queue(), _delay( s_delay ), _adjusted_packets_are_in_flight( false ) {} 40 | 41 | void accept( const Packet & p, const double & tickno ) noexcept 42 | { 43 | /* Make sure that we haven't reordered packets when delay was adjusted 44 | on packets already in-flight */ 45 | if ( not _queue.empty() ) { 46 | assert( tickno + _delay >= std::get< 0 >( _queue.front() ) ); 47 | } 48 | 49 | _queue.emplace_back( tickno + _delay, p, false ); 50 | } 51 | 52 | template 53 | void tick( NextHop & next, const double & tickno ) 54 | { 55 | fixup_adjusted_packets( tickno ); 56 | 57 | while ( (!_queue.empty()) && (std::get< 0 >( _queue.front() ) <= tickno) ) { 58 | assert( std::get< 0 >( _queue.front() ) == tickno ); 59 | next.accept( std::get< 1 >( _queue.front() ), tickno ); 60 | _queue.pop_front(); 61 | } 62 | } 63 | 64 | double next_event_time( const double & tickno ) const 65 | { 66 | if ( _queue.empty() ) { 67 | return std::numeric_limits::max(); 68 | } 69 | 70 | if ( std::get< 0 >( _queue.front() ) < tickno 71 | and std::get< 2 >( _queue.front() ) ) { 72 | return tickno; /* packet's delay was adjusted to be earlier than present time, 73 | so just release asap */ 74 | } 75 | 76 | assert( std::get< 0 >( _queue.front() ) >= tickno ); 77 | 78 | return std::get< 0 >( _queue.front() ); 79 | } 80 | 81 | bool empty( void ) const { return _queue.empty(); } 82 | 83 | std::vector packets_in_flight( const unsigned int num_senders ) const 84 | { 85 | std::vector ret( num_senders ); 86 | for ( const auto & x : _queue ) { 87 | ret.at( std::get<1>( x ).src )++; 88 | } 89 | return ret; 90 | } 91 | 92 | void set_delay( const double delay ) 93 | { 94 | /* Step 1: By how much is the delay changing? */ 95 | const double delay_difference = delay - _delay; 96 | 97 | /* Step 2: Adjust existing packets-in-flight */ 98 | for ( auto & p : _queue ) { 99 | std::get< 0 >( p ) += delay_difference; 100 | 101 | if ( delay_difference < 0 ) { 102 | std::get< 2 >( p ) = true; 103 | _adjusted_packets_are_in_flight = true; 104 | } 105 | } 106 | 107 | /* Step 3: Change delay that will be applied to future packets */ 108 | _delay = delay; 109 | } 110 | 111 | const double & delay( void ) const { return _delay; } 112 | }; 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /src/action.hh: -------------------------------------------------------------------------------- 1 | #ifndef ACTION_HH 2 | #define ACTION_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "memoryrange.hh" 8 | #include "dna.pb.h" 9 | 10 | using namespace std; 11 | 12 | class Action { 13 | protected: 14 | unsigned int _generation; 15 | MemoryRange _domain; 16 | 17 | public: 18 | Action( const Action & other ) : _generation( other._generation ), _domain( other._domain ) {}; 19 | Action( const MemoryRange & s_domain ) : _generation( 0 ), _domain( s_domain ) {}; 20 | virtual ~Action() {}; 21 | 22 | virtual std::string str( const unsigned int total=1 ) const = 0; 23 | virtual void round( void ) = 0; 24 | 25 | template 26 | vector< T > bisect( void ) const { 27 | vector< T > ret; 28 | for ( auto &x : _domain.bisect() ) { 29 | T new_action( (T &)(* this) ); 30 | new_action._domain = x; 31 | ret.push_back( new_action ); 32 | } 33 | return ret; 34 | } 35 | 36 | void use( void ) const { _domain.use(); } 37 | void reset_count( void ) const { _domain.reset_count(); } 38 | unsigned int count( void ) const { return _domain.count(); } 39 | 40 | const unsigned int & generation( void ) const { return _generation; } 41 | const MemoryRange & domain( void ) const { return _domain; } 42 | 43 | void promote( const unsigned int generation ) { _generation = max( _generation, generation ); } 44 | void demote( const unsigned int generation ) { _generation = generation; } 45 | 46 | template < typename T > 47 | struct OptimizationSetting 48 | { 49 | T min_value; /* the smallest the value can be */ 50 | T max_value; /* the biggest */ 51 | 52 | T min_change; /* the smallest change to the value in an optimization exploration step */ 53 | T max_change; /* the biggest change */ 54 | 55 | T multiplier; /* we will explore multiples of the min_change until we hit the max_change */ 56 | /* the multiplier defines which multiple (e.g. 1, 2, 4, 8... or 1, 3, 9, 27... ) */ 57 | 58 | T default_value; 59 | 60 | bool eligible_value( const T & value ) const { return value >= min_value and value <= max_value; } 61 | 62 | std::vector< T > alternatives( const T & value, bool active ) const 63 | { 64 | if ( !eligible_value( value ) ) { 65 | printf("Ineligible value: %s is not between %s and %s\n", to_string( value ).c_str(), to_string( min_value ).c_str(), to_string( max_value ).c_str()); 66 | assert(false); 67 | } 68 | 69 | vector< T > ret( 1, value ); 70 | 71 | /* If this axis isn't active, return only the current value. */ 72 | if (!active) return ret; 73 | 74 | for ( T proposed_change = min_change; 75 | proposed_change <= max_change; 76 | proposed_change *= multiplier ) { 77 | /* explore positive change */ 78 | const T proposed_value_up = value + proposed_change; 79 | const T proposed_value_down = value - proposed_change; 80 | 81 | if ( eligible_value( proposed_value_up ) ) { 82 | ret.push_back( proposed_value_up ); 83 | } 84 | 85 | if ( eligible_value( proposed_value_down ) ) { 86 | ret.push_back( proposed_value_down ); 87 | } 88 | } 89 | 90 | return ret; 91 | } 92 | 93 | RemyBuffers::OptimizationSetting DNA( void ) const 94 | { 95 | RemyBuffers::OptimizationSetting ret; 96 | 97 | ret.set_min_value( min_value ); 98 | ret.set_max_value( max_value ); 99 | ret.set_min_change( min_change ); 100 | ret.set_max_change( max_change ); 101 | ret.set_multiplier( multiplier ); 102 | ret.set_default_value( default_value ); 103 | 104 | return ret; 105 | } 106 | }; 107 | }; 108 | 109 | #endif -------------------------------------------------------------------------------- /src/memoryrange.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "memoryrange.hh" 4 | 5 | using namespace std; 6 | using namespace boost::accumulators; 7 | 8 | std::vector< MemoryRange > MemoryRange::bisect( void ) const 9 | { 10 | vector< MemoryRange > ret { *this }; 11 | 12 | /* bisect in each active axis */ 13 | for ( auto & i : _active_axis ) { 14 | vector< MemoryRange > doubled; 15 | for ( const auto &x : ret ) { 16 | auto ersatz_lower( x._lower ), ersatz_upper( x._upper ); 17 | ersatz_lower.mutable_field( i ) = ersatz_upper.mutable_field( i ) = median( _acc[ i ] ); 18 | 19 | if ( x._lower == ersatz_upper ) { 20 | /* try range midpoint instead */ 21 | ersatz_lower.mutable_field( i ) = ersatz_upper.mutable_field( i ) = (x._lower.field( i ) + x._upper.field( i )) / 2; 22 | } 23 | 24 | if ( x._lower == ersatz_upper ) { 25 | assert( !(ersatz_lower == x._upper) ); 26 | assert( x._lower == ersatz_lower ); 27 | /* cannot double on this axis */ 28 | doubled.push_back( x ); 29 | } else { 30 | doubled.emplace_back( x._lower, ersatz_upper, x._active_axis ); 31 | doubled.emplace_back( ersatz_lower, x._upper, x._active_axis ); 32 | } 33 | } 34 | 35 | ret = doubled; 36 | } 37 | 38 | assert( !ret.empty()); 39 | 40 | return ret; 41 | } 42 | 43 | Memory MemoryRange::range_median( void ) const 44 | { 45 | Memory median_data( _lower ); 46 | for ( auto & i : _active_axis ) { 47 | median_data.mutable_field( i ) = (_lower.field( i ) + _upper.field( i )) / 2; 48 | } 49 | return median_data; 50 | } 51 | 52 | bool MemoryRange::contains( const Memory & query ) const 53 | { 54 | for ( auto & i : _active_axis ) { 55 | if (!((query.field(i) >= _lower.field(i)) && (query.field(i) < _upper.field(i)))) { return false; } 56 | } 57 | return true; 58 | } 59 | 60 | void MemoryRange::track( const Memory & query ) const 61 | { 62 | /* log it */ 63 | for ( auto & i : _active_axis ) { 64 | _acc[ i ]( query.field( i ) ); 65 | } 66 | } 67 | 68 | bool MemoryRange::operator==( const MemoryRange & other ) const 69 | { 70 | for ( auto & i : _active_axis ) { 71 | if (!((_lower.field(i) == other._lower.field(i)) && (_upper.field(i) == other._upper.field(i)))) { return false; } 72 | } 73 | return true; 74 | } 75 | 76 | string MemoryRange::str( void ) const 77 | { 78 | char tmp[ 256 ]; 79 | strcpy( tmp, "(lo=< "); 80 | for ( auto & i : _active_axis ) { 81 | strcat( tmp, _lower.str( i ).c_str() ); 82 | } 83 | strcat( tmp, ">, hi=< "); 84 | for ( auto & i : _active_axis ) { 85 | strcat( tmp, _upper.str( i ).c_str() ); 86 | } 87 | strcat( tmp, ">)"); 88 | return tmp; 89 | } 90 | 91 | RemyBuffers::MemoryRange MemoryRange::DNA( void ) const 92 | { 93 | RemyBuffers::MemoryRange ret; 94 | 95 | ret.mutable_lower()->CopyFrom( _lower.DNA() ); 96 | ret.mutable_upper()->CopyFrom( _upper.DNA() ); 97 | for (auto & x : _active_axis) { 98 | ret.add_active_axis(x); 99 | } 100 | 101 | return ret; 102 | } 103 | 104 | MemoryRange::MemoryRange( const RemyBuffers::MemoryRange & dna ) 105 | : _lower( true, dna.lower() ), 106 | _upper( false, dna.upper() ), 107 | _active_axis( ), 108 | _acc( Memory::datasize ), 109 | _count( 0 ) 110 | { 111 | for (auto & x : dna.active_axis()) { 112 | _active_axis.push_back( (Axis) x ); 113 | } 114 | /* Backward compatibility: old remys don't have active axis; set to default */ 115 | if ( _active_axis.empty() ) { 116 | _active_axis = vector( { RemyBuffers::MemoryRange::SEND_EWMA, RemyBuffers::MemoryRange::REC_EWMA, 117 | RemyBuffers::MemoryRange::RTT_RATIO, RemyBuffers::MemoryRange::SLOW_REC_EWMA } ); 118 | } 119 | } 120 | 121 | size_t hash_value( const MemoryRange & mr ) 122 | { 123 | size_t seed = 0; 124 | boost::hash_combine( seed, mr._lower ); 125 | boost::hash_combine( seed, mr._upper ); 126 | 127 | return seed; 128 | } 129 | -------------------------------------------------------------------------------- /src/whisker.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "whisker.hh" 7 | 8 | using namespace std; 9 | 10 | Whisker::Whisker( const unsigned int s_window_increment, const double s_window_multiple, const double s_intersend, const MemoryRange & s_domain ) 11 | : Action(s_domain), 12 | _window_increment( s_window_increment ), 13 | _window_multiple( s_window_multiple ), 14 | _intersend( s_intersend ) 15 | { 16 | } 17 | 18 | Whisker::Whisker( const Whisker & other ) 19 | : Action( other ), 20 | _window_increment( other._window_increment ), 21 | _window_multiple( other._window_multiple ), 22 | _intersend( other._intersend ) 23 | { 24 | } 25 | 26 | Whisker::Whisker( const RemyBuffers::Whisker & dna ) 27 | : Action( dna.domain() ), 28 | _window_increment( dna.window_increment() ), 29 | _window_multiple( dna.window_multiple() ), 30 | _intersend( dna.intersend() ) 31 | { 32 | } 33 | 34 | RemyBuffers::Whisker Whisker::DNA( void ) const 35 | { 36 | RemyBuffers::Whisker ret; 37 | 38 | ret.set_window_increment( _window_increment ); 39 | ret.set_window_multiple( _window_multiple ); 40 | ret.set_intersend( _intersend ); 41 | ret.mutable_domain()->CopyFrom( _domain.DNA() ); 42 | 43 | return ret; 44 | } 45 | 46 | vector< Whisker > Whisker::next_generation( bool optimize_window_increment, bool optimize_window_multiple, bool optimize_intersend ) const 47 | { 48 | vector< Whisker> ret; 49 | 50 | auto window_increment_alternatives = get_optimizer().window_increment.alternatives( _window_increment, optimize_window_increment ); 51 | auto window_multiple_alternatives = get_optimizer().window_multiple.alternatives( _window_multiple, optimize_window_multiple ); 52 | auto intersend_alternatives = get_optimizer().intersend.alternatives( _intersend, optimize_intersend ); 53 | 54 | printf("Alternatives: window increment %u to %u, window multiple %f to %f, intersend %f to %f\n", 55 | *(min_element(window_increment_alternatives.begin(), window_increment_alternatives.end())), 56 | *(max_element(window_increment_alternatives.begin(), window_increment_alternatives.end())), 57 | *(min_element(window_multiple_alternatives.begin(), window_multiple_alternatives.end())), 58 | *(max_element(window_multiple_alternatives.begin(), window_multiple_alternatives.end())), 59 | *(min_element(intersend_alternatives.begin(), intersend_alternatives.end())), 60 | *(max_element(intersend_alternatives.begin(), intersend_alternatives.end())) 61 | ); 62 | 63 | for ( const auto & alt_window : window_increment_alternatives ) { 64 | for ( const auto & alt_multiple : window_multiple_alternatives ) { 65 | for ( const auto & alt_intersend : intersend_alternatives ) { 66 | Whisker new_whisker { *this }; 67 | new_whisker._generation++; 68 | 69 | new_whisker._window_increment = alt_window; 70 | new_whisker._window_multiple = alt_multiple; 71 | new_whisker._intersend = alt_intersend; 72 | 73 | new_whisker.round(); 74 | 75 | ret.push_back( new_whisker ); 76 | } 77 | } 78 | } 79 | 80 | return ret; 81 | } 82 | 83 | string Whisker::str( const unsigned int total ) const 84 | { 85 | char tmp[ 256 ]; 86 | snprintf( tmp, 256, "{%s} gen=%u usage=%.4f => (win=%d + %f * win, intersend=%f)", 87 | _domain.str().c_str(), _generation, double( _domain.count() ) / double( total ), _window_increment, _window_multiple, _intersend ); 88 | return tmp; 89 | } 90 | 91 | void Whisker::round( void ) 92 | { 93 | _window_multiple = (1.0/10000.0) * int( 10000 * _window_multiple ); 94 | _intersend = (1.0/10000.0) * int( 10000 * _intersend ); 95 | } 96 | 97 | size_t hash_value( const Whisker & whisker ) 98 | { 99 | size_t seed = 0; 100 | boost::hash_combine( seed, whisker._window_increment ); 101 | boost::hash_combine( seed, whisker._window_multiple ); 102 | boost::hash_combine( seed, whisker._intersend ); 103 | boost::hash_combine( seed, whisker._domain ); 104 | 105 | return seed; 106 | } 107 | -------------------------------------------------------------------------------- /src/network.hh: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_HH 2 | #define NETWORK_HH 3 | 4 | #include 5 | 6 | #include "sendergangofgangs.hh" 7 | #include "link.hh" 8 | #include "delay.hh" 9 | #include "stochastic-loss.hh" 10 | #include "receiver.hh" 11 | #include "random.hh" 12 | #include "answer.pb.h" 13 | 14 | class SimulationRunData; // from simulationresults.hh 15 | 16 | class NetConfig 17 | { 18 | public: 19 | double mean_on_duration, mean_off_duration; 20 | double num_senders; 21 | double link_ppt; 22 | double delay; 23 | double buffer_size; 24 | double stochastic_loss_rate; 25 | 26 | NetConfig( void ) 27 | : mean_on_duration( 5000.0 ), 28 | mean_off_duration( 5000.0 ), 29 | num_senders( 8 ), 30 | link_ppt( 1.0 ), 31 | delay( 150 ), 32 | buffer_size( std::numeric_limits::max() ), 33 | stochastic_loss_rate( 0 ) 34 | {} 35 | 36 | NetConfig( const RemyBuffers::NetConfig & dna ) 37 | : mean_on_duration( dna.mean_on_duration() ), 38 | mean_off_duration( dna.mean_off_duration() ), 39 | num_senders( dna.num_senders() ), 40 | link_ppt( dna.link_ppt() ), 41 | delay( dna.delay() ), 42 | buffer_size( dna.buffer_size() ), 43 | stochastic_loss_rate( dna.stochastic_loss_rate() ) 44 | {} 45 | 46 | NetConfig & set_link_ppt( const double s_link_ppt ) { link_ppt = s_link_ppt; return *this; } 47 | NetConfig & set_delay( const double s_delay ) { delay = s_delay; return *this; } 48 | NetConfig & set_num_senders( const unsigned int n ) { num_senders = n; return *this; } 49 | NetConfig & set_on_duration( const double & duration ) { mean_on_duration = duration; return *this; } 50 | NetConfig & set_off_duration( const double & duration ) { mean_off_duration = duration; return *this; } 51 | NetConfig & set_buffer_size( const unsigned int n ) { buffer_size = n; return *this; } 52 | NetConfig & set_stochastic_loss_rate( const double loss_rate ) { stochastic_loss_rate = loss_rate; return *this; } 53 | 54 | RemyBuffers::NetConfig DNA( void ) const 55 | { 56 | RemyBuffers::NetConfig ret; 57 | ret.set_mean_on_duration( mean_on_duration ); 58 | ret.set_mean_off_duration( mean_off_duration ); 59 | ret.set_num_senders( num_senders ); 60 | ret.set_delay( delay ); 61 | ret.set_link_ppt( link_ppt ); 62 | ret.set_buffer_size( buffer_size ); 63 | ret.set_stochastic_loss_rate( stochastic_loss_rate ); 64 | return ret; 65 | } 66 | 67 | std::string str( void ) const 68 | { 69 | char tmp[ 256 ]; 70 | snprintf( tmp, 256, "mean_on=%f, mean_off=%f, nsrc=%f, link_ppt=%f, delay=%f, buffer_size=%f, stochastic_loss_rate = %f\n", 71 | mean_on_duration, mean_off_duration, num_senders, link_ppt, delay, buffer_size, stochastic_loss_rate ); 72 | return tmp; 73 | } 74 | }; 75 | 76 | template 77 | class Network 78 | { 79 | private: 80 | PRNG & _prng; 81 | SenderGangofGangs _senders; 82 | Link _link; 83 | Delay _delay; 84 | Receiver _rec; 85 | 86 | double _tickno; 87 | StochasticLoss _stochastic_loss; 88 | void tick( void ); 89 | 90 | public: 91 | Network( const typename Gang1Type::Sender & example_sender1, const typename Gang2Type::Sender & example_sender2, PRNG & s_prng, const NetConfig & config ); 92 | 93 | Network( const typename Gang1Type::Sender & example_sender1, PRNG & s_prng, const NetConfig & config ); 94 | 95 | void run_simulation( const double & duration ); 96 | 97 | void run_simulation_with_logging_until( const double tick_limit, SimulationRunData &, const double interval ); 98 | 99 | void run_simulation_until( const double tick_limit ); 100 | 101 | const SenderGangofGangs & senders( void ) const { return _senders; } 102 | 103 | SenderGangofGangs & mutable_senders( void ) { return _senders; } 104 | 105 | std::vector< unsigned int > packets_in_flight( void ) const; 106 | 107 | const double & tickno( void ) const { return _tickno; } 108 | 109 | Link & mutable_link( void ) { return _link; } 110 | 111 | Delay & mutable_delay( void ) { return _delay; } 112 | }; 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /graph/fader.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "fader.hh" 7 | 8 | using namespace std; 9 | using namespace Glib; 10 | using namespace Gtk; 11 | 12 | class LabeledToggle 13 | { 14 | CheckButton control_ {}; 15 | 16 | public: 17 | LabeledToggle( Container & parent, mutex & the_mutex, const string & text, std::atomic & variable ) 18 | : control_( text ) 19 | { 20 | control_.signal_toggled().connect_notify( [&] () { 21 | unique_lock ul( the_mutex ); 22 | variable = control_.get_active(); 23 | } ); 24 | parent.add( control_ ); 25 | }; 26 | }; 27 | 28 | class LabeledScale 29 | { 30 | VBox label_and_control_ {}; 31 | Label label_ {}; 32 | VScale control_; 33 | 34 | public: 35 | LabeledScale( Container & parent, 36 | const string & text, const double minval, const double maxval, const double incr, 37 | const double multiplier, std::atomic & variable ) 38 | : control_( minval, maxval, incr ) 39 | { 40 | label_.set_markup( text ); 41 | label_.set_padding( 10, 10 ); 42 | control_.set_inverted(); 43 | control_.set_value( variable * multiplier ); 44 | control_.signal_change_value().connect_notify( [&] ( const ScrollType &, const double & val ) { 45 | variable = min( maxval, max( minval, val ) ) / multiplier; 46 | } ); 47 | 48 | label_and_control_.pack_start( label_, PACK_SHRINK ); 49 | label_and_control_.pack_start( control_, PACK_EXPAND_WIDGET ); 50 | 51 | parent.add( label_and_control_ ); 52 | } 53 | }; 54 | 55 | GTKFader::GTKFader( const unsigned int & num_senders ) 56 | : remy_( new atomic[ num_senders ] ), 57 | aimd_( new atomic[ num_senders ] ) 58 | { 59 | for ( unsigned int i = 0; i < num_senders; i++ ) { 60 | remy_.get()[ i ] = false; 61 | aimd_.get()[ i ] = false; 62 | } 63 | 64 | thread newthread( [&] () { 65 | RefPtr app = Application::create(); 66 | 67 | Window window; 68 | window.set_default_size( 200, 400 ); 69 | 70 | VBox stack; 71 | window.add( stack ); 72 | 73 | /* AIMD and RemyCC controls */ 74 | deque sender_controls; 75 | 76 | HBox aimd_senders; 77 | stack.pack_start( aimd_senders, PACK_SHRINK ); 78 | for ( unsigned int i = 0; i < num_senders; i++ ) { 79 | sender_controls.emplace_back( aimd_senders, mutex_, "AIMD" + to_string(i + 1), aimd_.get()[ i ] ); 80 | } 81 | 82 | HBox remy_senders; 83 | stack.pack_start( remy_senders, PACK_SHRINK ); 84 | for ( unsigned int i = 0; i < num_senders; i++ ) { 85 | sender_controls.emplace_back( remy_senders, mutex_, "RemyCC" + to_string(i + 1), remy_.get()[ i ] ); 86 | } 87 | 88 | /* numerical sliders */ 89 | HBox numeric; 90 | stack.pack_start( numeric ); 91 | 92 | LabeledScale link_rate( numeric, "Link rate (Mbps)", 0.3, 300.1, 0.1, 10, link_rate_ ); 93 | LabeledScale rtt( numeric, "RTT (ms)", 5, 500, 5, 1, rtt_ ); 94 | LabeledScale buffer( numeric, "Buffer cap (pkts)", 0, 20000, 1, 1, buffer_size_ ); 95 | LabeledScale speed( numeric, "Speed (%)", 0, 5000, 1, 60 * 100, time_increment_ ); 96 | LabeledScale width( numeric, "Width (s)", 1, 100, 0.1, 1, horizontal_size_ ); 97 | 98 | /* scaling buttons */ 99 | HBox buttons; 100 | stack.pack_start( buttons, PACK_SHRINK, 10 ); 101 | 102 | HBox spacer1, spacer2; 103 | buttons.pack_start( spacer1 ); 104 | buttons.pack_end( spacer2 ); 105 | 106 | Button autoscaler( "Scale to packets in flight" ); 107 | autoscaler.signal_clicked().connect_notify( [&] () { autoscale_ = true; } ); 108 | buttons.pack_start( autoscaler, PACK_SHRINK, 10 ); 109 | 110 | Button superautoscaler( "Scale to full buffer + BDP" ); 111 | superautoscaler.signal_clicked().connect_notify( [&] () { autoscale_all_ = true; } ); 112 | buttons.pack_start( superautoscaler, PACK_SHRINK, 10 ); 113 | 114 | window.show_all(); 115 | 116 | app->run( window, 0, nullptr ); 117 | quit_ = true; 118 | } ); 119 | 120 | newthread.detach(); 121 | } 122 | -------------------------------------------------------------------------------- /graph/cairo_objects.hh: -------------------------------------------------------------------------------- 1 | #ifndef CAIRO_OBJECTS_HH 2 | #define CAIRO_OBJECTS_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "image.hh" 10 | 11 | class Cairo 12 | { 13 | Image image_; 14 | 15 | struct Surface 16 | { 17 | struct Deleter { void operator() ( cairo_surface_t * x ) { cairo_surface_destroy( x ); } }; 18 | 19 | std::unique_ptr surface; 20 | 21 | Surface( Image & image ); 22 | 23 | void check_error( void ); 24 | } surface_; 25 | 26 | struct Context 27 | { 28 | struct Deleter { void operator() ( cairo_t * x ) { cairo_destroy( x ); } }; 29 | 30 | std::unique_ptr context; 31 | 32 | Context( Surface & surface ); 33 | 34 | void check_error( void ); 35 | } context_; 36 | 37 | static int stride_pixels_for_width( const unsigned int width ); 38 | void check_error( void ); 39 | 40 | public: 41 | Cairo( const std::pair size ); 42 | 43 | operator cairo_t * () { return context_.context.get(); } 44 | 45 | Image & mutable_image( void ) { return image_; } 46 | const Image & image( void ) const { return image_; } 47 | 48 | template 49 | struct Extent 50 | { 51 | double x, y, width, height; 52 | 53 | Extent to_user( Cairo & cairo ) const 54 | { 55 | static_assert( device_coordinates == true, 56 | "Extent::to_user() called but coordinates already in user-space" ); 57 | 58 | double x1 = x, x2 = x + width, y1 = y, y2 = y + height; 59 | 60 | cairo_device_to_user( cairo, &x1, &y1 ); 61 | cairo_device_to_user( cairo, &x2, &y2 ); 62 | 63 | return Extent( { x1, y1, x2 - x1, y2 - y1 } ); 64 | } 65 | 66 | Extent to_device( Cairo & cairo ) const 67 | { 68 | static_assert( device_coordinates == false, 69 | "Extent::to_device() called but coordinates already in device-space" ); 70 | 71 | double x1 = x, x2 = x + width, y1 = y, y2 = y + height; 72 | 73 | cairo_user_to_device( cairo, &x1, &y1 ); 74 | cairo_user_to_device( cairo, &x2, &y2 ); 75 | 76 | return Extent( { x1, y1, x2 - x1, y2 - y1 } ); 77 | } 78 | }; 79 | 80 | class Pattern 81 | { 82 | struct Deleter { void operator() ( cairo_pattern_t * x ) { cairo_pattern_destroy( x ); } }; 83 | 84 | std::unique_ptr pattern_; 85 | 86 | public: 87 | Pattern( cairo_pattern_t * pattern ); 88 | 89 | operator cairo_pattern_t * () { return pattern_.get(); } 90 | }; 91 | }; 92 | 93 | template 94 | struct PangoDelete { void operator() ( T * x ) { g_object_unref( x ); } }; 95 | 96 | class Pango 97 | { 98 | std::unique_ptr> context_; 99 | std::unique_ptr> layout_; 100 | 101 | public: 102 | Pango( Cairo & cairo ); 103 | 104 | operator PangoContext * () { return context_.get(); } 105 | operator PangoLayout * () { return layout_.get(); } 106 | 107 | struct Font 108 | { 109 | struct Deleter { void operator() ( PangoFontDescription * x ) { pango_font_description_free( x ); } }; 110 | 111 | std::unique_ptr font; 112 | 113 | Font( const std::string & description ); 114 | 115 | operator const PangoFontDescription * () const { return font.get(); } 116 | }; 117 | 118 | class Text 119 | { 120 | struct Deleter { void operator() ( cairo_path_t * x ) { cairo_path_destroy( x ); } }; 121 | 122 | std::unique_ptr path_; 123 | Cairo::Extent extent_; 124 | 125 | public: 126 | Text( Cairo & cairo, Pango & pango, const Font & font, const std::string & text ); 127 | 128 | const Cairo::Extent & extent( void ) const { return extent_; } 129 | 130 | void draw_centered_at( Cairo & cairo, const double x, const double y, const double max_width = std::numeric_limits::max() ) const; 131 | void draw_centered_rotated_at( Cairo & cairo, const double x, const double y ) const; 132 | 133 | operator const cairo_path_t * () const { return path_.get(); } 134 | }; 135 | 136 | void set_font( const Font & font ); 137 | }; 138 | 139 | #endif /* CAIRO_OBJECTS_HH */ 140 | -------------------------------------------------------------------------------- /graph/gl_objects.hh: -------------------------------------------------------------------------------- 1 | #ifndef GL_OBJECTS_HH 2 | #define GL_OBJECTS_HH 3 | 4 | #define GLEW_STATIC 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class Image; 14 | 15 | class GLFWContext 16 | { 17 | static void error_callback( const int, const char * const description ); 18 | 19 | public: 20 | GLFWContext(); 21 | ~GLFWContext(); 22 | 23 | /* forbid copy */ 24 | GLFWContext( const GLFWContext & other ) = delete; 25 | GLFWContext & operator=( const GLFWContext & other ) = delete; 26 | }; 27 | 28 | class Window 29 | { 30 | struct Deleter { void operator() ( GLFWwindow * x ) const; }; 31 | 32 | std::unique_ptr window_; 33 | 34 | public: 35 | Window( const unsigned int width, const unsigned int height, const std::string & title ); 36 | void make_context_current( const bool initialize_extensions = false ); 37 | bool should_close( void ) const; 38 | void swap_buffers( void ); 39 | void hide_cursor( const bool hidden ); 40 | bool key_pressed( const int key ) const; 41 | std::pair size( void ) const; 42 | }; 43 | 44 | template 45 | class Buffer 46 | { 47 | public: 48 | Buffer() = delete; 49 | 50 | template 51 | static void bind( const T & obj ) 52 | { 53 | glBindBuffer( id_, obj.num_ ); 54 | } 55 | 56 | static void load( const std::vector> & vertices, const GLenum usage ) 57 | { 58 | glBufferData( id, vertices.size() * sizeof( std::pair ), &vertices.front(), usage ); 59 | } 60 | 61 | constexpr static GLenum id = id_; 62 | }; 63 | 64 | using ArrayBuffer = Buffer; 65 | 66 | class VertexBufferObject 67 | { 68 | friend ArrayBuffer; 69 | 70 | GLuint num_; 71 | 72 | public: 73 | VertexBufferObject(); 74 | ~VertexBufferObject(); 75 | 76 | /* forbid copy */ 77 | VertexBufferObject( const VertexBufferObject & other ) = delete; 78 | VertexBufferObject & operator=( const VertexBufferObject & other ) = delete; 79 | }; 80 | 81 | class VertexArrayObject 82 | { 83 | GLuint num_; 84 | 85 | public: 86 | VertexArrayObject(); 87 | ~VertexArrayObject(); 88 | 89 | void bind( void ); 90 | 91 | /* forbid copy */ 92 | VertexArrayObject( const VertexArrayObject & other ) = delete; 93 | VertexArrayObject & operator=( const VertexArrayObject & other ) = delete; 94 | }; 95 | 96 | class Texture 97 | { 98 | GLuint num_; 99 | 100 | unsigned int width_, height_; 101 | 102 | public: 103 | Texture( const unsigned int width, const unsigned int height); 104 | ~Texture(); 105 | 106 | void bind( void ); 107 | void load( const Image & image ); 108 | void resize( const unsigned int width, const unsigned int height ); 109 | std::pair size( void ) const { return std::make_pair( width_, height_ ); } 110 | 111 | /* disallow copy */ 112 | Texture( const Texture & other ) = delete; 113 | Texture & operator=( const Texture & other ) = delete; 114 | }; 115 | 116 | void compile_shader( const GLuint num, const std::string & source ); 117 | 118 | template 119 | class Shader 120 | { 121 | friend class Program; 122 | 123 | GLuint num_ = glCreateShader( type_ ); 124 | 125 | public: 126 | Shader( const std::string & source ) 127 | { 128 | compile_shader( num_, source ); 129 | } 130 | 131 | ~Shader() 132 | { 133 | glDeleteShader( num_ ); 134 | } 135 | 136 | /* forbid copy */ 137 | Shader( const Shader & other ) = delete; 138 | Shader & operator=( const Shader & other ) = delete; 139 | }; 140 | 141 | class Program 142 | { 143 | GLuint num_ = glCreateProgram(); 144 | 145 | public: 146 | Program() {} 147 | ~Program(); 148 | 149 | template 150 | void attach( const Shader & shader ) 151 | { 152 | glAttachShader( num_, shader.num_ ); 153 | } 154 | 155 | void link( void ); 156 | void use( void ); 157 | 158 | GLint attribute_location( const std::string & name ) const; 159 | GLint uniform_location( const std::string & name ) const; 160 | 161 | /* forbid copy */ 162 | Program( const Program & other ) = delete; 163 | Program & operator=( const Program & other ) = delete; 164 | }; 165 | 166 | using VertexShader = Shader; 167 | using FragmentShader = Shader; 168 | 169 | void glCheck( const std::string & where, const bool expected = false ); 170 | 171 | #endif /* GL_OBJECTS_HH */ 172 | -------------------------------------------------------------------------------- /src/memory.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "memory.hh" 6 | 7 | using namespace std; 8 | 9 | static const double alpha = 1.0 / 8.0; 10 | 11 | static const double slow_alpha = 1.0 / 256.0; 12 | 13 | void Memory::packets_received( const vector< Packet > & packets, const unsigned int flow_id, 14 | const int largest_ack ) 15 | { 16 | for ( const auto &x : packets ) { 17 | if ( x.flow_id != flow_id ) { 18 | continue; 19 | } 20 | 21 | const double rtt = x.tick_received - x.tick_sent; 22 | int pkt_outstanding = 1; 23 | if ( x.seq_num > largest_ack ) { 24 | pkt_outstanding = x.seq_num - largest_ack; 25 | } 26 | if ( _last_tick_sent == 0 || _last_tick_received == 0 ) { 27 | _last_tick_sent = x.tick_sent; 28 | _last_tick_received = x.tick_received; 29 | _min_rtt = rtt; 30 | } else { 31 | _rec_send_ewma = (1 - alpha) * _rec_send_ewma + alpha * (x.tick_sent - _last_tick_sent); 32 | _rec_rec_ewma = (1 - alpha) * _rec_rec_ewma + alpha * (x.tick_received - _last_tick_received); 33 | _slow_rec_rec_ewma = (1 - slow_alpha) * _slow_rec_rec_ewma + slow_alpha * (x.tick_received - _last_tick_received); 34 | 35 | _last_tick_sent = x.tick_sent; 36 | _last_tick_received = x.tick_received; 37 | 38 | _min_rtt = min( _min_rtt, rtt ); 39 | _rtt_ratio = double( rtt ) / double( _min_rtt ); 40 | assert( _rtt_ratio >= 1.0 ); 41 | _rtt_diff = rtt - _min_rtt; 42 | assert( _rtt_diff >= 0 ); 43 | _queueing_delay = _rec_rec_ewma * pkt_outstanding; 44 | } 45 | } 46 | } 47 | 48 | string Memory::str( void ) const 49 | { 50 | char tmp[ 256 ]; 51 | snprintf( tmp, 256, "sewma=%f, rewma=%f, rttr=%f, slowrewma=%f, rttd=%f, qdelay=%f", _rec_send_ewma, _rec_rec_ewma, _rtt_ratio, _slow_rec_rec_ewma, _rtt_diff, _queueing_delay ); 52 | return tmp; 53 | } 54 | 55 | string Memory::str( unsigned int num ) const 56 | { 57 | char tmp[ 50 ]; 58 | switch ( num ) { 59 | case 0: 60 | snprintf( tmp, 50, "sewma=%f ", _rec_send_ewma ); 61 | break; 62 | case 1: 63 | snprintf( tmp, 50, "rewma=%f ", _rec_rec_ewma ); 64 | break; 65 | case 2: 66 | snprintf( tmp, 50, "rttr=%f ", _rtt_ratio ); 67 | break; 68 | case 3: 69 | snprintf( tmp, 50, "slowrewma=%f ", _slow_rec_rec_ewma ); 70 | break; 71 | case 4: 72 | snprintf( tmp, 50, "rttd=%f ", _rtt_diff ); 73 | break; 74 | case 5: 75 | snprintf( tmp, 50, "qdelay=%f ", _queueing_delay ); 76 | break; 77 | } 78 | return tmp; 79 | } 80 | 81 | const Memory & MAX_MEMORY( void ) 82 | { 83 | static const Memory max_memory( { 163840, 163840, 163840, 163840, 163840, 163840 } ); 84 | return max_memory; 85 | } 86 | 87 | RemyBuffers::Memory Memory::DNA( void ) const 88 | { 89 | RemyBuffers::Memory ret; 90 | ret.set_rec_send_ewma( _rec_send_ewma ); 91 | ret.set_rec_rec_ewma( _rec_rec_ewma ); 92 | ret.set_rtt_ratio( _rtt_ratio ); 93 | ret.set_slow_rec_rec_ewma( _slow_rec_rec_ewma ); 94 | ret.set_rtt_diff( _rtt_diff ); 95 | ret.set_queueing_delay( _queueing_delay ); 96 | return ret; 97 | } 98 | 99 | /* If fields are missing in the DNA, we want to wildcard the resulting rule to match anything */ 100 | #define get_val_or_default( protobuf, field, limit ) \ 101 | ( (protobuf).has_ ## field() ? (protobuf).field() : (limit) ? 0 : 163840 ) 102 | 103 | Memory::Memory( const bool is_lower_limit, const RemyBuffers::Memory & dna ) 104 | : _rec_send_ewma( get_val_or_default( dna, rec_send_ewma, is_lower_limit ) ), 105 | _rec_rec_ewma( get_val_or_default( dna, rec_rec_ewma, is_lower_limit ) ), 106 | _rtt_ratio( get_val_or_default( dna, rtt_ratio, is_lower_limit ) ), 107 | _slow_rec_rec_ewma( get_val_or_default( dna, slow_rec_rec_ewma, is_lower_limit ) ), 108 | _rtt_diff( get_val_or_default( dna, rtt_diff, is_lower_limit ) ), 109 | _queueing_delay( get_val_or_default( dna, queueing_delay, is_lower_limit ) ), 110 | _last_tick_sent( 0 ), 111 | _last_tick_received( 0 ), 112 | _min_rtt( 0 ) 113 | { 114 | } 115 | 116 | size_t hash_value( const Memory & mem ) 117 | { 118 | size_t seed = 0; 119 | boost::hash_combine( seed, mem._rec_send_ewma ); 120 | boost::hash_combine( seed, mem._rec_rec_ewma ); 121 | boost::hash_combine( seed, mem._rtt_ratio ); 122 | boost::hash_combine( seed, mem._slow_rec_rec_ewma ); 123 | boost::hash_combine( seed, mem._rtt_diff ); 124 | boost::hash_combine( seed, mem._queueing_delay ); 125 | 126 | return seed; 127 | } 128 | -------------------------------------------------------------------------------- /scripts/remy_tool_runner.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import subprocess 3 | from warnings import warn 4 | 5 | ROOTDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 6 | HLINE2 = "=" * 80 + "\n" 7 | 8 | class BaseRemyToolRunner(object): 9 | """Manages the running of Remy tool.""" 10 | 11 | def __init__(self, **kwargs): 12 | self.command = kwargs.pop('command', self.COMMAND) 13 | 14 | self.default_parameters = dict(self.general_default_parameters) 15 | for _, key in self.program_parameters: 16 | if key in kwargs: 17 | self.default_parameters[key] = kwargs.pop(key) 18 | 19 | if kwargs: 20 | warn("Unrecognized keyword arguments: {}".format(kwargs)) 21 | 22 | def _get_parameters(self, parameters, quiet=False): 23 | """Returns a dict of parameters, including default parameters. 24 | Warns if any parameters were unrecognized.""" 25 | recognized = [p[1] for p in self.program_parameters] 26 | unrecognized = [k for k in parameters if k not in recognized] 27 | if unrecognized and not quiet: 28 | warn("Unrecognized parameters: {}".format(unrecognized)) 29 | result = dict(self.default_parameters) 30 | result.update(parameters) 31 | return result 32 | 33 | @staticmethod 34 | def _write_to_file(command, output, outfile=None): 35 | """Writes the given command and output to a given file.""" 36 | 37 | if not outfile: 38 | return 39 | if isinstance(outfile, str): 40 | outfile = open(outfile, "w") 41 | outfile_was_str = True 42 | else: 43 | outfile_was_str = False 44 | 45 | outfile.writelines([ 46 | HLINE2, 47 | "This was the console output for the command:\n", 48 | " " + " ".join(command) + "\n", 49 | HLINE2, 50 | "\n" 51 | ]) 52 | outfile.write(output) 53 | 54 | if outfile_was_str: 55 | outfile.close() 56 | 57 | def run(self, remyccfilename, parameters={}, outfile=None): 58 | """Runs the executable with the given parameters and returns the output 59 | (from both stdout and stderr). 60 | 61 | `remyccfilename` is the name of the RemyCC to test. 62 | `parameters` is a dict of parameters. If any required parameter is not 63 | specified, the default for this instance is used. 64 | If `outfile` is specified, it must either be a file object or a string, 65 | and the output will be written to it. 66 | """ 67 | parameters = self._get_parameters(parameters) 68 | command = [self.command, "if={:s}".format(remyccfilename)] 69 | command += ["{}={}".format(rroptname, parameters[paramname]) for rroptname, paramname in 70 | self.program_parameters if paramname in parameters] 71 | output = subprocess.check_output(command, stderr=subprocess.STDOUT) 72 | output = output.decode() 73 | self._write_to_file(command, output, outfile) 74 | return output 75 | 76 | 77 | class SenderRunnerRunner(BaseRemyToolRunner): 78 | 79 | general_default_parameters = { 80 | 'nsenders': 2, 81 | 'link_ppt': 1.0, 82 | 'delay': 150.0, 83 | 'mean_on': 5000.0, 84 | 'mean_off': 5000.0, 85 | 'buffer_size': 'inf', 86 | 'sim_time': 1000.0, 87 | } 88 | 89 | program_parameters = [ 90 | # (sender-runner option name, Python option name) 91 | ("sender", "sender"), 92 | ("nsrc", "nsenders"), 93 | ("link", "link_ppt"), 94 | ("rtt", "delay"), 95 | ("on", "mean_on"), 96 | ("off", "mean_off"), 97 | ("buf", "buffer_size"), 98 | ("time", "sim_time"), 99 | ] 100 | 101 | COMMAND = os.path.join(ROOTDIR, "src", "sender-runner") 102 | 103 | class SenderLoggerRunner(BaseRemyToolRunner): 104 | 105 | general_default_parameters = { 106 | 'nsenders': 2, 107 | 'link_ppt': 1.0, 108 | 'delay': 150.0, 109 | 'buffer_size': 'inf', 110 | 'interval': 1.0, 111 | 'sim_time': 1000.0, 112 | } 113 | 114 | program_parameters = [ 115 | # (sender-runner option name, Python option name) 116 | ("sender", "sender"), 117 | ("nsrc", "nsenders"), 118 | ("link", "link_ppt"), 119 | ("rtt", "delay"), 120 | ("buf", "buffer_size"), 121 | ("interval", "interval"), 122 | ("time", "sim_time"), 123 | ("of", "datafile"), 124 | ("sender1on", "sender1_on"), 125 | ] 126 | 127 | COMMAND = os.path.join(ROOTDIR, "src", "sender-logger") 128 | -------------------------------------------------------------------------------- /src/network.cc: -------------------------------------------------------------------------------- 1 | #include "network.hh" 2 | #include "simulationresults.hh" 3 | #include "sendergangofgangs.cc" 4 | #include "link-templates.cc" 5 | #include "stochastic-loss.hh" 6 | template 7 | Network::Network( const typename Gang1Type::Sender & example_sender1, 8 | const typename Gang2Type::Sender & example_sender2, 9 | PRNG & s_prng, 10 | const NetConfig & config ) 11 | : _prng( s_prng ), 12 | _senders( Gang1Type( config.mean_on_duration, config.mean_off_duration, config.num_senders, example_sender1, _prng ), 13 | Gang2Type( config.mean_on_duration, config.mean_off_duration, config.num_senders, example_sender2, _prng, config.num_senders ) ), 14 | _link( config.link_ppt, config.buffer_size ), 15 | _delay( config.delay ), 16 | _rec(), 17 | _tickno( 0 ), 18 | _stochastic_loss( config.stochastic_loss_rate , _prng) 19 | { 20 | } 21 | 22 | template 23 | Network::Network( const typename Gang1Type::Sender & example_sender1, 24 | PRNG & s_prng, 25 | const NetConfig & config ) 26 | : _prng( s_prng ), 27 | _senders( Gang1Type( config.mean_on_duration, config.mean_off_duration, config.num_senders, example_sender1, _prng ), 28 | Gang2Type() ), 29 | _link( config.link_ppt, config.buffer_size ), 30 | _delay( config.delay ), 31 | _rec(), 32 | _tickno( 0 ), 33 | _stochastic_loss( config.stochastic_loss_rate , _prng) 34 | { 35 | } 36 | 37 | template 38 | void Network::tick( void ) 39 | { 40 | _senders.tick( _link, _rec, _tickno ); 41 | _link.tick( _stochastic_loss, _tickno ); 42 | _stochastic_loss.tick( _delay, _tickno ); 43 | _delay.tick( _rec, _tickno ); 44 | } 45 | 46 | template 47 | void Network::run_simulation( const double & duration ) 48 | { 49 | assert( _tickno == 0 ); 50 | 51 | while ( _tickno < duration ) { 52 | /* find element with soonest event */ 53 | _tickno = min( min( _senders.next_event_time( _tickno ), 54 | min(_link.next_event_time( _tickno ), _stochastic_loss.next_event_time( _tickno)) ), 55 | min( _delay.next_event_time( _tickno ), 56 | _rec.next_event_time( _tickno ) ) ); 57 | 58 | if ( _tickno > duration ) break; 59 | assert( _tickno < std::numeric_limits::max() ); 60 | 61 | tick(); 62 | } 63 | } 64 | 65 | template 66 | void Network::run_simulation_until( const double tick_limit ) 67 | { 68 | if ( _tickno >= tick_limit ) { 69 | return; 70 | } 71 | 72 | while ( true ) { 73 | /* find element with soonest event */ 74 | double next_tickno = min( min( _senders.next_event_time( _tickno ), 75 | _link.next_event_time( _tickno ) ), 76 | min( _delay.next_event_time( _tickno ), 77 | _rec.next_event_time( _tickno ) ) ); 78 | 79 | if ( next_tickno > tick_limit ) { 80 | _tickno = tick_limit; 81 | break; 82 | } 83 | 84 | assert( next_tickno < std::numeric_limits::max() ); 85 | 86 | _tickno = next_tickno; 87 | 88 | tick(); 89 | } 90 | } 91 | 92 | template 93 | void Network::run_simulation_with_logging_until( const double tick_limit, 94 | SimulationRunData & run_data, 95 | const double interval ) 96 | { 97 | if ( _tickno >= tick_limit ) { 98 | return; 99 | } 100 | 101 | double next_log_time = interval; 102 | 103 | while ( true ) { 104 | /* find element with soonest event */ 105 | double next_tickno = min( min( _senders.next_event_time( _tickno ), 106 | _link.next_event_time( _tickno ) ), 107 | min( _delay.next_event_time( _tickno ), 108 | _rec.next_event_time( _tickno ) ) ); 109 | 110 | if ( next_tickno > tick_limit ) { 111 | _tickno = tick_limit; 112 | break; 113 | } 114 | 115 | assert( next_tickno < std::numeric_limits::max() ); 116 | 117 | _tickno = next_tickno; 118 | tick(); 119 | 120 | if ( _tickno > next_log_time ) { 121 | SimulationRunDataPoint & datum = run_data.add_datum( _tickno / 1000.0 ); 122 | datum.add_sender_data( _senders.statistics_for_log() ); 123 | datum.add_network_data( packets_in_flight() ); 124 | next_log_time += interval; 125 | } 126 | 127 | } 128 | } 129 | 130 | template 131 | vector Network::packets_in_flight( void ) const 132 | { 133 | unsigned int num_senders = _senders.count_senders(); 134 | vector ret = _link.packets_in_flight( num_senders ); 135 | const vector delayed = _delay.packets_in_flight( num_senders ); 136 | 137 | for ( unsigned int i = 0; i < num_senders; i++ ) { 138 | ret.at( i ) += delayed.at( i ); 139 | } 140 | 141 | return ret; 142 | } 143 | -------------------------------------------------------------------------------- /graph/cairo_objects.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cairo_objects.hh" 4 | 5 | using namespace std; 6 | 7 | int Cairo::stride_pixels_for_width( const unsigned int width ) 8 | { 9 | const int stride_bytes = cairo_format_stride_for_width( CAIRO_FORMAT_ARGB32, width ); 10 | 11 | if ( stride_bytes % sizeof( Pixel ) ) { 12 | throw runtime_error( "Cairo requested stride that was not even multiple of pixel size" ); 13 | } else if ( stride_bytes < int( sizeof( Pixel ) * width ) ) { 14 | throw runtime_error( "Cairo does not support width " + to_string( width ) ); 15 | } 16 | 17 | return stride_bytes / sizeof( Pixel ); 18 | } 19 | 20 | Cairo::Cairo( const pair size ) 21 | : image_( size.first, 22 | size.second, 23 | stride_pixels_for_width( size.first ) ), 24 | surface_( image_ ), 25 | context_( surface_ ) 26 | { 27 | check_error(); 28 | } 29 | 30 | Cairo::Surface::Surface( Image & image ) 31 | : surface( cairo_image_surface_create_for_data( image.raw_pixels(), 32 | CAIRO_FORMAT_ARGB32, 33 | image.size().first, 34 | image.size().second, 35 | image.stride_bytes() ) ) 36 | { 37 | check_error(); 38 | } 39 | 40 | Cairo::Context::Context( Surface & surface ) 41 | : context( cairo_create( surface.surface.get() ) ) 42 | { 43 | check_error(); 44 | } 45 | 46 | void Cairo::Surface::check_error( void ) 47 | { 48 | const cairo_status_t surface_result = cairo_surface_status( surface.get() ); 49 | if ( surface_result ) { 50 | throw runtime_error( string( "cairo surface error: " ) + cairo_status_to_string( surface_result ) ); 51 | } 52 | } 53 | 54 | void Cairo::Context::check_error( void ) 55 | { 56 | const cairo_status_t context_result = cairo_status( context.get() ); 57 | if ( context_result ) { 58 | throw runtime_error( string( "cairo context error: " ) + cairo_status_to_string( context_result ) ); 59 | } 60 | } 61 | 62 | void Cairo::check_error( void ) 63 | { 64 | context_.check_error(); 65 | surface_.check_error(); 66 | } 67 | 68 | Pango::Pango( Cairo & cairo ) 69 | : context_( pango_cairo_create_context( cairo ) ), 70 | layout_( pango_layout_new( *this ) ) 71 | {} 72 | 73 | Pango::Font::Font( const string & description ) 74 | : font( pango_font_description_from_string( description.c_str() ) ) 75 | {} 76 | 77 | void Pango::set_font( const Pango::Font & font ) 78 | { 79 | pango_layout_set_font_description( *this, font ); 80 | } 81 | 82 | Pango::Text::Text( Cairo & cairo, Pango & pango, const Font & font, const string & text ) 83 | : path_(), 84 | extent_( { 0, 0, 0, 0 } ) 85 | { 86 | cairo_identity_matrix( cairo ); 87 | cairo_new_path( cairo ); 88 | 89 | pango.set_font( font ); 90 | 91 | pango_layout_set_text( pango, text.data(), text.size() ); 92 | 93 | pango_cairo_layout_path( cairo, pango ); 94 | 95 | path_.reset( cairo_copy_path( cairo ) ); 96 | 97 | /* get logical extents */ 98 | PangoRectangle logical; 99 | pango_layout_get_extents( pango, nullptr, &logical ); 100 | extent_ = { logical.x / double( PANGO_SCALE ), 101 | logical.y / double( PANGO_SCALE ), 102 | logical.width / double( PANGO_SCALE ), 103 | logical.height / double( PANGO_SCALE ) }; 104 | } 105 | 106 | void Pango::Text::draw_centered_at( Cairo & cairo, const double x, const double y, const double max_width ) const 107 | { 108 | cairo_identity_matrix( cairo ); 109 | cairo_new_path( cairo ); 110 | Cairo::Extent my_extent = extent().to_device( cairo ); 111 | 112 | double center_x = x - my_extent.x - my_extent.width / 2; 113 | double center_y = y - my_extent.y - my_extent.height / 2; 114 | 115 | if ( my_extent.width > max_width ) { 116 | const double scale_factor = max_width / my_extent.width; 117 | cairo_scale( cairo, scale_factor, scale_factor ); 118 | center_x = x - my_extent.x - my_extent.width * scale_factor / 2; 119 | center_y = y - my_extent.y - my_extent.height * scale_factor / 2; 120 | } 121 | 122 | cairo_device_to_user( cairo, ¢er_x, ¢er_y ); 123 | 124 | cairo_translate( cairo, center_x, center_y ); 125 | 126 | cairo_append_path( cairo, path_.get() ); 127 | } 128 | 129 | void Pango::Text::draw_centered_rotated_at( Cairo & cairo, const double x, const double y ) const 130 | { 131 | cairo_identity_matrix( cairo ); 132 | cairo_new_path( cairo ); 133 | 134 | cairo_rotate( cairo, - 3.1415926 / 2.0 ); 135 | 136 | Cairo::Extent my_extent = extent().to_device( cairo ); 137 | 138 | double center_x = x - my_extent.x - my_extent.width / 2; 139 | double center_y = y - my_extent.y - my_extent.height / 2; 140 | 141 | cairo_device_to_user( cairo, ¢er_x, ¢er_y ); 142 | cairo_translate( cairo, center_x, center_y ); 143 | cairo_append_path( cairo, path_.get() ); 144 | } 145 | 146 | Cairo::Pattern::Pattern( cairo_pattern_t * pattern ) 147 | : pattern_( pattern ) 148 | { 149 | const cairo_status_t pattern_result = cairo_pattern_status( pattern_.get() ); 150 | if ( pattern_result ) { 151 | throw runtime_error( string( "cairo pattern error: " ) + cairo_status_to_string( pattern_result ) ); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/sendergang.hh: -------------------------------------------------------------------------------- 1 | #ifndef SENDERGANG_HH 2 | #define SENDERGANG_HH 3 | 4 | #include 5 | 6 | #include "exponential.hh" 7 | #include "receiver.hh" 8 | #include "utility.hh" 9 | #include "senderdatapoint.hh" 10 | 11 | template 12 | class SwitchedSender { 13 | private: 14 | double internal_tick; 15 | 16 | protected: 17 | double next_switch_tick; 18 | SenderType sender; 19 | 20 | /* is abstract base class */ 21 | virtual void switcher( const double & tickno, 22 | PRNG & prng, 23 | Exponential & start_distribution, 24 | Exponential & stop_distribution, 25 | const unsigned int num_sending ) = 0; 26 | 27 | void accumulate_sending_time_until( const double & tickno, const unsigned int num_sending ); 28 | 29 | void receive_feedback( Receiver & rec ); 30 | 31 | public: 32 | void switch_on( const double & tickno ); 33 | void switch_off( const double & tickno, const unsigned int num_sending ); 34 | 35 | double next_event_time( const double & tickno ) const; 36 | SenderDataPoint statistics_for_log( void ) const; 37 | Utility utility; 38 | bool sending; 39 | unsigned int id; 40 | 41 | SwitchedSender( const unsigned int s_id, 42 | const double & start_tick, 43 | const SenderType & s_sender ) 44 | : internal_tick( 0 ), 45 | next_switch_tick( start_tick ), 46 | sender( s_sender ), 47 | utility(), 48 | sending( false ), 49 | id( s_id ) 50 | {} 51 | 52 | virtual ~SwitchedSender() {} 53 | }; 54 | 55 | template 56 | class TimeSwitchedSender : public SwitchedSender { 57 | public: 58 | template 59 | void tick( NextHop & next, Receiver & rec, 60 | const double & tickno, 61 | const unsigned int num_sending, 62 | PRNG & prng, 63 | Exponential & start_distribution ); 64 | 65 | void switcher( const double & tickno, 66 | PRNG & prng, 67 | Exponential & start_distribution, 68 | Exponential & stop_distribution, 69 | const unsigned int num_sending ) override; 70 | 71 | using SwitchedSender::SwitchedSender; 72 | }; 73 | 74 | template 75 | class ByteSwitchedSender : public SwitchedSender { 76 | private: 77 | unsigned int packets_sent_cap_ { 0 }; 78 | 79 | public: 80 | template 81 | void tick( NextHop & next, Receiver & rec, 82 | const double & tickno, 83 | const unsigned int num_sending, 84 | PRNG & prng, 85 | Exponential & start_distribution ); 86 | 87 | void switcher( const double & tickno, 88 | PRNG & prng, 89 | Exponential & start_distribution, 90 | Exponential & stop_distribution, 91 | const unsigned int num_sending ) override; 92 | 93 | using SwitchedSender::SwitchedSender; 94 | }; 95 | 96 | template 97 | class ExternalSwitchedSender : public SwitchedSender { 98 | public: 99 | template 100 | void tick( NextHop & next, Receiver & rec, 101 | const double & tickno, 102 | const unsigned int num_sending, 103 | PRNG & prng, 104 | Exponential & start_distribution ); 105 | 106 | void switcher( const double &, 107 | PRNG &, 108 | Exponential &, 109 | Exponential &, 110 | const unsigned int ) override 111 | { 112 | SwitchedSender::next_switch_tick = std::numeric_limits::max(); 113 | } /* don't switch */ 114 | 115 | using SwitchedSender::SwitchedSender; 116 | }; 117 | 118 | template 119 | class SenderGang 120 | { 121 | private: 122 | std::vector< SwitcherType > _gang; 123 | 124 | PRNG & _prng; 125 | 126 | Exponential _start_distribution, _stop_distribution; 127 | 128 | public: 129 | typedef SenderType Sender; 130 | 131 | SenderGang( const double mean_on_duration, 132 | const double mean_off_duration, 133 | const unsigned int num_senders, 134 | const SenderType & exemplar, 135 | PRNG & s_prng, 136 | const unsigned int id_range_begin = 0 ); 137 | 138 | /* Create empty SenderGang */ 139 | SenderGang(); 140 | 141 | unsigned int count_active_senders( void ) const; 142 | size_t count_senders( void ) const { return _gang.size(); } 143 | unsigned int id_of_first_sender( void ) const { return _gang.at( 0 ).id; } 144 | 145 | void switch_senders( const unsigned int num_sending, const double & tickno ); 146 | 147 | template 148 | void run_senders( NextHop & next, Receiver & rec, 149 | const unsigned int num_sending, 150 | const double & tickno ); 151 | 152 | template 153 | void tick( NextHop & next, Receiver & rec, const double & tickno ); 154 | 155 | double utility( void ) const; 156 | 157 | std::vector< std::pair< double, double > > throughputs_delays( void ) const; 158 | std::vector< SenderDataPoint > statistics_for_log( void ) const; 159 | 160 | double next_event_time( const double & tickno ) const; 161 | 162 | SwitcherType & mutable_sender( const unsigned int num ) { return _gang.at( num ); } 163 | const SwitcherType & sender( const unsigned int num ) const { return _gang.at( num ); } 164 | }; 165 | 166 | #endif 167 | -------------------------------------------------------------------------------- /src/sender-runner.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "evaluator.hh" 11 | #include "configrange.hh" 12 | using namespace std; 13 | 14 | template 15 | void print_tree(T & tree) 16 | { 17 | if ( tree.has_config() ) { 18 | printf( "Prior assumptions:\n%s\n\n", tree.config().DebugString().c_str() ); 19 | } 20 | 21 | if ( tree.has_optimizer() ) { 22 | printf( "Remy optimization settings:\n%s\n\n", tree.optimizer().DebugString().c_str() ); 23 | } 24 | } 25 | 26 | template 27 | void parse_outcome( T & outcome ) 28 | { 29 | printf( "score = %f\n", outcome.score ); 30 | double norm_score = 0; 31 | 32 | for ( auto &run : outcome.throughputs_delays ) { 33 | printf( "===\nconfig: %s\n", run.first.str().c_str() ); 34 | for ( auto &x : run.second ) { 35 | printf( "sender: [tp=%f, del=%f]\n", x.first / run.first.link_ppt, x.second / run.first.delay ); 36 | norm_score += log2( x.first / run.first.link_ppt ) - log2( x.second / run.first.delay ); 37 | } 38 | } 39 | 40 | printf( "normalized_score = %f\n", norm_score ); 41 | 42 | printf( "Rules: %s\n", outcome.used_actions.str().c_str() ); 43 | } 44 | 45 | int main( int argc, char *argv[] ) 46 | { 47 | WhiskerTree whiskers; 48 | FinTree fins; 49 | bool is_poisson = false; 50 | unsigned int num_senders = 2; 51 | double link_ppt = 1.0; 52 | double delay = 100.0; 53 | double mean_on_duration = 5000.0; 54 | double mean_off_duration = 5000.0; 55 | double buffer_size = numeric_limits::max(); 56 | double stochastic_loss_rate = 0; 57 | unsigned int simulation_ticks = 1000000; 58 | 59 | for ( int i = 1; i < argc && !is_poisson; i++ ) { 60 | string arg( argv[ i ] ); 61 | if ( arg.substr( 0, 7) == "sender=" ) { 62 | string sender_type( arg.substr( 7 ) ); 63 | if ( sender_type == "poisson" ) { 64 | is_poisson = true; 65 | fprintf( stderr, "Running poisson sender\n" ); 66 | } 67 | } 68 | } 69 | for ( int i = 1; i < argc; i++ ) { 70 | string arg( argv[ i ] ); 71 | if ( arg.substr( 0, 3 ) == "if=" ) { 72 | string filename( arg.substr( 3 ) ); 73 | int fd = open( filename.c_str(), O_RDONLY ); 74 | if ( fd < 0 ) { 75 | perror( "open" ); 76 | exit( 1 ); 77 | } 78 | 79 | if ( is_poisson ) { 80 | RemyBuffers::FinTree tree; 81 | if ( !tree.ParseFromFileDescriptor( fd ) ) { 82 | fprintf( stderr, "Could not parse %s.\n", filename.c_str() ); 83 | exit( 1 ); 84 | } 85 | fins = FinTree( tree ); 86 | print_tree< RemyBuffers::FinTree >(tree); 87 | } else { 88 | RemyBuffers::WhiskerTree tree; 89 | if ( !tree.ParseFromFileDescriptor( fd ) ) { 90 | fprintf( stderr, "Could not parse %s.\n", filename.c_str() ); 91 | exit( 1 ); 92 | } 93 | whiskers = WhiskerTree( tree ); 94 | print_tree< RemyBuffers::WhiskerTree >(tree); 95 | } 96 | 97 | if ( close( fd ) < 0 ) { 98 | perror( "close" ); 99 | exit( 1 ); 100 | } 101 | } else if ( arg.substr( 0, 5 ) == "nsrc=" ) { 102 | num_senders = atoi( arg.substr( 5 ).c_str() ); 103 | fprintf( stderr, "Setting num_senders to %d\n", num_senders ); 104 | } else if ( arg.substr( 0, 5 ) == "link=" ) { 105 | link_ppt = atof( arg.substr( 5 ).c_str() ); 106 | fprintf( stderr, "Setting link packets per ms to %f\n", link_ppt ); 107 | } else if ( arg.substr( 0, 4 ) == "rtt=" ) { 108 | delay = atof( arg.substr( 4 ).c_str() ); 109 | fprintf( stderr, "Setting delay to %f ms\n", delay ); 110 | } else if ( arg.substr( 0, 3 ) == "on=" ) { 111 | mean_on_duration = atof( arg.substr( 3 ).c_str() ); 112 | fprintf( stderr, "Setting mean_on_duration to %f ms\n", mean_on_duration ); 113 | } else if ( arg.substr( 0, 4 ) == "off=" ) { 114 | mean_off_duration = atof( arg.substr( 4 ).c_str() ); 115 | fprintf( stderr, "Setting mean_off_duration to %f ms\n", mean_off_duration ); 116 | } else if ( arg.substr( 0, 4 ) == "buf=" ) { 117 | if (arg.substr( 4 ) == "inf") { 118 | buffer_size = numeric_limits::max(); 119 | } else { 120 | buffer_size = atoi( arg.substr( 4 ).c_str() ); 121 | } 122 | } else if ( arg.substr( 0, 6 ) == "sloss=" ) { 123 | stochastic_loss_rate = atof( arg.substr( 6 ).c_str() ); 124 | fprintf( stderr, "Setting stochastic loss rate to %f\n", stochastic_loss_rate ); 125 | } 126 | } 127 | 128 | ConfigRange configuration_range; 129 | configuration_range.link_ppt = Range( link_ppt,link_ppt, 0 ); /* 1 Mbps to 10 Mbps */ 130 | configuration_range.rtt = Range( delay, delay, 0 ); /* ms */ 131 | configuration_range.num_senders = Range( num_senders, num_senders, 0 ); 132 | configuration_range.mean_on_duration = Range( mean_on_duration, mean_on_duration, 0 ); 133 | configuration_range.mean_off_duration = Range( mean_off_duration, mean_off_duration, 0 ); 134 | configuration_range.buffer_size = Range( buffer_size, buffer_size, 0 ); 135 | configuration_range.stochastic_loss_rate = Range( stochastic_loss_rate, stochastic_loss_rate, 0); 136 | configuration_range.simulation_ticks = simulation_ticks; 137 | 138 | if ( is_poisson ) { 139 | Evaluator< FinTree > eval( configuration_range ); 140 | auto outcome = eval.score( fins, false, 10 ); 141 | parse_outcome< Evaluator< FinTree >::Outcome > ( outcome ); 142 | } else { 143 | Evaluator< WhiskerTree > eval( configuration_range ); 144 | auto outcome = eval.score( whiskers, false, 10 ); 145 | parse_outcome< Evaluator< WhiskerTree >::Outcome > ( outcome ); 146 | } 147 | 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /src/remy-poisson.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "fishbreeder.hh" 10 | #include "dna.pb.h" 11 | #include "configrange.hh" 12 | using namespace std; 13 | 14 | int main( int argc, char *argv[] ) 15 | { 16 | FinTree fins; 17 | string output_filename; 18 | BreederOptions options; 19 | RemyBuffers::ConfigRange input_config; 20 | string config_filename; 21 | 22 | for ( int i = 1; i < argc; i++ ) { 23 | string arg( argv[ i ] ); 24 | if ( arg.substr( 0, 3 ) == "if=" ) { 25 | string filename( arg.substr( 3 ) ); 26 | int fd = open( filename.c_str(), O_RDONLY ); 27 | if ( fd < 0 ) { 28 | perror( "open" ); 29 | exit( 1 ); 30 | } 31 | 32 | RemyBuffers::FinTree tree; 33 | if ( !tree.ParseFromFileDescriptor( fd ) ) { 34 | fprintf( stderr, "Could not parse %s.\n", filename.c_str() ); 35 | exit( 1 ); 36 | } 37 | fins = FinTree( tree ); 38 | 39 | if ( close( fd ) < 0 ) { 40 | perror( "close" ); 41 | exit( 1 ); 42 | } 43 | 44 | } else if ( arg.substr( 0, 3 ) == "of=" ) { 45 | output_filename = string( arg.substr( 3 ) ); 46 | 47 | } else if ( arg.substr( 0, 3 ) == "cf=" ) { 48 | config_filename = string( arg.substr( 3 ) ); 49 | int cfd = open( config_filename.c_str(), O_RDONLY ); 50 | if ( cfd < 0 ) { 51 | perror( "open config file error"); 52 | exit( 1 ); 53 | } 54 | if ( !input_config.ParseFromFileDescriptor( cfd ) ) { 55 | fprintf( stderr, "Could not parse input config from file %s. \n", config_filename.c_str() ); 56 | exit ( 1 ); 57 | } 58 | if ( close( cfd ) < 0 ) { 59 | perror( "close" ); 60 | exit( 1 ); 61 | } 62 | } 63 | } 64 | 65 | if ( config_filename.empty() ) { 66 | fprintf( stderr, "An input configuration protobuf must be provided via the cf= option. \n"); 67 | fprintf( stderr, "You can generate one using './configuration'. \n"); 68 | exit ( 1 ); 69 | } 70 | 71 | options.config_range = ConfigRange( input_config ); 72 | 73 | FishBreeder breeder( options ); 74 | 75 | unsigned int run = 0; 76 | 77 | printf( "#######################\n" ); 78 | printf( "Evaluator simulations will run for %d ticks\n", 79 | options.config_range.simulation_ticks ); 80 | printf( "Optimizing for link packets_per_ms in [%f, %f]\n", 81 | options.config_range.link_ppt.low, 82 | options.config_range.link_ppt.high ); 83 | printf( "Optimizing for rtt_ms in [%f, %f]\n", 84 | options.config_range.rtt.low, 85 | options.config_range.rtt.high ); 86 | printf( "Optimizing for num_senders in [%f, %f]\n", 87 | options.config_range.num_senders.low, options.config_range.num_senders.high ); 88 | printf( "Optimizing for mean_on_duration in [%f, %f], mean_off_duration in [ %f, %f]\n", 89 | options.config_range.mean_on_duration.low, options.config_range.mean_on_duration.high, options.config_range.mean_off_duration.low, options.config_range.mean_off_duration.high ); 90 | printf( "Optimizing for stochastic_loss_rate in [%f, %f]\n", options.config_range.stochastic_loss_rate.low, options.config_range.stochastic_loss_rate.high ); 91 | if ( options.config_range.buffer_size.low != numeric_limits::max() ) { 92 | printf( "Optimizing for buffer_size in [%f, %f]\n", 93 | options.config_range.buffer_size.low, 94 | options.config_range.buffer_size.high ); 95 | } else { 96 | printf( "Optimizing for infinitely sized buffers. \n"); 97 | } 98 | 99 | printf( "Initial rules (use if=FILENAME to read from disk): %s\n", fins.str().c_str() ); 100 | printf( "#######################\n" ); 101 | 102 | if ( !output_filename.empty() ) { 103 | printf( "Writing to \"%s.N\".\n", output_filename.c_str() ); 104 | } else { 105 | printf( "Not saving output. Use the of=FILENAME argument to save the results.\n" ); 106 | } 107 | 108 | RemyBuffers::ConfigVector training_configs; 109 | bool written = false; 110 | 111 | while ( 1 ) { 112 | auto outcome = breeder.improve( fins ); 113 | printf( "run = %u, score = %f\n", run, outcome.score ); 114 | 115 | printf( "fins: %s\n", fins.str().c_str() ); 116 | 117 | for ( auto &run : outcome.throughputs_delays ) { 118 | if ( !(written) ) { 119 | for ( auto &run : outcome.throughputs_delays) { 120 | // record the config to the protobuf 121 | RemyBuffers::NetConfig* net_config = training_configs.add_config(); 122 | *net_config = run.first.DNA(); 123 | written = true; 124 | 125 | } 126 | } 127 | printf( "===\nconfig: %s\n", run.first.str().c_str() ); 128 | for ( auto &x : run.second ) { 129 | printf( "sender: [tp=%f, del=%f]\n", x.first / run.first.link_ppt, x.second / run.first.delay ); 130 | } 131 | } 132 | 133 | if ( !output_filename.empty() ) { 134 | char of[ 128 ]; 135 | snprintf( of, 128, "%s.%d", output_filename.c_str(), run ); 136 | fprintf( stderr, "Writing to \"%s\"... ", of ); 137 | int fd = open( of, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR ); 138 | if ( fd < 0 ) { 139 | perror( "open" ); 140 | exit( 1 ); 141 | } 142 | 143 | auto remycc = fins.DNA(); 144 | remycc.mutable_config()->CopyFrom( options.config_range.DNA() ); 145 | remycc.mutable_optimizer()->CopyFrom( Fin::get_optimizer().DNA() ); 146 | remycc.mutable_configvector()->CopyFrom( training_configs ); 147 | if ( not remycc.SerializeToFileDescriptor( fd ) ) { 148 | fprintf( stderr, "Could not serialize RemyCC.\n" ); 149 | exit( 1 ); 150 | } 151 | 152 | if ( close( fd ) < 0 ) { 153 | perror( "close" ); 154 | exit( 1 ); 155 | } 156 | 157 | fprintf( stderr, "done.\n" ); 158 | } 159 | 160 | fflush( NULL ); 161 | run++; 162 | } 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /src/breeder.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "breeder.hh" 8 | 9 | using namespace boost::accumulators; 10 | using namespace std; 11 | 12 | typedef accumulator_set< double, stats< tag::tail_quantile > > 13 | accumulator_t_right; 14 | 15 | template 16 | void Breeder< T >::apply_best_split( T & tree, const unsigned int generation ) const 17 | { 18 | const Evaluator< T > eval( _options.config_range ); 19 | auto outcome( eval.score( tree, true ) ); 20 | 21 | while ( 1 ) { 22 | auto my_action( outcome.used_actions.most_used( generation ) ); 23 | assert( my_action ); 24 | 25 | T bisected_action( *my_action, true ); 26 | 27 | if ( bisected_action.num_children() == 1 ) { 28 | printf( "Got unbisectable whisker! %s\n", my_action->str().c_str() ); 29 | auto mutable_action ( *my_action ); 30 | mutable_action.promote( generation + 1 ); 31 | assert( outcome.used_actions.replace( mutable_action ) ); 32 | continue; 33 | } 34 | 35 | printf( "Bisecting === %s === into === %s ===\n", my_action->str().c_str(), bisected_action.str().c_str() ); 36 | assert( tree.replace( *my_action, bisected_action ) ); 37 | break; 38 | } 39 | } 40 | 41 | template 42 | ActionImprover< T, A >::ActionImprover( const Evaluator< T > & s_evaluator, 43 | const T & tree, 44 | const double score_to_beat) 45 | : eval_( s_evaluator ), 46 | tree_( tree ), 47 | score_to_beat_( score_to_beat ) 48 | {} 49 | 50 | template 51 | void ActionImprover< T, A >::evaluate_replacements(const vector &replacements, 52 | vector< pair< const A &, future< pair< bool, double > > > > &scores, 53 | const double carefulness ) 54 | { 55 | for ( const auto & test_replacement : replacements ) { 56 | if ( eval_cache_.find( test_replacement ) == eval_cache_.end() ) { 57 | /* need to fire off a new thread to evaluate */ 58 | scores.emplace_back( test_replacement, 59 | async( launch::async, [] ( const Evaluator< T > & e, 60 | const A & r, 61 | const T & tree, 62 | const double carefulness ) { 63 | T replaced_tree( tree ); 64 | const bool found_replacement __attribute((unused)) = replaced_tree.replace( r ); 65 | assert( found_replacement ); 66 | return make_pair( true, e.score( replaced_tree, false, carefulness ).score ); }, 67 | eval_, test_replacement, tree_, carefulness ) ); 68 | } else { 69 | /* we already know the score */ 70 | scores.emplace_back( test_replacement, 71 | async( launch::deferred, [] ( const double value ) { 72 | return make_pair( false, value ); }, eval_cache_.at( test_replacement ) ) ); 73 | } 74 | } 75 | } 76 | 77 | template 78 | vector ActionImprover< T, A >::early_bail_out( const vector< A > &replacements, 79 | const double carefulness, const double quantile_to_keep ) 80 | { 81 | vector< pair< const A &, future< pair< bool, double > > > > scores; 82 | evaluate_replacements( replacements, scores, carefulness ); 83 | 84 | accumulator_t_right acc( 85 | tag::tail< boost::accumulators::right >::cache_size = scores.size() ); 86 | vector raw_scores; 87 | for ( auto & x : scores ) { 88 | const double score( x.second.get().second ); 89 | acc( score ); 90 | raw_scores.push_back( score ); 91 | } 92 | 93 | /* Set the lower bound to be MAX_PERCENT_ERROR worse than the current best score */ 94 | double lower_bound = std::min( score_to_beat_ * (1 + MAX_PERCENT_ERROR), 95 | score_to_beat_ * (1 - MAX_PERCENT_ERROR) ); 96 | /* Get the score at given quantile */ 97 | double quantile_bound = quantile( acc, quantile_probability = 1 - quantile_to_keep ); 98 | double cutoff = std::max( lower_bound, quantile_bound ); 99 | 100 | /* Discard replacements below threshold */ 101 | vector top_replacements; 102 | for ( uint i = 0; i < scores.size(); i ++ ) { 103 | const A & replacement( scores.at( i ).first ); 104 | const double score( raw_scores.at( i ) ); 105 | if ( score >= cutoff ) { 106 | top_replacements.push_back( replacement ); 107 | } 108 | } 109 | return top_replacements; 110 | } 111 | 112 | template 113 | double ActionImprover< T, A >::improve( A & action_to_improve ) 114 | { 115 | auto replacements = get_replacements( action_to_improve ); 116 | vector< pair< const A &, future< pair< bool, double > > > > scores; 117 | 118 | /* Run for 10% simulation time to get estimates for the final score 119 | and discard bad performing ones early on. */ 120 | vector top_replacements = early_bail_out( replacements, 0.1, 0.5 ); 121 | 122 | /* find best replacement */ 123 | evaluate_replacements( top_replacements, scores, 1 ); 124 | for ( auto & x : scores ) { 125 | const A & replacement( x.first ); 126 | const auto outcome( x.second.get() ); 127 | const bool was_new_evaluation( outcome.first ); 128 | const double score( outcome.second ); 129 | 130 | /* should we cache this result? */ 131 | if ( was_new_evaluation ) { 132 | eval_cache_.insert( make_pair( replacement, score ) ); 133 | } 134 | 135 | if ( score > score_to_beat_ ) { 136 | score_to_beat_ = score; 137 | action_to_improve = replacement; 138 | } 139 | } 140 | 141 | cout << "Chose " << action_to_improve.str() << endl; 142 | 143 | return score_to_beat_; 144 | } 145 | 146 | template class ActionImprover< WhiskerTree, Whisker >; 147 | template class ActionImprover< FinTree, Fin >; 148 | template class Breeder< FinTree >; 149 | template class Breeder< WhiskerTree >; 150 | -------------------------------------------------------------------------------- /src/fintree.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fintree.hh" 4 | 5 | using namespace std; 6 | 7 | FinTree::FinTree() 8 | : _domain( Memory(), MAX_MEMORY(), { RemyBuffers::MemoryRange::RTT_DIFF } ), 9 | _children(), 10 | _leaf( 1, Fin( _domain ) ) 11 | { 12 | } 13 | 14 | FinTree::FinTree( const Fin & fin, const bool bisect ) 15 | : _domain( fin.domain() ), 16 | _children(), 17 | _leaf() 18 | { 19 | if ( !bisect ) { 20 | _leaf.push_back( fin ); 21 | } else { 22 | for ( auto &x : fin.bisect() ) { 23 | _children.push_back( FinTree( x, false ) ); 24 | } 25 | } 26 | } 27 | 28 | void FinTree::reset_counts( void ) 29 | { 30 | if ( is_leaf() ) { 31 | _leaf.front().reset_count(); 32 | } else { 33 | for ( auto &x : _children ) { 34 | x.reset_counts(); 35 | } 36 | } 37 | } 38 | 39 | const Fin & FinTree::use_fin( const Memory & _memory, const bool track ) const 40 | { 41 | const Fin * ret( fin( _memory ) ); 42 | 43 | if ( !ret ) { 44 | fprintf( stderr, "ERROR: No Fin found for %s\n", _memory.str().c_str() ); 45 | exit( 1 ); 46 | } 47 | 48 | assert( ret ); 49 | 50 | ret->use(); 51 | 52 | if ( track ) { 53 | ret->domain().track( _memory ); 54 | } 55 | 56 | return *ret; 57 | } 58 | 59 | const Fin * FinTree::fin( const Memory & _memory ) const 60 | { 61 | if ( !_domain.contains( _memory ) ) { 62 | return nullptr; 63 | } 64 | 65 | if ( is_leaf() ) { 66 | return &_leaf[ 0 ]; 67 | } 68 | 69 | /* need to descend */ 70 | for ( auto &x : _children ) { 71 | auto ret( x.fin( _memory ) ); 72 | if ( ret ) { 73 | return ret; 74 | } 75 | } 76 | 77 | assert( false ); 78 | return nullptr; 79 | } 80 | 81 | const Fin * FinTree::most_used( const unsigned int max_generation ) const 82 | { 83 | if ( is_leaf() ) { 84 | if ( (_leaf.front().generation() <= max_generation) 85 | && (_leaf.front().count() > 0) ) { 86 | return &_leaf[ 0 ]; 87 | } 88 | return nullptr; 89 | } 90 | 91 | /* recurse */ 92 | unsigned int count_max = 0; 93 | const Fin * ret( nullptr ); 94 | 95 | for ( auto &x : _children ) { 96 | const Fin * candidate( x.most_used( max_generation ) ); 97 | if ( candidate 98 | && (candidate->generation() <= max_generation) 99 | && (candidate->count() >= count_max) ) { 100 | ret = candidate; 101 | count_max = candidate->count(); 102 | } 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | void FinTree::reset_generation( void ) 109 | { 110 | if ( is_leaf() ) { 111 | assert( _leaf.size() == 1 ); 112 | assert( _children.empty() ); 113 | _leaf.front().demote( 0 ); 114 | } else { 115 | for ( auto &x : _children ) { 116 | x.reset_generation(); 117 | } 118 | } 119 | } 120 | 121 | void FinTree::promote( const unsigned int generation ) 122 | { 123 | if ( is_leaf() ) { 124 | assert( _leaf.size() == 1 ); 125 | assert( _children.empty() ); 126 | _leaf.front().promote( generation ); 127 | } else { 128 | for ( auto &x : _children ) { 129 | x.promote( generation ); 130 | } 131 | } 132 | } 133 | 134 | bool FinTree::replace( const Fin & w ) 135 | { 136 | if ( !_domain.contains( w.domain().range_median() ) ) { 137 | return false; 138 | } 139 | 140 | if ( is_leaf() ) { 141 | assert( w.domain() == _leaf.front().domain() ); 142 | _leaf.front() = w; 143 | return true; 144 | } 145 | 146 | for ( auto &x : _children ) { 147 | if ( x.replace( w ) ) { 148 | return true; 149 | } 150 | } 151 | 152 | assert( false ); 153 | return false; 154 | } 155 | 156 | bool FinTree::replace( const Fin & src, const FinTree & dst ) 157 | { 158 | if ( !_domain.contains( src.domain().range_median() ) ) { 159 | return false; 160 | } 161 | 162 | if ( is_leaf() ) { 163 | assert( src.domain() == _leaf.front().domain() ); 164 | /* convert from leaf to interior node */ 165 | *this = dst; 166 | return true; 167 | } 168 | 169 | for ( auto &x : _children ) { 170 | if ( x.replace( src, dst ) ) { 171 | return true; 172 | } 173 | } 174 | 175 | assert( false ); 176 | return false; 177 | } 178 | 179 | unsigned int FinTree::total_fin_queries( void ) const 180 | { 181 | if ( is_leaf() ) { 182 | assert( _children.empty() ); 183 | return _leaf.front().domain().count(); 184 | } 185 | 186 | return accumulate( _children.begin(), 187 | _children.end(), 188 | 0, 189 | []( const unsigned int sum, 190 | const FinTree & x ) 191 | { return sum + x.total_fin_queries(); } ); 192 | } 193 | 194 | string FinTree::str() const 195 | { 196 | return str( total_fin_queries() ); 197 | } 198 | 199 | string FinTree::str( const unsigned int total ) const 200 | { 201 | if ( is_leaf() ) { 202 | assert( _children.empty() ); 203 | string tmp = string( "[" ) + _leaf.front().str( total ) + "]"; 204 | return tmp; 205 | } 206 | 207 | string ret; 208 | 209 | for ( const auto &x : _children ) { 210 | ret += x.str( total ); 211 | } 212 | 213 | return ret; 214 | } 215 | 216 | unsigned int FinTree::num_children( void ) const 217 | { 218 | if ( is_leaf() ) { 219 | assert( _leaf.size() == 1 ); 220 | return 1; 221 | } 222 | 223 | return _children.size(); 224 | } 225 | 226 | bool FinTree::is_leaf( void ) const 227 | { 228 | return !_leaf.empty(); 229 | } 230 | 231 | RemyBuffers::FinTree FinTree::DNA( void ) const 232 | { 233 | RemyBuffers::FinTree ret; 234 | 235 | /* set domain */ 236 | ret.mutable_domain()->CopyFrom( _domain.DNA() ); 237 | 238 | /* set children */ 239 | if ( is_leaf() ) { 240 | ret.mutable_leaf()->CopyFrom( _leaf.front().DNA() ); 241 | } else { 242 | for ( auto &x : _children ) { 243 | RemyBuffers::FinTree *child = ret.add_children(); 244 | *child = x.DNA(); 245 | } 246 | } 247 | 248 | return ret; 249 | } 250 | 251 | FinTree::FinTree( const RemyBuffers::FinTree & dna ) 252 | : _domain( dna.domain() ), 253 | _children(), 254 | _leaf() 255 | { 256 | if ( dna.has_leaf() ) { 257 | assert( dna.children_size() == 0 ); 258 | _leaf.emplace_back( dna.leaf() ); 259 | } else { 260 | assert( dna.children_size() > 0 ); 261 | for ( const auto &x : dna.children() ) { 262 | _children.emplace_back( x ); 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/scoring-example.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "evaluator.hh" 11 | #include "configrange.hh" 12 | using namespace std; 13 | 14 | 15 | int main( int argc, char *argv[] ) 16 | { 17 | WhiskerTree whiskers; 18 | unsigned int num_senders = 2; 19 | double link_ppt = 1.0; 20 | double delay = 100.0; 21 | double mean_on_duration = 5000.0; 22 | double mean_off_duration = 5000.0; 23 | double stochastic_loss_rate = 0; 24 | for ( int i = 1; i < argc; i++ ) { 25 | string arg( argv[ i ] ); 26 | if ( arg.substr( 0, 3 ) == "if=" ) { 27 | string filename( arg.substr( 3 ) ); 28 | int fd = open( filename.c_str(), O_RDONLY ); 29 | if ( fd < 0 ) { 30 | perror( "open" ); 31 | exit( 1 ); 32 | } 33 | 34 | RemyBuffers::WhiskerTree tree; 35 | if ( !tree.ParseFromFileDescriptor( fd ) ) { 36 | fprintf( stderr, "Could not parse %s.\n", filename.c_str() ); 37 | exit( 1 ); 38 | } 39 | whiskers = WhiskerTree( tree ); 40 | 41 | if ( close( fd ) < 0 ) { 42 | perror( "close" ); 43 | exit( 1 ); 44 | } 45 | 46 | if ( tree.has_config() ) { 47 | printf( "Prior assumptions:\n%s\n\n", tree.config().DebugString().c_str() ); 48 | } 49 | 50 | if ( tree.has_optimizer() ) { 51 | printf( "Remy optimization settings:\n%s\n\n", tree.optimizer().DebugString().c_str() ); 52 | } 53 | } else if ( arg.substr( 0, 5 ) == "nsrc=" ) { 54 | num_senders = atoi( arg.substr( 5 ).c_str() ); 55 | fprintf( stderr, "Setting num_senders to %d\n", num_senders ); 56 | } else if ( arg.substr( 0, 5 ) == "link=" ) { 57 | link_ppt = atof( arg.substr( 5 ).c_str() ); 58 | fprintf( stderr, "Setting link packets per ms to %f\n", link_ppt ); 59 | } else if ( arg.substr( 0, 4 ) == "rtt=" ) { 60 | delay = atof( arg.substr( 4 ).c_str() ); 61 | fprintf( stderr, "Setting delay to %f ms\n", delay ); 62 | } else if ( arg.substr( 0, 3 ) == "on=" ) { 63 | mean_on_duration = atof( arg.substr( 3 ).c_str() ); 64 | fprintf( stderr, "Setting mean_on_duration to %f ms\n", mean_on_duration ); 65 | } else if ( arg.substr( 0, 4 ) == "off=" ) { 66 | mean_off_duration = atof( arg.substr( 4 ).c_str() ); 67 | fprintf( stderr, "Setting mean_off_duration to %f ms\n", mean_off_duration ); 68 | } else if ( arg.substr(0, 6 ) == "sloss=" ) { 69 | stochastic_loss_rate = atof( arg.substr( 6 ).c_str() ); 70 | fprintf( stderr, "Setting stochastic loss rate to %f\n", stochastic_loss_rate ); 71 | } 72 | } 73 | 74 | ConfigRange configuration_range; 75 | configuration_range.link_ppt = Range( link_ppt,link_ppt, 0 ); /* 1 Mbps to 10 Mbps */ 76 | configuration_range.rtt = Range( delay, delay, 0 ); /* ms */ 77 | configuration_range.num_senders = Range(num_senders, num_senders, 0 ); 78 | configuration_range.mean_on_duration = Range(mean_on_duration, mean_on_duration, 0); 79 | configuration_range.mean_off_duration = Range(mean_off_duration, mean_off_duration, 0); 80 | configuration_range.stochastic_loss_rate = Range(stochastic_loss_rate, stochastic_loss_rate, 0); 81 | Evaluator< WhiskerTree > eval( configuration_range ); 82 | 83 | // save problem to file 84 | ProblemBuffers::Problem serialized_problem = eval.DNA( whiskers ); 85 | string problem_filename = "problem_example.dna"; 86 | 87 | char of_p[ 128 ]; 88 | snprintf( of_p, 128, "%s", problem_filename.c_str() ); 89 | fprintf( stderr, "Writing to \"%s\"... ", of_p ); 90 | int fd = open( of_p, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR ); 91 | if ( fd < 0 ) { 92 | perror( "open" ); 93 | exit( 1 ); 94 | } 95 | 96 | if ( not serialized_problem.SerializeToFileDescriptor( fd ) ) { 97 | fprintf( stderr, "Could not serialize problem.\n" ); 98 | exit( 1 ); 99 | } 100 | 101 | if ( close( fd ) < 0 ) { 102 | perror( "close" ); 103 | exit( 1 ); 104 | } 105 | 106 | cerr << "Successfully stored problem." << endl; 107 | 108 | // parse file and score 109 | fd = open( problem_filename.c_str(), O_RDONLY ); 110 | if ( fd < 0 ) { 111 | perror( "open" ); 112 | exit( 1 ); 113 | } 114 | 115 | ProblemBuffers::Problem problem; 116 | if ( !problem.ParseFromFileDescriptor( fd ) ) { 117 | fprintf( stderr, "Could not parse %s.\n", problem_filename.c_str() ); 118 | exit( 1 ); 119 | } 120 | 121 | auto outcome = Evaluator< WhiskerTree >::parse_problem_and_evaluate( problem ); 122 | 123 | cerr << "Successfully scored problem." << endl; 124 | 125 | // save score to file 126 | string answer_filename = "answer_example.dna"; 127 | 128 | AnswerBuffers::Outcome serialized_answer = outcome.DNA(); 129 | char of_a[ 128 ]; 130 | snprintf( of_a, 128, "%s", answer_filename.c_str() ); 131 | fprintf( stderr, "Writing to \"%s\"... ", of_a ); 132 | fd = open( of_a, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR ); 133 | if ( fd < 0 ) { 134 | perror( "open" ); 135 | exit( 1 ); 136 | } 137 | 138 | if ( not serialized_answer.SerializeToFileDescriptor( fd ) ) { 139 | fprintf( stderr, "Could not serialize outcome.\n" ); 140 | exit( 1 ); 141 | } 142 | 143 | if ( close( fd ) < 0 ) { 144 | perror( "close" ); 145 | exit( 1 ); 146 | } 147 | 148 | cerr << "Successfully stored answer." << endl; 149 | 150 | // read score from file 151 | 152 | fd = open( answer_filename.c_str(), O_RDONLY ); 153 | if ( fd < 0 ) { 154 | perror( "open" ); 155 | exit( 1 ); 156 | } 157 | 158 | AnswerBuffers::Outcome proto_outcome; 159 | if ( !proto_outcome.ParseFromFileDescriptor( fd ) ) { 160 | fprintf( stderr, "Could not parse %s.\n", answer_filename.c_str() ); 161 | exit( 1 ); 162 | } 163 | 164 | Evaluator< WhiskerTree >::Outcome parsed_outcome( proto_outcome ); 165 | printf( "score = %f\n", outcome.score ); 166 | double norm_score = 0; 167 | 168 | for ( auto &run : parsed_outcome.throughputs_delays ) { 169 | printf( "===\nconfig: %s\n", run.first.str().c_str() ); 170 | for ( auto &x : run.second ) { 171 | printf( "sender: [tp=%f, del=%f]\n", x.first / run.first.link_ppt, x.second / run.first.delay ); 172 | norm_score += log2( x.first / run.first.link_ppt ) - log2( x.second / run.first.delay ); 173 | } 174 | } 175 | 176 | printf( "normalized_score = %f\n", norm_score ); 177 | 178 | printf( "Whiskers: %s\n", outcome.used_actions.str().c_str() ); 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /src/whiskertree.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "whiskertree.hh" 7 | 8 | using namespace std; 9 | 10 | WhiskerTree::WhiskerTree() 11 | : _domain( Memory(), MAX_MEMORY() ), 12 | _children(), 13 | _leaf( 1, Whisker( _domain ) ) 14 | { 15 | } 16 | 17 | WhiskerTree::WhiskerTree( const Whisker & whisker, const bool bisect ) 18 | : _domain( whisker.domain() ), 19 | _children(), 20 | _leaf() 21 | { 22 | if ( !bisect ) { 23 | _leaf.push_back( whisker ); 24 | } else { 25 | for ( auto &x : whisker.bisect() ) { 26 | _children.push_back( WhiskerTree( x, false ) ); 27 | } 28 | } 29 | } 30 | 31 | void WhiskerTree::reset_counts( void ) 32 | { 33 | if ( is_leaf() ) { 34 | _leaf.front().reset_count(); 35 | } else { 36 | for ( auto &x : _children ) { 37 | x.reset_counts(); 38 | } 39 | } 40 | } 41 | 42 | const Whisker & WhiskerTree::use_whisker( const Memory & _memory, const bool track ) const 43 | { 44 | const Whisker * ret( whisker( _memory ) ); 45 | 46 | if ( !ret ) { 47 | fprintf( stderr, "ERROR: No whisker found for %s\n", _memory.str().c_str() ); 48 | exit( 1 ); 49 | } 50 | 51 | assert( ret ); 52 | 53 | ret->use(); 54 | 55 | if ( track ) { 56 | ret->domain().track( _memory ); 57 | } 58 | 59 | return *ret; 60 | } 61 | 62 | const Whisker * WhiskerTree::whisker( const Memory & _memory ) const 63 | { 64 | if ( !_domain.contains( _memory ) ) { 65 | return nullptr; 66 | } 67 | 68 | if ( is_leaf() ) { 69 | return &_leaf[ 0 ]; 70 | } 71 | 72 | /* need to descend */ 73 | for ( auto &x : _children ) { 74 | auto ret( x.whisker( _memory ) ); 75 | if ( ret ) { 76 | return ret; 77 | } 78 | } 79 | 80 | assert( false ); 81 | return nullptr; 82 | } 83 | 84 | const Whisker * WhiskerTree::most_used( const unsigned int max_generation ) const 85 | { 86 | if ( is_leaf() ) { 87 | if ( (_leaf.front().generation() <= max_generation) 88 | && (_leaf.front().count() > 0) ) { 89 | return &_leaf[ 0 ]; 90 | } 91 | return nullptr; 92 | } 93 | 94 | /* recurse */ 95 | unsigned int count_max = 0; 96 | const Whisker * ret( nullptr ); 97 | 98 | for ( auto &x : _children ) { 99 | const Whisker * candidate( x.most_used( max_generation ) ); 100 | if ( candidate 101 | && (candidate->generation() <= max_generation) 102 | && (candidate->count() >= count_max) ) { 103 | ret = candidate; 104 | count_max = candidate->count(); 105 | } 106 | } 107 | 108 | return ret; 109 | } 110 | 111 | void WhiskerTree::reset_generation( void ) 112 | { 113 | if ( is_leaf() ) { 114 | assert( _leaf.size() == 1 ); 115 | assert( _children.empty() ); 116 | _leaf.front().demote( 0 ); 117 | } else { 118 | for ( auto &x : _children ) { 119 | x.reset_generation(); 120 | } 121 | } 122 | } 123 | 124 | void WhiskerTree::promote( const unsigned int generation ) 125 | { 126 | if ( is_leaf() ) { 127 | assert( _leaf.size() == 1 ); 128 | assert( _children.empty() ); 129 | _leaf.front().promote( generation ); 130 | } else { 131 | for ( auto &x : _children ) { 132 | x.promote( generation ); 133 | } 134 | } 135 | } 136 | 137 | bool WhiskerTree::replace( const Whisker & w ) 138 | { 139 | if ( !_domain.contains( w.domain().range_median() ) ) { 140 | return false; 141 | } 142 | 143 | if ( is_leaf() ) { 144 | assert( w.domain() == _leaf.front().domain() ); 145 | _leaf.front() = w; 146 | return true; 147 | } 148 | 149 | for ( auto &x : _children ) { 150 | if ( x.replace( w ) ) { 151 | return true; 152 | } 153 | } 154 | 155 | assert( false ); 156 | return false; 157 | } 158 | 159 | bool WhiskerTree::replace( const Whisker & src, const WhiskerTree & dst ) 160 | { 161 | if ( !_domain.contains( src.domain().range_median() ) ) { 162 | return false; 163 | } 164 | 165 | if ( is_leaf() ) { 166 | assert( src.domain() == _leaf.front().domain() ); 167 | /* convert from leaf to interior node */ 168 | *this = dst; 169 | return true; 170 | } 171 | 172 | for ( auto &x : _children ) { 173 | if ( x.replace( src, dst ) ) { 174 | return true; 175 | } 176 | } 177 | 178 | assert( false ); 179 | return false; 180 | } 181 | 182 | unsigned int WhiskerTree::total_whisker_queries( void ) const 183 | { 184 | if ( is_leaf() ) { 185 | assert( _children.empty() ); 186 | return _leaf.front().domain().count(); 187 | } 188 | 189 | return accumulate( _children.begin(), 190 | _children.end(), 191 | 0, 192 | []( const unsigned int sum, 193 | const WhiskerTree & x ) 194 | { return sum + x.total_whisker_queries(); } ); 195 | } 196 | 197 | string WhiskerTree::str() const 198 | { 199 | return str( total_whisker_queries() ); 200 | } 201 | 202 | string WhiskerTree::str( const unsigned int total ) const 203 | { 204 | if ( is_leaf() ) { 205 | assert( _children.empty() ); 206 | string tmp = string( "[" ) + _leaf.front().str( total ) + "]"; 207 | return tmp; 208 | } 209 | 210 | string ret; 211 | 212 | for ( const auto &x : _children ) { 213 | ret += x.str( total ); 214 | } 215 | 216 | return ret; 217 | } 218 | 219 | unsigned int WhiskerTree::num_children( void ) const 220 | { 221 | if ( is_leaf() ) { 222 | assert( _leaf.size() == 1 ); 223 | return 1; 224 | } 225 | 226 | return _children.size(); 227 | } 228 | 229 | bool WhiskerTree::is_leaf( void ) const 230 | { 231 | return !_leaf.empty(); 232 | } 233 | 234 | RemyBuffers::WhiskerTree WhiskerTree::DNA( void ) const 235 | { 236 | RemyBuffers::WhiskerTree ret; 237 | 238 | /* set domain */ 239 | ret.mutable_domain()->CopyFrom( _domain.DNA() ); 240 | 241 | /* set children */ 242 | if ( is_leaf() ) { 243 | ret.mutable_leaf()->CopyFrom( _leaf.front().DNA() ); 244 | } else { 245 | for ( auto &x : _children ) { 246 | RemyBuffers::WhiskerTree *child = ret.add_children(); 247 | *child = x.DNA(); 248 | } 249 | } 250 | 251 | return ret; 252 | } 253 | 254 | WhiskerTree::WhiskerTree( const RemyBuffers::WhiskerTree & dna ) 255 | : _domain( dna.domain() ), 256 | _children(), 257 | _leaf() 258 | { 259 | if ( dna.has_leaf() ) { 260 | assert( dna.children_size() == 0 ); 261 | _leaf.emplace_back( dna.leaf() ); 262 | } else { 263 | assert( dna.children_size() > 0 ); 264 | for ( const auto &x : dna.children() ) { 265 | _children.emplace_back( x ); 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /graph/gl_objects.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "gl_objects.hh" 6 | #include "image.hh" 7 | 8 | using namespace std; 9 | 10 | GLFWContext::GLFWContext() 11 | { 12 | glfwSetErrorCallback( error_callback ); 13 | 14 | glfwInit(); 15 | } 16 | 17 | void GLFWContext::error_callback( const int, const char * const description ) 18 | { 19 | throw runtime_error( description ); 20 | } 21 | 22 | GLFWContext::~GLFWContext() 23 | { 24 | glfwTerminate(); 25 | } 26 | 27 | Window::Window( const unsigned int width, const unsigned int height, const string & title ) 28 | : window_() 29 | { 30 | glfwDefaultWindowHints(); 31 | 32 | /* 33 | glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); 34 | glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 1 ); 35 | glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); 36 | */ // not supported using software renderer on VMware Fusion (hardware even worse) 37 | glfwWindowHint( GLFW_SAMPLES, 4 ); 38 | glfwWindowHint( GLFW_RESIZABLE, GL_TRUE ); 39 | // glfwWindowHint( GLFW_ALPHA_BITS, 0 ); 40 | 41 | window_.reset( glfwCreateWindow( width, height, title.c_str(), nullptr, nullptr ) ); 42 | if ( not window_.get() ) { 43 | throw runtime_error( "could not create window" ); 44 | } 45 | } 46 | 47 | void Window::make_context_current( const bool initialize_extensions ) 48 | { 49 | glfwMakeContextCurrent( window_.get() ); 50 | 51 | glCheck( "after MakeContextCurrent" ); 52 | 53 | if ( initialize_extensions ) { 54 | glewExperimental = GL_TRUE; 55 | glewInit(); 56 | glCheck( "after initializing GLEW", true ); 57 | } 58 | } 59 | 60 | bool Window::should_close( void ) const 61 | { 62 | return glfwWindowShouldClose( window_.get() ); 63 | } 64 | 65 | void Window::swap_buffers( void ) 66 | { 67 | return glfwSwapBuffers( window_.get() ); 68 | } 69 | 70 | void Window::hide_cursor( const bool hidden ) 71 | { 72 | glfwSetInputMode( window_.get(), GLFW_CURSOR, hidden ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL ); 73 | } 74 | 75 | bool Window::key_pressed( const int key ) const 76 | { 77 | return GLFW_PRESS == glfwGetKey( window_.get(), key ); 78 | } 79 | 80 | pair Window::size( void ) const 81 | { 82 | int width, height; 83 | glfwGetFramebufferSize( window_.get(), &width, &height ); 84 | 85 | if ( width < 0 or height < 0 ) { 86 | throw runtime_error( "negative framebuffer width or height" ); 87 | } 88 | 89 | return pair( width, height ); 90 | } 91 | 92 | void Window::Deleter::operator() ( GLFWwindow * x ) const 93 | { 94 | glfwHideWindow( x ); 95 | glfwDestroyWindow( x ); 96 | } 97 | 98 | VertexBufferObject::VertexBufferObject() 99 | : num_() 100 | { 101 | glGenBuffers( 1, &num_ ); 102 | } 103 | 104 | VertexBufferObject::~VertexBufferObject() 105 | { 106 | glDeleteBuffers( 1, &num_ ); 107 | } 108 | 109 | VertexArrayObject::VertexArrayObject() 110 | : num_() 111 | { 112 | glGenVertexArrays( 1, &num_ ); 113 | } 114 | 115 | VertexArrayObject::~VertexArrayObject() 116 | { 117 | glDeleteVertexArrays( 1, &num_ ); 118 | } 119 | 120 | void VertexArrayObject::bind( void ) 121 | { 122 | glBindVertexArray( num_ ); 123 | } 124 | 125 | Texture::Texture( const unsigned int width, const unsigned int height ) 126 | : num_(), 127 | width_( width ), 128 | height_( height ) 129 | { 130 | glGenTextures( 1, &num_ ); 131 | } 132 | 133 | Texture::~Texture() 134 | { 135 | glDeleteTextures( 1, &num_ ); 136 | } 137 | 138 | void Texture::bind( void ) 139 | { 140 | glBindTexture( GL_TEXTURE_RECTANGLE, num_ ); 141 | 142 | resize( width_, height_ ); 143 | } 144 | 145 | void Texture::resize( const unsigned int width, const unsigned int height ) 146 | { 147 | width_ = width; 148 | height_ = height; 149 | glTexImage2D( GL_TEXTURE_RECTANGLE, 0, GL_RGBA8, width_, height_, 0, 150 | GL_BGRA, GL_UNSIGNED_BYTE, nullptr ); 151 | } 152 | 153 | void Texture::load( const Image & image ) 154 | { 155 | if ( image.size() != size() ) { 156 | throw runtime_error( "image size does not match texture dimensions" ); 157 | } 158 | 159 | glPixelStorei( GL_UNPACK_ROW_LENGTH, image.stride_pixels() ); 160 | glTexSubImage2D( GL_TEXTURE_RECTANGLE, 0, 0, 0, width_, height_, 161 | GL_BGRA, GL_UNSIGNED_BYTE, &image.pixels().front() ); 162 | } 163 | 164 | void compile_shader( const GLuint num, const string & source ) 165 | { 166 | const char * source_c_str = source.c_str(); 167 | glShaderSource( num, 1, &source_c_str, nullptr ); 168 | glCompileShader( num ); 169 | 170 | /* check if there were log messages */ 171 | GLint log_length; 172 | glGetShaderiv( num, GL_INFO_LOG_LENGTH, &log_length ); 173 | 174 | if ( log_length > 1 ) { 175 | unique_ptr buffer( new GLchar[ log_length ] ); 176 | GLsizei written_length; 177 | glGetShaderInfoLog( num, log_length, &written_length, buffer.get() ); 178 | 179 | if ( written_length + 1 != log_length ) { 180 | throw runtime_error( "GL shader log size mismatch" ); 181 | } 182 | 183 | cerr << "GL shader compilation log: " << string( buffer.get(), log_length ) << endl; 184 | } 185 | 186 | /* check if it compiled */ 187 | GLint success; 188 | glGetShaderiv( num, GL_COMPILE_STATUS, &success ); 189 | 190 | if ( not success ) { 191 | throw runtime_error( "GL shader failed to compile" ); 192 | } 193 | } 194 | 195 | void Program::link( void ) 196 | { 197 | glLinkProgram( num_ ); 198 | } 199 | 200 | void Program::use( void ) 201 | { 202 | glUseProgram( num_ ); 203 | } 204 | 205 | GLint Program::attribute_location( const string & name ) const 206 | { 207 | const GLint ret = glGetAttribLocation( num_, name.c_str() ); 208 | if ( ret < 0 ) { 209 | throw runtime_error( "attribute not found: " + name ); 210 | } 211 | return ret; 212 | } 213 | 214 | GLint Program::uniform_location( const string & name ) const 215 | { 216 | const GLint ret = glGetUniformLocation( num_, name.c_str() ); 217 | if ( ret < 0 ) { 218 | throw runtime_error( "uniform not found: " + name ); 219 | } 220 | return ret; 221 | } 222 | 223 | Program::~Program() 224 | { 225 | glDeleteProgram( num_ ); 226 | } 227 | 228 | void glCheck( const string & where, const bool expected ) 229 | { 230 | GLenum error = glGetError(); 231 | 232 | if ( error != GL_NO_ERROR ) { 233 | while ( error != GL_NO_ERROR ) { 234 | if ( not expected ) { 235 | cerr << "GL error " << where << ": " << gluErrorString( error ) << endl; 236 | } 237 | error = glGetError(); 238 | } 239 | 240 | if ( not expected ) { 241 | throw runtime_error( "GL error " + where ); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/remy.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ratbreeder.hh" 10 | #include "dna.pb.h" 11 | #include "configrange.hh" 12 | using namespace std; 13 | 14 | void print_range( const Range & range, const string & name ) 15 | { 16 | printf( "Optimizing for %s over [%f : %f : %f]\n", name.c_str(), 17 | range.low, range.incr, range.high ); 18 | } 19 | 20 | int main( int argc, char *argv[] ) 21 | { 22 | WhiskerTree whiskers; 23 | string output_filename; 24 | BreederOptions options; 25 | WhiskerImproverOptions whisker_options; 26 | RemyBuffers::ConfigRange input_config; 27 | string config_filename; 28 | 29 | for ( int i = 1; i < argc; i++ ) { 30 | string arg( argv[ i ] ); 31 | if ( arg.substr( 0, 3 ) == "if=" ) { 32 | string filename( arg.substr( 3 ) ); 33 | int fd = open( filename.c_str(), O_RDONLY ); 34 | if ( fd < 0 ) { 35 | perror( "open" ); 36 | exit( 1 ); 37 | } 38 | 39 | RemyBuffers::WhiskerTree tree; 40 | if ( !tree.ParseFromFileDescriptor( fd ) ) { 41 | fprintf( stderr, "Could not parse %s.\n", filename.c_str() ); 42 | exit( 1 ); 43 | } 44 | whiskers = WhiskerTree( tree ); 45 | 46 | if ( close( fd ) < 0 ) { 47 | perror( "close" ); 48 | exit( 1 ); 49 | } 50 | 51 | } else if ( arg.substr( 0, 3 ) == "of=" ) { 52 | output_filename = string( arg.substr( 3 ) ); 53 | 54 | } else if ( arg.substr( 0, 4 ) == "opt=" ) { 55 | whisker_options.optimize_window_increment = false; 56 | whisker_options.optimize_window_multiple = false; 57 | whisker_options.optimize_intersend = false; 58 | for ( char & c : arg.substr( 4 ) ) { 59 | if ( c == 'b' ) { 60 | whisker_options.optimize_window_increment = true; 61 | } else if ( c == 'm' ) { 62 | whisker_options.optimize_window_multiple = true; 63 | } else if ( c == 'r' ) { 64 | whisker_options.optimize_intersend = true; 65 | } else { 66 | fprintf( stderr, "Invalid optimize option: %c\n", c ); 67 | exit( 1 ); 68 | } 69 | } 70 | 71 | } else if ( arg.substr(0, 3 ) == "cf=" ) { 72 | config_filename = string( arg.substr( 3 ) ); 73 | int cfd = open( config_filename.c_str(), O_RDONLY ); 74 | if ( cfd < 0 ) { 75 | perror( "open config file error"); 76 | exit( 1 ); 77 | } 78 | if ( !input_config.ParseFromFileDescriptor( cfd ) ) { 79 | fprintf( stderr, "Could not parse input config from file %s. \n", config_filename.c_str() ); 80 | exit ( 1 ); 81 | } 82 | if ( close( cfd ) < 0 ) { 83 | perror( "close" ); 84 | exit( 1 ); 85 | } 86 | } 87 | } 88 | 89 | if ( config_filename.empty() ) { 90 | fprintf( stderr, "An input configuration protobuf must be provided via the cf= option. \n"); 91 | fprintf( stderr, "You can generate one using './configuration'. \n"); 92 | exit ( 1 ); 93 | } 94 | 95 | options.config_range = ConfigRange( input_config ); 96 | 97 | RatBreeder breeder( options, whisker_options ); 98 | 99 | unsigned int run = 0; 100 | 101 | printf( "#######################\n" ); 102 | printf( "Evaluator simulations will run for %d ticks\n", 103 | options.config_range.simulation_ticks ); 104 | printf( "Optimizing window increment: %d, window multiple: %d, intersend: %d\n", 105 | whisker_options.optimize_window_increment, whisker_options.optimize_window_multiple, 106 | whisker_options.optimize_intersend); 107 | print_range( options.config_range.link_ppt, "link packets_per_ms" ); 108 | print_range( options.config_range.rtt, "rtt_ms" ); 109 | print_range( options.config_range.num_senders, "num_senders" ); 110 | print_range( options.config_range.mean_on_duration, "mean_on_duration" ); 111 | print_range( options.config_range.mean_off_duration, "mean_off_duration" ); 112 | print_range( options.config_range.stochastic_loss_rate, "stochastic_loss_rate" ); 113 | if ( options.config_range.buffer_size.low != numeric_limits::max() ) { 114 | print_range( options.config_range.buffer_size, "buffer_size" ); 115 | } else { 116 | printf( "Optimizing for infinitely sized buffers. \n"); 117 | } 118 | 119 | printf( "Initial rules (use if=FILENAME to read from disk): %s\n", whiskers.str().c_str() ); 120 | printf( "#######################\n" ); 121 | 122 | if ( !output_filename.empty() ) { 123 | printf( "Writing to \"%s.N\".\n", output_filename.c_str() ); 124 | } else { 125 | printf( "Not saving output. Use the of=FILENAME argument to save the results.\n" ); 126 | } 127 | 128 | RemyBuffers::ConfigVector training_configs; 129 | bool written = false; 130 | 131 | while ( 1 ) { 132 | auto outcome = breeder.improve( whiskers ); 133 | printf( "run = %u, score = %f\n", run, outcome.score ); 134 | 135 | printf( "whiskers: %s\n", whiskers.str().c_str() ); 136 | 137 | for ( auto &run : outcome.throughputs_delays ) { 138 | if ( !(written) ) { 139 | for ( auto &run : outcome.throughputs_delays) { 140 | // record the config to the protobuf 141 | RemyBuffers::NetConfig* net_config = training_configs.add_config(); 142 | *net_config = run.first.DNA(); 143 | written = true; 144 | 145 | } 146 | } 147 | printf( "===\nconfig: %s\n", run.first.str().c_str() ); 148 | for ( auto &x : run.second ) { 149 | printf( "sender: [tp=%f, del=%f]\n", x.first / run.first.link_ppt, x.second / run.first.delay ); 150 | } 151 | } 152 | 153 | if ( !output_filename.empty() ) { 154 | char of[ 128 ]; 155 | snprintf( of, 128, "%s.%d", output_filename.c_str(), run ); 156 | fprintf( stderr, "Writing to \"%s\"... ", of ); 157 | int fd = open( of, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR ); 158 | if ( fd < 0 ) { 159 | perror( "open" ); 160 | exit( 1 ); 161 | } 162 | 163 | auto remycc = whiskers.DNA(); 164 | remycc.mutable_config()->CopyFrom( options.config_range.DNA() ); 165 | remycc.mutable_optimizer()->CopyFrom( Whisker::get_optimizer().DNA() ); 166 | remycc.mutable_configvector()->CopyFrom( training_configs ); 167 | if ( not remycc.SerializeToFileDescriptor( fd ) ) { 168 | fprintf( stderr, "Could not serialize RemyCC.\n" ); 169 | exit( 1 ); 170 | } 171 | 172 | if ( close( fd ) < 0 ) { 173 | perror( "close" ); 174 | exit( 1 ); 175 | } 176 | 177 | fprintf( stderr, "done.\n" ); 178 | } 179 | 180 | fflush( NULL ); 181 | run++; 182 | } 183 | 184 | return 0; 185 | } 186 | --------------------------------------------------------------------------------