I have this writer, I run this like so: ./writer 12 14
it creates two shared memory segments with a spsc queue in each one.
The writer just send text with a counter to the spsc queue of his memory segment.
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <boost/process.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace bip = boost::interprocess;
namespace chro = std::chrono;
namespace blf = boost::lockfree;
using char_alloc = bip::allocator<char, bip::managed_shared_memory::segment_manager>;
using shared_string = bip::basic_string<char, std::char_traits<char>, char_alloc>;
using ring_buffer = blf::spsc_queue<shared_string, blf::capacity<200>>;
int main(int argc, char* argv[]) {
if (argc > 1) {
std::string n1 = argv[1];
std::string n2 = argv[2];
const std::string shm_name1 = "segmentrb" + n1;
const std::string shm_name2 = "segmentrb" + n2;
const std::string qname = "queue";
boost::interprocess::shared_memory_object::remove(shm_name1.c_str());
boost::interprocess::shared_memory_object::remove(shm_name2.c_str());
bip::managed_shared_memory seg1(bip::open_or_create, shm_name1.c_str(), 10'000);
char_alloc char_alloc1(seg1.get_segment_manager());
ring_buffer* qu1 = seg1.find_or_construct<ring_buffer>(qname.c_str())();
bip::managed_shared_memory seg2(bip::open_or_create, shm_name2.c_str(), 10'000);
char_alloc char_alloc2(seg2.get_segment_manager());
ring_buffer* qu2 = seg2.find_or_construct<ring_buffer>(qname.c_str())();
int counter = 0;
while (true) {
std::string text1 = "Text from 1, count ";
text1.append(std::to_string(counter));
qu1->push(shared_string(text1.c_str(), char_alloc1));
std::string text2 = "Text from 2, count ";
text2.append(std::to_string(counter));
qu2->push(shared_string(text2.c_str(), char_alloc2));
std::this_thread::sleep_for(std::chrono::milliseconds(1));
counter++;
}
}
}
Then I have this reader, that reads an pop the spsc of the two segments:
I run this with: ./reader 12 14
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <boost/process.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace bip = boost::interprocess;
namespace chro = std::chrono;
namespace blf = boost::lockfree;
using char_alloc = bip::allocator<char, bip::managed_shared_memory::segment_manager>;
using shared_string = bip::basic_string<char, std::char_traits<char>, char_alloc>;
using ring_buffer = blf::spsc_queue<shared_string, blf::capacity<200>>;
int main(int argc, char* argv[]) {
if (argc > 1) {
std::string n1 = argv[1];
std::string n2 = argv[2];
const std::string shm_name1 = "segmentrb" + n1;
const std::string shm_name2 = "segmentrb" + n2;
const std::string qname = "queue";
bip::managed_shared_memory seg1(bip::open_only, shm_name1.c_str());
char_alloc char_alloc1(seg1.get_segment_manager());
ring_buffer* qu1 = seg1.find<ring_buffer>(qname.c_str()).first;
bip::managed_shared_memory seg2(bip::open_only, shm_name2.c_str());
char_alloc char_alloc2(seg2.get_segment_manager());
ring_buffer* qu2 = seg2.find<ring_buffer>(qname.c_str()).first;
while (true) {
shared_string v1(char_alloc1);
shared_string v2(char_alloc2);
qu1->pop(v1);
qu2->pop(v2);
long lv1 = v1.length();
long lv2 = v2.length();
long lvs = lv1 + lv2;
if (lvs > 0) {
if (lv1 > 0) {
std::cout << "Rec1: " << v1 << "\n";
}
if (lv2 > 0) {
std::cout << "Rec2: " << v2 << "\n";
}
}
else {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
}
}
when I do kill -9 on the reader I get this on the writer:
terminate called after throwing an instance of 'boost::interprocess::bad_alloc'
what(): boost::interprocess::bad_alloc
Aborted (core dumped)
How can I avoid the writer being killed?
Reviewed your producer code, removing duplicate code:
Live On Coliru
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <boost/process.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace bip = boost::interprocess;
namespace blf = boost::lockfree;
using namespace std::literals;
namespace Shared {
using Segment = bip::managed_shared_memory;
using Mgr = Segment::segment_manager;
template <typename T> using Alloc = bip::allocator<T, Mgr>;
using shared_string = bip::basic_string<char, std::char_traits<char>, Alloc<char>>;
using ring_buffer = blf::spsc_queue<shared_string, blf::capacity<200>>;
struct Queue {
Queue(std::string const& name, std::string const& qname = "queue")
: _name(name)
, _buffer(_segment.find_or_construct<ring_buffer>(qname.c_str())()) {}
std::string const& name() const { return _name; }
bool push(std::string const& item) {
return _buffer->push(
shared_string(item.begin(), item.end(), _segment.get_segment_manager()));
}
private:
std::string const _name;
struct pre_remover_t {
pre_remover_t(std::string const& name) {
bip::shared_memory_object::remove(name.c_str());
}
} _pre_remover{_name};
bip::managed_shared_memory _segment{bip::open_or_create, _name.c_str(), 10'000};
ring_buffer* _buffer = nullptr;
};
} // namespace Shared
int main(int argc, char* argv[]) {
std::deque<Shared::Queue> queues;
for (auto& arg : std::vector<std::string>(argv + 1, argv + argc))
queues.emplace_back("segmentrb" + arg);
for (int counter = 0; true; ++counter) {
for (auto& q : queues)
q.push("Text from " + q.name() + ", count " + std::to_string(counter));
std::this_thread::sleep_for(1ms);
}
}
Reading that makes the problem readily evident: You are pushing elements every 1ms.
Killing the producer gives you 200*1ms = 0.2s before the queue is full. Obviously, then push will fail with the exception indicating that the queue is out of capacity.
UPDATE
From the comments, a version that combines producer and consumer:
Live On Coliru
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <deque>
#include <iomanip>
#include <iostream>
#include <ranges>
#include <thread>
namespace bip = boost::interprocess;
namespace blf = boost::lockfree;
using namespace std::literals;
using std::this_thread::sleep_for;
using std::ranges::views::transform;
namespace Shared {
static constexpr std::string_view queue_name = "queue";
using Segment = bip::managed_shared_memory;
struct pre_remover_t {
pre_remover_t(std::string const& name) { bip::shared_memory_object::remove(name.c_str()); }
};
using Mgr = Segment::segment_manager;
template <typename T> using Alloc = bip::allocator<T, Mgr>;
using String = bip::basic_string<char, std::char_traits<char>, Alloc<char>>;
using ring_buffer = blf::spsc_queue<String, blf::capacity<200>>;
struct Producer {
Producer(std::string const& name)
: _name(name)
, _buffer(_segment.find_or_construct<ring_buffer>(queue_name.data())()) {}
std::string const& name() const { return _name; }
bool push(std::string const& item) {
std::cerr << "push: " << quoted(item) << std::endl;
return _buffer->push(
String(item.begin(), item.end(), _segment.get_segment_manager()));
}
private:
std::string const _name;
pre_remover_t _pre_remover{_name};
Segment _segment{bip::open_or_create, _name.c_str(), 10'000};
ring_buffer* _buffer = nullptr;
};
struct Consumer {
Consumer(std::string const& name)
: _name(name)
, _buffer(_segment.find_or_construct<ring_buffer>(queue_name.data())()) {}
String pop() {
String r(_segment.get_segment_manager());
_buffer->pop(r);
return r;
}
private:
std::string const _name;
Segment _segment{bip::open_only, _name.c_str()};
ring_buffer* _buffer = nullptr;
};
} // namespace Shared
int main(int argc, char* argv[]) {
std::deque<std::string> args(argv + 1, argv + argc);
bool const is_producer = args.front() == "producer";
args.pop_front();
if (is_producer) {
std::deque<Shared::Producer> queues;
for (auto& arg : args)
queues.emplace_back("segmentrb" + arg);
for (int counter = 0; true; ++counter) {
for (auto& q : queues)
q.push("Text from " + q.name() + ", count " + std::to_string(counter));
sleep_for(1s);
}
} else { // consumer
std::deque<Shared::Consumer> queues;
for (auto& arg : args)
queues.emplace_back("segmentrb" + arg);
for (;;) {
auto no_data = true;
for (int index = 0; auto&& v : queues | transform(&Shared::Consumer::pop)) {
if (!v.empty()) {
no_data = false;
std::cout << "Rec" << ++index << ": " << v << "\n";
}
}
if (no_data) {
std::cerr << "Consumer no-data cycle" << std::endl;
sleep_for(2s);
}
}
}
}
With a local demonstration as well:
Related
In the code below, I'm trying to initialize a managed_shared_memory object. When the constructor is invoked I see the below error message -
terminate called after throwing an instance of 'boost::interprocess::interprocess_exception'
what(): boost::interprocess_exception::library_error
Aborted
why is this exception being thrown?
I'm running this on an ubuntu 16.04 linux OS, compiled the program using g++ 9.3.0. Boost version 1.58.0
struct test_obj {
size_t x;
size_t y;
uint8_t buf[32];
bool is_valid;
};
class shm_wrapper {
public:
shm_wrapper() : m_shm(
boost::interprocess::open_or_create,
"my_shm",
sizeof(test_obj) )
{};
private:
boost::interprocess::managed_shared_memory m_shm;
};
It aborts because the size is insufficient for the segment manager control blocks.
sizeof(test_obj) is just 56 bytes (on my system).
If you give the segment 10 KiB, it reports 224 bytes effectively used:
Live On Coliru
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
namespace bip = boost::interprocess;
#ifdef COLIRU
using segment_type = bip::managed_mapped_file;
#else
using segment_type = bip::managed_shared_memory;
#endif
static constexpr size_t SegmentSize = 10 * 1024;
struct test_obj {
size_t x;
size_t y;
uint8_t buf[32];
bool is_valid;
};
class shm_wrapper {
public:
shm_wrapper() : m_shm(bip::open_or_create, "my_shm", SegmentSize){};
size_t free() const { return m_shm.get_free_memory(); }
private:
segment_type m_shm;
};
#include <iostream>
int main() {
std::cout << sizeof(test_obj) << std::endl;
shm_wrapper w;
std::cout << w.free() << "\n";
std::cout << "Effectively used:" << (SegmentSize - w.free()) << "\n";
}
Prints
56
10016
Effectively used:224
Summary
Maybe you didn't want a segment manager with dynamic allocation features. In that case, have a look at shared_memory_object instead.
BONUS: Example Using Mapped Region
To store a "dumb object" in a fixed-size region, you don't need a segment manager. You'd use a mapped_region on a shared_memory_object (or a file_mapping).
Here's another sample Live On Coliru
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
namespace bip = boost::interprocess;
struct test_obj {
size_t x;
size_t y;
uint8_t buf[32];
bool is_valid;
};
#ifdef COLIRU
#include <fstream>
using mapping_type = bip::file_mapping;
#else
using mapping_type = bip::shared_memory_object;
#endif
template <typename T>
class shm_wrapper {
static_assert(std::is_trivial_v<T>);
static_assert(std::is_standard_layout_v<T>);
#ifdef COLIRU // file mappings require more work to cater for the storage
struct backing_t { } backing;
backing_t ensure_file(std::string name, size_t size) {
std::filebuf fbuf;
fbuf.open(name, std::ios::in | std::ios::out | std::ios::app | std::ios::binary);
//set the size, sparsely
fbuf.pubseekoff(size-1, std::ios_base::beg);
fbuf.sputc(0);
fbuf.close();
return {};
}
public:
shm_wrapper()
: backing { ensure_file("my_shm", sizeof(T)) },
m_mappable("my_shm", bip::read_write),
m_reg(m_mappable, bip::read_write, 0, sizeof(T))
{ }
#else
public:
shm_wrapper()
: m_mappable(bip::open_or_create, "my_shm", bip::read_write),
m_reg(m_mappable, bip::read_write, 0, sizeof(T))
{
m_mappable.truncate(sizeof(T));
}
#endif
T& get() { return *static_cast<T*>(m_reg.get_address()); }
T const& get() const { return *static_cast<T const*>(m_reg.get_address()); }
auto size() const { return m_reg.get_size(); }
auto flush() { return m_reg.flush(); }
private:
mapping_type m_mappable;
bip::mapped_region m_reg;
};
int main() {
shm_wrapper<test_obj> w;
std::cout << "x:" << w.get().x << "\n";
w.get().x ^= 0xa7;
return w.flush()? 0 : 1;
}
Prints, when run 4x in succession:
x:0
x:167
x:0
x:167
I'm doing a benchmark on boost::interprocess:vector and std::vector, since I'm gonna use shared memory in my program but I'm concerned with any potential performance issues.
My benchmark is simply random accessing a vector, and it turned out that std::vector is almost 2x faster than boost::interprocess::vector.
Note: in the benchmark I only have a single process, and I don't do any synchronization manually.
I don't know where is the bottleneck....I have three guess:
shared memory make it slower
boost vector has a slower implementation
boost shared memory as well as its containers have some overhead somehow, i.e., if I use mmap and do things in a plain way, it will be better
And what further experiment should I do to figure out this? Or tune something to make it faster? Any idea?
Here is the benchmark code:
for boost::interprocess::vector
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <random>
#include <string>
#include <vector>
using namespace boost::interprocess;
typedef allocator<double, managed_shared_memory::segment_manager> ShmemAllocator;
typedef vector<double, ShmemAllocator> MyVector;
const int total_size = 2000 * 2000;
const int mem_size = 2000 * 2000 * 8 * 2;
const int query_size = 100000;
int main(int argc, char *argv[]) {
std::uniform_real_distribution<double> unif(0, 10000);
std::default_random_engine re;
std::vector<double> data;
data.reserve(total_size);
for (int i = 0; i < total_size; ++i) {
data.push_back(unif(re));
}
std::vector<int> query;
query.reserve(query_size);
for (int i = 0; i < query_size; ++i) {
query.push_back(rand() % total_size);
}
struct shm_remove {
shm_remove() { shared_memory_object::remove("MySharedMemory"); }
~shm_remove() { shared_memory_object::remove("MySharedMemory"); }
} remover;
managed_shared_memory segment(create_only, "MySharedMemory", mem_size);
const ShmemAllocator alloc_inst(segment.get_segment_manager());
MyVector *myvector = segment.construct<MyVector>("MyVector")(alloc_inst);
myvector->reserve(total_size);
for (auto d : data) myvector->push_back(d);
auto t1 = std::chrono::high_resolution_clock::now();
for (auto q : query) {
double res = (*myvector)[q];
}
auto t2 = std::chrono::high_resolution_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << std::endl;
return 0;
}
for std::vector
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <chrono>
#include <cstdlib> //std::system
#include <iostream>
#include <random>
#include <string>
#include <vector>
const int total_size = 2000 * 2000;
const int mem_size = 2000 * 2000 * 8 * 8;
const int query_size = 100000;
int main(int argc, char *argv[]) {
std::uniform_real_distribution<double> unif(0, 10000);
std::default_random_engine re;
std::vector<double> data;
data.reserve(total_size);
for (int i = 0; i < total_size; ++i) {
data.push_back(unif(re));
}
std::vector<int> query;
query.reserve(query_size);
for (int i = 0; i < query_size; ++i) {
query.push_back(rand() % total_size);
}
std::vector<double> myvector;
myvector.reserve(total_size);
for (auto d : data) myvector.push_back(d);
auto t1 = std::chrono::high_resolution_clock::now();
for (auto q : query) {
double res = myvector[q];
}
auto t2 = std::chrono::high_resolution_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << std::endl;
return 0;
}
There is a string like this: M90I4D7
I need to push it in to this kind of struct:
struct CigarOp {
char Type; //!< CIGAR operation type (MIDNSHPX=)
uint32_t Length; //!< CIGAR operation length (number of bases)
//! constructor
CigarOp(const char type = '\0',
const uint32_t& length = 0)
: Type(type)
, Length(length)
{ }
};
which means I need to split it into 3 groups and each of them is a CigarOp( 'M' ,90 'I', 4 'D' ,7 )
Assuming that the string is of the form ([A-Z][0-9]+)*, you could quite simply do something like this:
#include <sstream>
...
std::vector<CigarOp> cigars;
std::istringstream parser("M90I4D7");
char c;
std::uint32_t l;
while(parser >> c >> l) {
cigars.push_back(CigarOp(c, l));
}
Note that this code doesn't do any sort of validation. If validation is necessary, one way to achieve it is to use Boost.Spirit (found on http://boost.org):
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <cstdint>
#include <iostream>
struct CigarOp {
char Type;
std::uint32_t Length;
};
BOOST_FUSION_ADAPT_STRUCT(CigarOp, (char, Type) (std::uint32_t, Length))
int main() {
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::char_;
using boost::spirit::qi::uint_;
using boost::spirit::qi::standard::space;
std::vector<CigarOp> cigars;
std::string s = "M90I4D7";
std::string::const_iterator first = s.begin(), last = s.end();
bool r = phrase_parse(first, last, *(char_ >> uint_), space, cigars);
if(r && first == last) {
// string was well-formed
for(auto const &cigar : cigars) {
std::cout << cigar.Type << ", " << cigar.Length << '\n';
}
}
}
how about:
#include <cstdio>
#include <cctype>
#include <vector>
#include <iostream>
#include <cstdlib>
using namespace std;
struct CigarOp {
char op; //!< CIGAR operation type (MIDNSHPX=)
int size; //!< CIGAR operation length (number of bases)
static int parse(const char* s,vector<CigarOp>& v)
{
char* p=(char*)(s);
while(*p!=0)
{
char* endptr;
CigarOp c;
c.op = *p;
if(!isalpha(c.op)) return -1;
++p;
if(!isdigit(*p)) return -1;
c.size =strtol(p,&endptr,10);
if(c.size<=0) return -1;
v.push_back(c);
p=endptr;
}
return 0;
}
};
int main(int argc,char** argv)
{
vector<CigarOp> cigar;
if(CigarOp::parse("M90I4D7",cigar)!=0) return -1;
for(size_t i=0;i< cigar.size();++i)
{
cout << cigar[i].op << ":" << cigar[i].size << endl;
}
return 0;
}
btw , for bioinformatics, you should ask biostars.org.
I ported a Java GC test program to C++ (see the code below) as well as Python. The Java and Python performance is much greater than C++ and I was thinking this was due to all the calls to new that have to be done to create the strings each time. I've tried using Boost's fast_pool_allocator but that actually worsened performance from 700ms to 1200ms. Am I using the allocator wrong, or is there something else I should be doing?
EDIT: Compiled with g++ -O3 -march=native --std=c++11 garbage.cpp -lboost_system. g++ is version 4.8.1
One iteration takes in Python is about 300ms and with Java about 50ms. std::allocator gives about 700ms and boost::fast_pool_allocator gives about 1200ms.
#include <string>
#include <vector>
#include <chrono>
#include <list>
#include <iostream>
#include <boost/pool/pool_alloc.hpp>
#include <memory>
//#include <gc/gc_allocator.h>
using namespace std;
#include <sstream>
typedef boost::fast_pool_allocator<char> c_allocator;
//typedef std::allocator<char> c_allocator;
typedef basic_string<char, char_traits<char>, c_allocator> pool_string;
namespace patch {
template <typename T> pool_string to_string(const T& in) {
std::basic_stringstream<char, char_traits<char>, c_allocator> stm;
stm << in;
return stm.str();
}
}
#include "mytime.hpp"
class Garbage {
public:
vector<pool_string> outer;
vector<pool_string> old;
const int nThreads = 1;
//static auto time = chrono::high_resolution_clock();
void go() {
// outer.resize(1000000);
//old.reserve(1000000);
auto tt = mytime::msecs();
for (int i = 0; i < 10; ++i) {
if (i % 100 == 0) {
cout << "DOING AN OLD" << endl;
doOld();
tt = mytime::msecs();
}
for (int j = 0; j < 1000000/nThreads; ++j)
outer.push_back(patch::to_string(j));
outer.clear();
auto t = mytime::msecs();
cout << (t - tt) << endl;
tt = t;
}
}
void doOld() {
old.clear();
for (int i = 0; i < 1000000/nThreads; ++i)
old.push_back(patch::to_string(i));
}
};
int main() {
Garbage().go();
}
The problem is you're using a new string stream each time to convert an integer.
Fix it:
namespace patch {
template <typename T> pool_string to_string(const T& in) {
return boost::lexical_cast<pool_string>(in);
}
}
Now the timings are:
DOING AN OLD
0.175462
0.0670085
0.0669926
0.0687969
0.0692518
0.0669318
0.0669196
0.0669187
0.0668962
0.0669185
real 0m0.801s
user 0m0.784s
sys 0m0.016s
See it Live On Coliru
Full code for reference:
#include <boost/pool/pool_alloc.hpp>
#include <chrono>
#include <iostream>
#include <list>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
//#include <gc/gc_allocator.h>
using string = std::string;
namespace patch {
template <typename T> string to_string(const T& in) {
return boost::lexical_cast<string>(in);
}
}
class Timer
{
typedef std::chrono::high_resolution_clock clock;
clock::time_point _start;
public:
Timer() { reset(); }
void reset() { _start = now(); }
double elapsed()
{
using namespace std::chrono;
auto e = now() - _start;
return duration_cast<nanoseconds>(e).count()*1.0e-9;
}
clock::time_point now()
{
return clock::now();
}
};
class Garbage {
public:
std::vector<string> outer;
std::vector<string> old;
const int nThreads = 1;
void go() {
outer.resize(1000000);
//old.reserve(1000000);
Timer timer;
for (int i = 0; i < 10; ++i) {
if (i % 100 == 0) {
std::cout << "DOING AN OLD" << std::endl;
doOld();
}
for (int j = 0; j < 1000000/nThreads; ++j)
outer.push_back(patch::to_string(j));
outer.clear();
std::cout << timer.elapsed() << std::endl;
timer.reset();
}
}
void doOld() {
old.clear();
for (int i = 0; i < 1000000/nThreads; ++i)
old.push_back(patch::to_string(i));
}
};
int main() {
Garbage().go();
}
Since I don't use boost on my machine, I simplified the code to use standard C++11 to_string (thus accidentally "fixing" the problem sehe found), and got this:
#include <string>
#include <vector>
#include <chrono>
#include <list>
#include <iostream>
#include <memory>
//#include <gc/gc_allocator.h>
#include <sstream>
using namespace std;
class Timer
{
typedef std::chrono::high_resolution_clock clock;
clock::time_point _start;
public:
Timer() { reset(); }
void reset() { _start = now(); }
double elapsed()
{
using namespace std::chrono;
auto e = now() - _start;
return duration_cast<nanoseconds>(e).count()*1.0e-9;
}
clock::time_point now()
{
return clock::now();
}
};
class Garbage {
public:
vector<string> outer;
vector<string> old;
const int nThreads = 1;
Timer timer;
void go() {
// outer.resize(1000000);
//old.reserve(1000000);
for (int i = 0; i < 10; ++i) {
if (i % 100 == 0) {
cout << "DOING AN OLD" << endl;
doOld();
}
for (int j = 0; j < 1000000/nThreads; ++j)
outer.push_back(to_string(j));
outer.clear();
cout << timer.elapsed() << endl;
timer.reset();
}
}
void doOld() {
old.clear();
for (int i = 0; i < 1000000/nThreads; ++i)
old.push_back(to_string(i));
}
};
int main() {
Garbage().go();
}
Compiling with:
$ g++ -O3 -std=c++11 gc.cpp
$ ./a.out
DOING AN OLD
0.414637
0.189082
0.189143
0.186336
0.184449
0.18504
0.186302
0.186055
0.183123
0.186835
clang 3.5 build with source from Friday 18th of April 2014 gives similar results with the same compiler options.
My processor is a AMD Phenom(tm) II X4 965, running at 3.6GHz (if I remember right).
#include <tuple>
#include <vector>
#include <string>
#include <iostream>
//-------------------------------------------------------------------------
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
//-------------------------------------------------------------------------
namespace ph = boost::phoenix;
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> Sink;
typedef std::tuple<double,int> Data;
typedef std::vector<Data> Container;
struct Generator : karma::grammar<Sink,Container()>
{
Generator(void) : Generator::base_type(start,"Generator")
{
start = data % karma::eol;
//data = karma::delimit[???];
return;
}
karma::rule<Sink,Container()> start;
karma::rule<Sink,Data()> data;
};
//-------------------------------------------------------------------------
int main(int argc,char** argv)
{
Generator generator;
Container container;
container.push_back(Data(3.1415,100500));
container.push_back(Data(2.7183,9000));
std::string result;
Sink sink(result);
bool b = boost::spirit::karma::generate(sink,generator,container);
std::cerr << (b == true ? result : std::string("Error!")) << std::endl;
return 0;
}
in rule data (as example) I need generate int before double and make with it arithmetical operation. How can I get access to elements of synthesized attribute (tuple) in semantic actions of data rule?
The quickest solution I can come up with at this instant is simply:
data = delimit [ int_ [ _1 = at_c<1>(_val) ] << double_ [ _1 = at_c<0>(_val) ] ];
So, a full sample would look like:
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/tuple/tuple.hpp>
//-------------------------------------------------------------------------
namespace ph = boost::phoenix;
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> Sink;
typedef boost::tuple<double,int> Data;
typedef std::vector<Data> Container;
struct Generator : karma::grammar<Sink,Container()>
{
Generator(void) : Generator::base_type(start,"Generator")
{
using namespace karma;
using namespace ph;
data = delimit [ int_ [ _1 = at_c<1>(_val) ] << double_ [ _1 = at_c<0>(_val) ] ];
start = data % eol;
return;
}
karma::rule<Sink,Container()> start;
karma::rule<Sink,Data()> data;
};
//-------------------------------------------------------------------------
int main(int argc,char** argv)
{
Generator generator;
Container container;
container.push_back(Data(3.1415,100500));
container.push_back(Data(2.7183,9000));
std::string result;
Sink sink(result);
bool b = boost::spirit::karma::generate(sink,generator,container);
std::cerr << (b == true ? result : std::string("Error!")) << std::endl;
return 0;
}
Output:
100500 3.142
9000 2.718