Skip to content
This repository was archived by the owner on May 31, 2025. It is now read-only.

Commit 4f42dbf

Browse files
Tyler Etzeltyleretzel
authored andcommitted
single threaded test framework
- mixer selects which producers to run - two different producers with different allocation patterns
1 parent 22feec4 commit 4f42dbf

File tree

6 files changed

+238
-0
lines changed

6 files changed

+238
-0
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ find_package(Threads)
66
add_executable(memsetVsMadvise memsetVsMadvise.cpp)
77
set_property(TARGET memsetVsMadvise PROPERTY CXX_STANDARD 11)
88
target_link_libraries(memsetVsMadvise PRIVATE ${CMAKE_THREAD_LIBS_INIT} gflags)
9+
10+
add_executable(stress stress_test/Main.cpp stress_test/Producers.cpp stress_test/Mixer.cpp)
11+
set_property(TARGET stress PROPERTY CXX_STANDARD 14)
12+
target_link_libraries(stress PRIVATE gflags)

stress_test/Main.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <chrono>
2+
#include <iostream>
3+
#include <vector>
4+
5+
#include <gflags/gflags.h>
6+
7+
#include "Mixer.h"
8+
9+
DEFINE_int32(num_producers, 100, "number of producers to run");
10+
11+
int main(int argc, char **argv) {
12+
13+
gflags::ParseCommandLineFlags(&argc, &argv, true);
14+
15+
vector<std::unique_ptr<Producer>> producers;
16+
producers.push_back(std::move(std::make_unique<SimpleProducer>(8, 100000)));
17+
producers.push_back(std::move(std::make_unique<VectorProducer>(
18+
100000, std::chrono::duration<double>(1.0))));
19+
20+
using namespace std::chrono;
21+
22+
Mixer m(std::move(producers), FLAGS_num_producers);
23+
24+
high_resolution_clock::time_point beginTime = high_resolution_clock::now();
25+
m.run();
26+
high_resolution_clock::time_point endTime = high_resolution_clock::now();
27+
28+
duration<double> span = duration_cast<duration<double>>(endTime - beginTime);
29+
std::cout << "Elapsed time: " << span.count() << std::endl;
30+
}

stress_test/Mixer.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "Mixer.h"
2+
3+
Mixer::Mixer(vector<unique_ptr<Producer>> producers, int numProducers)
4+
: producers_(move(producers)), producersRemaining_(numProducers),
5+
producerIndexPicker_(0, this->producers_.size() - 1) {}
6+
7+
// Picks next producer for the mixer to run. Currently uniform random choice
8+
const Producer &Mixer::pick() {
9+
int producerIndex = this->producerIndexPicker_(this->generator_);
10+
return *(this->producers_[producerIndex]);
11+
}
12+
13+
void Mixer::run() {
14+
while (this->producersRemaining_ > 0) {
15+
if (this->allocated_.size() > 0 &&
16+
this->allocated_.back().freeAfter() <
17+
std::chrono::high_resolution_clock::now()) {
18+
// deallocate something if it's lifetime has expired
19+
this->allocated_.pop_back();
20+
} else {
21+
// otherwise run a random producer
22+
Allocation a = this->pick().run();
23+
if (!a.isEmpty()) {
24+
this->allocated_.push_back(std::move(a));
25+
sort(rbegin(this->allocated_), rend(this->allocated_));
26+
}
27+
producersRemaining_--;
28+
}
29+
}
30+
// cleanup remaining allocated things immediately, regardless of lifetime
31+
while (!this->allocated_.empty()) {
32+
this->allocated_.pop_back();
33+
}
34+
}

stress_test/Mixer.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <random>
4+
#include <vector>
5+
6+
#include "Producers.h"
7+
8+
using std::unique_ptr;
9+
using std::vector;
10+
11+
class Mixer {
12+
public:
13+
void run();
14+
Mixer(vector<unique_ptr<Producer>> producers, int numProducers);
15+
16+
private:
17+
/* maintains reverse-sorted order by lifetime; i.e. [push_back] yields the
18+
* allocation that should be deallocated the soonest */
19+
vector<Allocation> allocated_;
20+
vector<unique_ptr<Producer>> producers_;
21+
int producersRemaining_;
22+
const Producer &pick();
23+
std::uniform_int_distribution<int> producerIndexPicker_;
24+
std::default_random_engine generator_;
25+
};

