Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Enhance performance tests and add insertion_and_removal #53

Merged
merged 5 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 142 additions & 99 deletions benchmark/comprehensive_benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:ordered_set/comparing.dart';
import 'package:ordered_set/ordered_set.dart';

const _maxOperations = 2500;
const _maxOperations = 1000;
const _maxElement = 10000;
const _startingSetSize = 500;
const _startingSetSize = 250;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just to put it in line with the time of the other tests. with the new impl it is totally fine to go even above these numbers


class ComprehensiveBenchmark extends BenchmarkBase {
final Random r;
Expand Down Expand Up @@ -77,13 +77,13 @@ class _Runtime {

while (_queue.isNotEmpty) {
final op = _queue.removeAt(0);
op.execute(this, _set).forEach(_queueOp);
op.$1.execute(op, this, _set).forEach(_queueOp);
}
}

void _populateSet() {
for (var i = 0; i < _startingSetSize; i++) {
_queueOp(_AddOperation(_randomElement()));
_queueOp((_OperationType.add, _randomElement()));
}
}

Expand All @@ -94,167 +94,210 @@ class _Runtime {

_Operation _randomOperation() {
final type = _OperationType.values[r.nextInt(_OperationType.values.length)];
const noop = (_OperationType.noop, 0);
switch (type) {
case _OperationType.add:
return _AddOperation(_randomElement());
case _OperationType.removeIdx:
case _NoOp():
return noop;
case _AddOp():
return (type, _randomElement());
case _RemoveIdxOp():
if (_set.isEmpty) {
return _AddOperation(_randomElement());
return noop;
}
return _RemoveIdxOperation(r.nextInt(_set.length));
case _OperationType.removeElement:
return (type, r.nextInt(_set.length));
case _RemoveElementOp():
if (_set.isEmpty) {
return _AddOperation(_randomElement());
return noop;
}
return _RemoveElementOperation(_set.elementAt(r.nextInt(_set.length)));
case _OperationType.removeWhere:
return _RemoveWhereOperation(_randomElement());
case _OperationType.visit:
return _VisitOperation(_randomElement());
case _OperationType.iterateThenAdd:
return _IterateThenAddOperation(_randomElement());
case _OperationType.iterateThenRemove:
return _IterateThenRemoveOperation(_randomElement());
return (type, _set.elementAt(r.nextInt(_set.length)));
case _RemoveWhereOp():
return (type, _randomElement());
case _VisitOp():
return (type, _randomElement());
case _IterateThenAddOp():
return (type, _randomElement());
case _IterateThenRemoveOp():
return (type, _randomElement());
}
}

int _randomElement() => r.nextInt(_maxElement) + 1;
}

enum _OperationType {
// when queued, generates a random element; then adds using `add`
add,
// when queued, selects a random index; then removes using `removeAt`
removeIdx,
// when queued, selects a random element; then removes using `remove`
removeElement,
// when queued, generates a random factor; then removes all elements with
// that factor using `removeWhere`
removeWhere,
// when queued, generates a random factor; then finds the elements matching
// that factor, using normal for iteration
visit,
// when queued, generates two random factors; iterates over the set,
//finds elements that match the first factor, then multiplies them by
//the second factor, queue adding the results with the `add` operation
iterateThenAdd,
// when queued, generates a random factor; iterates over the set, finding
// elements that match the factor, then queue their removal with
// the `removeElement` operation
iterateThenRemove,
sealed class _OperationType {
static const noop = _NoOp();
static const add = _AddOp();
static const removeIdx = _RemoveIdxOp();
static const removeElement = _RemoveElementOp();
static const removeWhere = _RemoveWhereOp();
static const visit = _VisitOp();
static const iterateThenAdd = _IterateThenAddOp();
static const iterateThenRemove = _IterateThenRemoveOp();

static const values = [
noop,
add,
removeIdx,
removeElement,
removeWhere,
visit,
iterateThenAdd,
iterateThenRemove,
];

const _OperationType();

List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
);
}

abstract class _Operation {
final _OperationType type;
/// Just placeholder to return an operation when none is desired.
class _NoOp extends _OperationType {
const _NoOp();

const _Operation(this.type);

List<_Operation> execute(_Runtime runtime, OrderedSet<int> set);
@override
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
return [];
}
}

class _AddOperation extends _Operation {
final int element;

_AddOperation(this.element) : super(_OperationType.add);
/// When queued, generates a random element; then adds using `add`.
class _AddOp extends _OperationType {
const _AddOp();

@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
set.add(element);
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
set.add(operation.$2);
return [];
}
}

class _RemoveIdxOperation extends _Operation {
final int index;

_RemoveIdxOperation(this.index) : super(_OperationType.removeIdx);
/// When queued, selects a random index; then removes using `removeAt`.
class _RemoveIdxOp extends _OperationType {
const _RemoveIdxOp();

@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
if (index < set.length) {
set.removeAt(index);
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
if (set.isEmpty) {
return [];
}
set.removeAt(operation.$2);
return [];
}
}

class _RemoveElementOperation extends _Operation {
final int element;

_RemoveElementOperation(this.element) : super(_OperationType.removeElement);
/// When queued, selects a random element; then removes using `remove`.
class _RemoveElementOp extends _OperationType {
const _RemoveElementOp();

@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
set.remove(element);
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
set.remove(operation.$2);
return [];
}
}

class _RemoveWhereOperation extends _Operation {
final int factor;

_RemoveWhereOperation(this.factor) : super(_OperationType.removeWhere);
/// When queued, generates a random factor; then removes all elements with
/// that factor using `removeWhere`.
class _RemoveWhereOp extends _OperationType {
const _RemoveWhereOp();

@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
set.removeWhere((e) => e % factor == 0);
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
set.removeWhere((e) => e % operation.$2 == 0);
return [];
}
}

class _VisitOperation extends _Operation {
final int factor;

_VisitOperation(this.factor) : super(_OperationType.visit);
/// When queued, generates a random factor; then finds the elements matching
/// that factor, using normal for iteration.
class _VisitOp extends _OperationType {
const _VisitOp();

@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
final output = <_Operation>[];
for (final e in set) {
if (e % factor == 0) {
output.add(_AddOperation(e * factor));
if (e % operation.$2 == 0) {
output.add((_OperationType.add, e * operation.$2));
}
}
return output;
}
}

class _IterateThenAddOperation extends _Operation {
final int factor;

_IterateThenAddOperation(this.factor) : super(_OperationType.iterateThenAdd);

/// When queued, generates two random factors; iterates over the set,
/// finds elements that match the first factor, then multiplies them by
/// the second factor, queue adding the results with the `add` operation
class _IterateThenAddOp extends _OperationType {
const _IterateThenAddOp();
@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
final toAdd = <int>[];
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
final output = <_Operation>[];
for (final e in set) {
if (e % factor == 0) {
toAdd.add(e);
if (e % operation.$2 == 0) {
output.add((_OperationType.add, e * operation.$2));
}
}

return toAdd.map(_AddOperation.new).toList();
return output;
}
}

class _IterateThenRemoveOperation extends _Operation {
final int factor;

_IterateThenRemoveOperation(this.factor)
: super(_OperationType.iterateThenRemove);
/// When queued, generates a random factor; iterates over the set, finding
/// elements that match the factor, then queue their removal with
/// the `removeElement` operation.
class _IterateThenRemoveOp extends _OperationType {
const _IterateThenRemoveOp();

@override
List<_Operation> execute(_Runtime runtime, OrderedSet<int> set) {
final toRemove = <int>[];
List<_Operation> execute(
_Operation operation,
_Runtime runtime,
OrderedSet<int> set,
) {
final output = <_Operation>[];
for (final e in set) {
if (e % factor == 0) {
toRemove.add(e);
if (e % operation.$2 == 0) {
output.add((_OperationType.removeElement, e));
}
}
return toRemove.map(_RemoveElementOperation.new).toList();
return output;
}
}

typedef _Operation = (_OperationType, int);

int _countFactors(int initialValue, int factor) {
var count = 0;
var value = initialValue;
Expand Down
36 changes: 36 additions & 0 deletions benchmark/insertion_and_removal_benchmark.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:ordered_set/ordered_set.dart';

import '../test/comparable_object.dart';

const _iterationAmount = 1000;

class InsertionAndRemovalBenchmark extends BenchmarkBase {
late final OrderedSet<ComparableObject> set;
late final Map<int, ComparableObject> objects;

InsertionAndRemovalBenchmark() : super('Insertion and Removal Benchmark');

static void main() {
InsertionAndRemovalBenchmark().report();
}

@override
void setup() {
set = OrderedSet<ComparableObject>();
objects = {
for (var i = 0; i < _iterationAmount; i++) i: ComparableObject(i, '$i'),
};
}

@override
void exercise() {
for (var i = 0; i < _iterationAmount; i++) {
set.add(objects[i]!);
}

for (var i = 0; i < _iterationAmount; i++) {
set.remove(objects[i]!);
}
}
}
4 changes: 3 additions & 1 deletion benchmark/iteration_benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'package:ordered_set/ordered_set.dart';

import '../test/comparable_object.dart';

const _iterationAmount = 1000;

class IterationBenchmark extends BenchmarkBase {
late final OrderedSet<ComparableObject> set;

Expand All @@ -17,7 +19,7 @@ class IterationBenchmark extends BenchmarkBase {
@override
void setup() {
set = OrderedSet();
for (var i = 0; i < 1000; i++) {
for (var i = 0; i < _iterationAmount; i++) {
final l = (10 + sqrt(i)).floor();
for (var j = 0; j <= l; j++) {
set.add(ComparableObject(i, '$i-$j'));
Expand Down
2 changes: 2 additions & 0 deletions benchmark/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'comprehensive_benchmark.dart';
import 'insertion_and_removal_benchmark.dart';
import 'iteration_benchmark.dart';

void main() {
IterationBenchmark.main();
InsertionAndRemovalBenchmark.main();
ComprehensiveBenchmark.main();
}
Loading