stress_test/Producers.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include "Producers.h"
2+
3+
#include <iostream>
4+
5+
// Allocation
6+
7+
bool Allocation::operator<(const Allocation &that) const {
8+
return this->toFree_ < that.toFree_;
9+
}
10+
11+
bool Allocation::isEmpty() const { return this->toFree_.size() == 0; }
12+
13+
std::chrono::high_resolution_clock::time_point Allocation::freeAfter() const {
14+
return this->freeAfter_;
15+
}
16+
17+
Allocation::Allocation(std::vector<void *> toFree,
18+
std::chrono::high_resolution_clock::time_point freeAfter)
19+
: toFree_(toFree), freeAfter_(freeAfter) {}
20+
21+
Allocation::~Allocation() {
22+
for (auto it = begin(this->toFree_); it != end(this->toFree_); ++it) {
23+
free(*it);
24+
}
25+
}
26+
27+
// Simple Producer
28+
29+
SimpleProducer::SimpleProducer(int allocSize, int numAllocs)
30+
: allocSize_(allocSize), numAllocs_(numAllocs) {}
31+
32+
Allocation SimpleProducer::run() const {
33+
for (int i = 0; i < this->numAllocs_; i++) {
34+
char *ptr = (char *)calloc(this->allocSize_, sizeof(char));
35+
if (ptr == NULL) {
36+
std::cout << "allocation failed" << std::endl;
37+
}
38+
free(ptr);
39+
}
40+
return std::move(Allocation());
41+
}
42+
43+
void swap(Allocation &a1, Allocation &a2) {
44+
a1.toFree_.swap(a2.toFree_);
45+
std::swap(a1.freeAfter_, a2.freeAfter_);
46+
}
47+
48+
// Vector Producer
49+
50+
VectorProducer::VectorProducer(int vectorSize,
51+
std::chrono::duration<double> lifetime)
52+
: vectorSize_(vectorSize), lifetime_(lifetime), shouldFree_(true) {}
53+
54+
VectorProducer::VectorProducer(int vectorSize)
55+
: vectorSize_(vectorSize), lifetime_(0.0), shouldFree_(true) {}
56+
57+
Allocation VectorProducer::run() const {
58+
void *ptr = malloc(1);
59+
size_t currSize = 1;
60+
while (currSize < this->vectorSize_) {
61+
free(ptr);
62+
currSize *= 2;
63+
ptr = malloc(currSize);
64+
}
65+
if (this->shouldFree_) {
66+
free(ptr);
67+
return Allocation();
68+
} else {
69+
70+
using namespace std::chrono;
71+
high_resolution_clock::time_point t = high_resolution_clock::now();
72+
high_resolution_clock::duration d =
73+
duration_cast<high_resolution_clock::duration>(this->lifetime_);
74+
t += d;
75+
return std::move(Allocation(std::vector<void *>({ptr}), t));
76+
}
77+
}

stress_test/Producers.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#pragma once
2+
3+
#include <chrono>
4+
#include <vector>
5+
6+
class Allocation {
7+
public:
8+
// sorts based on [freeAfter] field
9+
bool operator<(const Allocation &that) const;
10+
// true iff [this->toFree_] is empty
11+
bool isEmpty() const;
12+
std::chrono::high_resolution_clock::time_point freeAfter() const;
13+
Allocation(std::vector<void *> toFree,
14+
std::chrono::high_resolution_clock::time_point freeAfter);
15+
// makes an allocation such that [isEmpty()] is true
16+
Allocation() = default;
17+
18+
// Disable copy constructor: whoever owns the Allocation should deallocate it
19+
Allocation(Allocation const &) = delete;
20+
Allocation &operator=(Allocation const &) = delete;
21+
22+
// must define a move constructor since we deleted the copy constructor
23+
Allocation(Allocation&&) = default;
24+
Allocation& operator=(Allocation&&) = default;
25+
26+
// The destructor deallocates the memory in [toFree_]
27+
~Allocation();
28+
29+
// needed to sort
30+
friend void swap(Allocation &a1, Allocation &a2);
31+
32+
private:
33+
std::vector<void *> toFree_;
34+
// absolute time after which this should be freed
35+
std::chrono::high_resolution_clock::time_point freeAfter_;
36+
};
37+
38+
class Producer {
39+
public:
40+
virtual Allocation run() const = 0;
41+
};
42+
43+
// allocates a vector of size [sz] and then frees it
44+
class VectorProducer : public Producer {
45+
public:
46+
Allocation run() const;
47+
// allocate, and then free after [lifetime] has elapsed
48+
VectorProducer(int vectorSize, std::chrono::duration<double> lifetime);
49+
// allocate and then free immediately
50+
VectorProducer(int vectorSize);
51+
52+
private:
53+
int vectorSize_;
54+
std::chrono::duration<double> lifetime_;
55+
bool shouldFree_;
56+
};
57+
58+
/* allocates a block of size [alloc_sz], and then immediately frees it. Repeats
59+
* this [n_allocs] times. */
60+
class SimpleProducer : public Producer {
61+
public:
62+
Allocation run() const;
63+
SimpleProducer(int allocSize, int numAllocs);
64+
65+
private:
66+
int allocSize_;
67+
int numAllocs_;
68+
};

0 commit comments

Comments
 (0)