contrek 1.3.9 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -1
- data/Gemfile.lock +1 -1
- data/PERFORMANCE.md +40 -4
- data/README.md +13 -28
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +35 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +5 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +23 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Poolable.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +3 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +5 -1
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +1 -0
- data/lib/contrek/finder/concurrent/cluster.rb +2 -0
- data/lib/contrek/finder/concurrent/finder.rb +25 -9
- data/lib/contrek/finder/concurrent/merger.rb +1 -1
- data/lib/contrek/finder/concurrent/tile.rb +5 -3
- data/lib/contrek/finder/polygon_finder.rb +2 -1
- data/lib/contrek/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eada79f09d6eede09420a2f933496d8839152f741d9c90e7d6b63fc7537bec29
|
|
4
|
+
data.tar.gz: 5652df8b513dfb26db08a1640cf59e2642a0feba7df2f9ce62eec0fe4e6dc445
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8dc280b616db1de56688ca94f5c1bcef1bf02cb81ff820638ebdf0bed821ba37745799a579a2660da6dc0934afa46a94aae51affdcb3b6fd987710a84f5f85d9
|
|
7
|
+
data.tar.gz: c815702b9abafa8b5e6de4e1f2e05ab6b03ed91248efe0786807307db6ebff19a7e2c9d002fadf94a12e2c9f41a8c07fe0ca05c01f5a838722090a737c888983
|
data/CHANGELOG.md
CHANGED
|
@@ -149,4 +149,10 @@ All notable changes to this project will be documented in this file.
|
|
|
149
149
|
- Fixed issue found on treemap determination.
|
|
150
150
|
|
|
151
151
|
## [1.3.9] - 2026-08-09
|
|
152
|
-
- Removed the curses development dependency along with the old TerminalTracker; actually Bitmaps::
|
|
152
|
+
- Removed the curses development dependency along with the old TerminalTracker; actually Contrek::Bitmaps::PngBitmap.to_window renders visually better results.
|
|
153
|
+
|
|
154
|
+
## [1.4.0] - 2026-08-13
|
|
155
|
+
- Added deterministic merge mode usefull when you need a byte-for-byte reproducible outputs across different runs (by default, the merge stage is non-deterministic to maximize performance and thread utilization).
|
|
156
|
+
|
|
157
|
+
## [1.4.1] - 2026-08-23
|
|
158
|
+
- The number_of_threads effective value is now returned inside metadata output struct too.
|
data/Gemfile.lock
CHANGED
data/PERFORMANCE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Contrek Performance Tuning
|
|
2
2
|
|
|
3
3
|
This document describes optional dependencies and configuration tips to get the best performance out of Contrek on large images.
|
|
4
4
|
|
|
@@ -41,7 +41,7 @@ sudo make install
|
|
|
41
41
|
sudo ldconfig
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
>
|
|
44
|
+
> The `-DZLIB_COMPAT=ON` flag is mandatory. Without it, zlib-ng uses a different ABI and CMake's `find_package(ZLIB)` won't detect it.
|
|
45
45
|
|
|
46
46
|
**macOS:**
|
|
47
47
|
```bash
|
|
@@ -136,9 +136,45 @@ Consider this depends your system. Probably is better not to saturate all cores
|
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
139
|
-
## 4.
|
|
139
|
+
## 4. Transparent Huge Pages (THP) — Kernel Memory Allocation
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
When processing high-resolution images, Contrek performs dense dynamic memory allocations. By default, Linux manages memory in standard 4 KiB pages, which creates heavy kernel overhead from frequent page faults and Translation Lookaside Buffer (TLB) misses under multithreaded loads.
|
|
142
|
+
|
|
143
|
+
Enabling **Transparent Huge Pages (`always`)** forces the Linux kernel to allocate memory in **2 MiB blocks**, reducing page table lookup entries by a factor of 512 and dramatically speeding up memory access.
|
|
144
|
+
|
|
145
|
+
### Temporary Activation
|
|
146
|
+
|
|
147
|
+
To enable it immediately for your current Linux session:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
echo always | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Permanent Activation
|
|
154
|
+
To ensure this setting persists across system reboots:
|
|
155
|
+
|
|
156
|
+
Open /etc/default/grub in an editor:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
sudo nano /etc/default/grub
|
|
160
|
+
```
|
|
161
|
+
Add transparent_hugepage=always to GRUB_CMDLINE_LINUX_DEFAULT:
|
|
162
|
+
|
|
163
|
+
```Plaintext
|
|
164
|
+
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash transparent_hugepage=always"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Update GRUB:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
sudo update-grub
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 5. Combining All Optimizations
|
|
176
|
+
|
|
177
|
+
Install zlib-ng and tcmalloc, enable THP, then configure:
|
|
142
178
|
|
|
143
179
|
```ruby
|
|
144
180
|
# Ruby
|
data/README.md
CHANGED
|
@@ -166,7 +166,8 @@ Metadata includes timing information (expressed in milliseconds), the number of
|
|
|
166
166
|
},
|
|
167
167
|
:groups=>2,
|
|
168
168
|
:named_sequence=>"",
|
|
169
|
-
:treemap=>[]
|
|
169
|
+
:treemap=>[],
|
|
170
|
+
:number_of_threads=>0
|
|
170
171
|
}
|
|
171
172
|
```
|
|
172
173
|
|
|
@@ -376,11 +377,9 @@ The native implementation uses the number of available CPU cores reported by the
|
|
|
376
377
|
|
|
377
378
|
## A note about determinism
|
|
378
379
|
|
|
379
|
-
|
|
380
|
+
By default, the merge stage is **non-deterministic** to maximize performance and thread utilization.
|
|
380
381
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
Each stripe is processed independently and adjacent stripes are merged as soon as they become available. Depending on thread scheduling, the merge order may differ from one execution to another.
|
|
382
|
+
Each stripe is processed independently, and adjacent stripes are merged opportunistically as soon as they become available. Depending on thread scheduling, the merge order may differ between runs.
|
|
384
383
|
|
|
385
384
|
For example, given three stripes:
|
|
386
385
|
|
|
@@ -400,7 +399,13 @@ while another execution may produce:
|
|
|
400
399
|
B1 + (B2 + B3)
|
|
401
400
|
```
|
|
402
401
|
|
|
403
|
-
Both executions generate equivalent polygons, but the order of intermediate merge operations is
|
|
402
|
+
Both executions generate equivalent polygons, but the order of intermediate merge operations is different so the final coordinate sequence is not guaranteed to be byte-for-byte identical across executions.
|
|
403
|
+
|
|
404
|
+
If you require byte-for-byte reproducible outputs across different runs, you can enable the **deterministic merge mode** (setting `deterministic: true`).
|
|
405
|
+
|
|
406
|
+
In deterministic mode, stripes are merged strictly according to a fixed binary reduction tree based on their order and index.
|
|
407
|
+
|
|
408
|
+
> **Requirement:** Deterministic mode requires an **even number of stripes/tiles** to process correctly.
|
|
404
409
|
|
|
405
410
|
## Native execution
|
|
406
411
|
|
|
@@ -596,7 +601,8 @@ Typical information includes
|
|
|
596
601
|
benchmarks:{...},
|
|
597
602
|
groups:1,
|
|
598
603
|
treemap:[],
|
|
599
|
-
options:{...}
|
|
604
|
+
options:{...},
|
|
605
|
+
number_of_threads: 4
|
|
600
606
|
}
|
|
601
607
|
```
|
|
602
608
|
|
|
@@ -713,27 +719,6 @@ Instead, it repeatedly combines smaller topologically consistent pieces into a l
|
|
|
713
719
|
This strategy makes it possible to process images whose full size would otherwise exceed the available memory.
|
|
714
720
|
|
|
715
721
|
|
|
716
|
-
## Merge order
|
|
717
|
-
|
|
718
|
-
The merge order is intentionally left to the thread scheduler.
|
|
719
|
-
|
|
720
|
-
For three stripes, both of the following execution orders are valid:
|
|
721
|
-
|
|
722
|
-
```
|
|
723
|
-
(B1 + B2) + B3
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
or
|
|
727
|
-
|
|
728
|
-
```
|
|
729
|
-
B1 + (B2 + B3)
|
|
730
|
-
```
|
|
731
|
-
|
|
732
|
-
Although the intermediate merge sequence changes, the reconstructed polygons remain geometrically equivalent.
|
|
733
|
-
|
|
734
|
-
The exact ordering of coordinates may differ because polygon simplification is applied after merging.
|
|
735
|
-
|
|
736
|
-
|
|
737
722
|
## Polygon reconstruction
|
|
738
723
|
|
|
739
724
|
Internally, polygons crossing a stripe boundary are temporarily divided into smaller pieces.
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
namespace Contrek {
|
|
23
23
|
|
|
24
|
+
enum class Versus {
|
|
25
|
+
A, // Anticlockwise
|
|
26
|
+
O // Clockwise
|
|
27
|
+
};
|
|
28
|
+
|
|
24
29
|
enum class MatchMode {
|
|
25
30
|
NOT_COLOR, // Tracks border of what is not target color
|
|
26
31
|
EXACT_COLOR // Tracks border of what exactly matchs target color
|
|
@@ -31,15 +36,22 @@ enum class Connectivity {
|
|
|
31
36
|
OMNIDIRECTIONAL = 8 // 8 directions
|
|
32
37
|
};
|
|
33
38
|
|
|
39
|
+
|
|
34
40
|
struct Config {
|
|
35
41
|
int threads = 4;
|
|
36
42
|
int tiles = 2;
|
|
43
|
+
Versus versus = Versus::A;
|
|
37
44
|
bool compress_unique = false;
|
|
38
45
|
bool compress_linear = false;
|
|
39
46
|
bool compress_raster = false;
|
|
40
47
|
bool compress_douglas_peucker = false;
|
|
41
48
|
bool compress_visvalingam = false;
|
|
49
|
+
float compress_visvalingam_tolerance = 0.0f;
|
|
42
50
|
bool treemap = false;
|
|
51
|
+
bool bounds = false;
|
|
52
|
+
bool named_sequences = false;
|
|
53
|
+
bool unsafe_mode = false;
|
|
54
|
+
bool deterministic = false;
|
|
43
55
|
int32_t target_color = -1;
|
|
44
56
|
MatchMode mode = MatchMode::NOT_COLOR;
|
|
45
57
|
Connectivity connectivity_mode = Connectivity::ORTHOGONAL;
|
|
@@ -81,19 +93,33 @@ inline TraceContext trace(const std::string& image_path, const Config& cfg = Con
|
|
|
81
93
|
ctx.matcher = std::make_unique<RGBMatcher>(color_to_match);
|
|
82
94
|
}
|
|
83
95
|
|
|
96
|
+
std::string versus_str = (cfg.versus == Versus::O) ? "o" : "a";
|
|
84
97
|
ctx.internal_args = {
|
|
85
|
-
{"versus", Identifier{
|
|
98
|
+
{"versus", Identifier{versus_str}},
|
|
99
|
+
{"number_of_tiles", static_cast<int64_t>(cfg.tiles)},
|
|
100
|
+
{"treemap", cfg.treemap},
|
|
101
|
+
{"bounds", cfg.bounds},
|
|
102
|
+
{"named_sequences", cfg.named_sequences},
|
|
103
|
+
{"unsafe_mode", cfg.unsafe_mode},
|
|
104
|
+
{"deterministic", cfg.deterministic}
|
|
86
105
|
};
|
|
87
|
-
Options compression_opts;
|
|
88
|
-
|
|
89
|
-
if (cfg.compress_unique) compression_opts["uniq"] = true;
|
|
90
|
-
if (cfg.compress_linear) compression_opts["linear"] = true;
|
|
91
|
-
if (cfg.compress_visvalingam) compression_opts["visvalingam"] = true;
|
|
92
|
-
if (cfg.compress_unique || cfg.compress_linear || cfg.compress_visvalingam) ctx.internal_args["compression"] = compression_opts;
|
|
93
|
-
ctx.internal_args["number_of_tiles"] = cfg.tiles;
|
|
94
106
|
if (cfg.connectivity_mode == Connectivity::OMNIDIRECTIONAL) {
|
|
95
|
-
ctx.internal_args["connectivity"] = 8;
|
|
107
|
+
ctx.internal_args["connectivity"] = static_cast<int64_t>(8);
|
|
96
108
|
}
|
|
109
|
+
Options compress_opts;
|
|
110
|
+
if (cfg.compress_unique) compress_opts["uniq"] = true;
|
|
111
|
+
if (cfg.compress_linear) compress_opts["linear"] = true;
|
|
112
|
+
if (cfg.compress_raster) compress_opts["raster"] = true;
|
|
113
|
+
if (cfg.compress_douglas_peucker) compress_opts["douglas_peucker"] = true;
|
|
114
|
+
if (cfg.compress_visvalingam) compress_opts["visvalingam"] = true;
|
|
115
|
+
if (cfg.compress_visvalingam_tolerance > 0.0f) {
|
|
116
|
+
compress_opts["visvalingam_tolerance"] = static_cast<double>(cfg.compress_visvalingam_tolerance);
|
|
117
|
+
}
|
|
118
|
+
if (cfg.compress_unique || cfg.compress_linear || cfg.compress_raster ||
|
|
119
|
+
cfg.compress_douglas_peucker || cfg.compress_visvalingam) {
|
|
120
|
+
ctx.internal_args["compress"] = compress_opts;
|
|
121
|
+
}
|
|
122
|
+
|
|
97
123
|
ctx.finder = std::make_unique<Finder>(cfg.threads, ctx.bitmap.get(), ctx.matcher.get(), ctx.internal_args);
|
|
98
124
|
ctx.result = std::unique_ptr<ProcessResult>(ctx.finder->process_info());
|
|
99
125
|
|
|
@@ -28,6 +28,7 @@ void FinderUtils::sanitize_options(pf_Options& options, const Options& incoming_
|
|
|
28
28
|
options.named_sequences);
|
|
29
29
|
options.bounds = incoming_options.get<bool>("bounds", options.bounds);
|
|
30
30
|
options.unsafe_mode = incoming_options.get<bool>("unsafe_mode", options.unsafe_mode);
|
|
31
|
+
options.deterministic = incoming_options.get<bool>("deterministic", options.deterministic);
|
|
31
32
|
|
|
32
33
|
if (const Options* compress = incoming_options.get_options("compress")) {
|
|
33
34
|
options.compress_uniq = compress->get<bool>("uniq", options.compress_uniq);
|
|
@@ -110,6 +110,7 @@ ProcessResult* PolygonFinder::process_info() {
|
|
|
110
110
|
pr->has_bounds = this->node_cluster->options->bounds;
|
|
111
111
|
pr->versus = this->options.versus;
|
|
112
112
|
pr->options = this->incoming_options_;
|
|
113
|
+
pr->number_of_threads = 0;
|
|
113
114
|
|
|
114
115
|
if (this->node_cluster->options->named_sequences && typeid(*this->source_bitmap) == typeid(Bitmap))
|
|
115
116
|
{ std::string sequence;
|
|
@@ -53,6 +53,7 @@ struct pf_Options {
|
|
|
53
53
|
bool named_sequences = false;
|
|
54
54
|
bool unsafe_mode = false;
|
|
55
55
|
bool bounds = false;
|
|
56
|
+
bool deterministic = false;
|
|
56
57
|
int connectivity_offset = 0;
|
|
57
58
|
float compress_visvalingam_tolerance = 10.0;
|
|
58
59
|
int number_of_tiles = 1;
|
|
@@ -68,6 +69,7 @@ struct ProcessResult {
|
|
|
68
69
|
int groups;
|
|
69
70
|
int width, height;
|
|
70
71
|
int versus;
|
|
72
|
+
int number_of_threads;
|
|
71
73
|
bool has_bounds = false;
|
|
72
74
|
std::map<std::string, double> benchmarks;
|
|
73
75
|
std::list<Polygon> polygons;
|
|
@@ -135,16 +135,17 @@ Tile* Cluster::merge_tiles() {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
double
|
|
138
|
+
Tile* first_tile = tiles_.front();
|
|
139
|
+
Tile* last_tile = tiles_.back();
|
|
140
|
+
double past_tot_outer = first_tile->benchmarks.outer + last_tile->benchmarks.outer;
|
|
141
|
+
double past_tot_inner = first_tile->benchmarks.inner + last_tile->benchmarks.inner;
|
|
141
142
|
Benchmarks b{
|
|
142
143
|
tot_outer + past_tot_outer,
|
|
143
144
|
tot_inner + past_tot_inner
|
|
144
145
|
};
|
|
145
146
|
|
|
146
147
|
Tile* tile = new Tile(
|
|
147
|
-
this->finder_,
|
|
148
|
+
this->finder_, first_tile->start_x(), last_tile->end_x(), first_tile->name() + last_tile->name(), first_tile->index() / 2, b, first_tile->order() + 1);
|
|
148
149
|
|
|
149
150
|
tile->assign_shapes(new_shapes);
|
|
150
151
|
for (const auto shape : detach_shapes) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#include <utility>
|
|
11
11
|
#include <vector>
|
|
12
12
|
#include <string>
|
|
13
|
+
#include <algorithm>
|
|
13
14
|
#include "Finder.h"
|
|
14
15
|
#include "../../bitmaps/Bitmap.h"
|
|
15
16
|
#include "../../matchers/Matcher.h"
|
|
@@ -35,6 +36,9 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, const Op
|
|
|
35
36
|
if (cw < 1.0) {
|
|
36
37
|
throw std::runtime_error("One pixel tile width minimum!");
|
|
37
38
|
}
|
|
39
|
+
if (this->options_.deterministic && (this->options_.number_of_tiles % 2 != 0)) {
|
|
40
|
+
throw std::invalid_argument("Deterministic mode requires an even number of tiles!");
|
|
41
|
+
}
|
|
38
42
|
int x = 0;
|
|
39
43
|
for (int tile_index = 0; tile_index < this->options_.number_of_tiles; tile_index++)
|
|
40
44
|
{ int tile_end_x = static_cast<int>(cw * (tile_index + 1));
|
|
@@ -65,14 +69,14 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, const Op
|
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
Tile* tile = new Tile(this, payload.tile_start_x, payload.tile_end_x,
|
|
68
|
-
std::to_string(payload.tile_index), Benchmarks {0, 0});
|
|
72
|
+
std::to_string(payload.tile_index), payload.tile_index, Benchmarks {0, 0});
|
|
69
73
|
tile->initial_process(finder);
|
|
70
74
|
tiles_.queue_push(tile);
|
|
71
75
|
});
|
|
72
76
|
|
|
73
77
|
x = tile_end_x - 1;
|
|
74
78
|
}
|
|
75
|
-
this->process_tiles();
|
|
79
|
+
this->process_tiles(this->options_.deterministic);
|
|
76
80
|
reports["init"] = cpu_timer.stop();
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -82,7 +86,7 @@ Finder::Finder(int number_of_threads, const Options& options)
|
|
|
82
86
|
reports["init"] = 0;
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
void Finder::process_tiles() {
|
|
89
|
+
void Finder::process_tiles(bool deterministic) {
|
|
86
90
|
std::vector<Tile*> arriving_tiles;
|
|
87
91
|
|
|
88
92
|
while (true) {
|
|
@@ -97,7 +101,14 @@ void Finder::process_tiles() {
|
|
|
97
101
|
arriving_tiles.begin(),
|
|
98
102
|
arriving_tiles.end(),
|
|
99
103
|
[&](Tile* t) {
|
|
100
|
-
|
|
104
|
+
bool is_adjacent = (t->start_x() == (tile->end_x() - 1)) ||
|
|
105
|
+
((t->end_x() - 1) == tile->start_x());
|
|
106
|
+
if (!is_adjacent) return false;
|
|
107
|
+
if (deterministic && !this->last_couple(tile, t)) {
|
|
108
|
+
if (tile->order() != t->order()) return false;
|
|
109
|
+
if (std::min(t->index(), tile->index()) % 2 != 0) return false;
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
101
112
|
});
|
|
102
113
|
|
|
103
114
|
if (it != arriving_tiles.end()) {
|
|
@@ -124,6 +135,13 @@ void Finder::process_tiles() {
|
|
|
124
135
|
}
|
|
125
136
|
}
|
|
126
137
|
|
|
138
|
+
bool Finder::last_couple(const Tile* tile_a, const Tile* tile_b) const {
|
|
139
|
+
bool a_is_first = tile_a->index() < tile_b->index();
|
|
140
|
+
const Tile* first = a_is_first ? tile_a : tile_b;
|
|
141
|
+
const Tile* last = a_is_first ? tile_b : tile_a;
|
|
142
|
+
return (first->start_x() == 0) && (last->end_x() == this->maximum_width());
|
|
143
|
+
}
|
|
144
|
+
|
|
127
145
|
Finder::~Finder() {
|
|
128
146
|
if (this->whole_tile) {
|
|
129
147
|
delete this->whole_tile; // last tile to be deleted (not owned by a cluster)
|
|
@@ -148,6 +166,7 @@ ProcessResult* Finder::process_info() {
|
|
|
148
166
|
pr->has_bounds = this->options_.bounds;
|
|
149
167
|
pr->versus = this->options_.versus;
|
|
150
168
|
pr->options = this->input_options;
|
|
169
|
+
pr->number_of_threads = this->number_of_threads();
|
|
151
170
|
FakeCluster fake_cluster(pr->polygons, this->options_);
|
|
152
171
|
cpu_timer.start();
|
|
153
172
|
fake_cluster.compress_coords(pr->polygons, this->options_);
|
|
@@ -40,13 +40,14 @@ class Finder : public Poolable {
|
|
|
40
40
|
std::mutex finders_mutex;
|
|
41
41
|
std::map<std::string, double> reports;
|
|
42
42
|
CpuTimer cpu_timer;
|
|
43
|
+
bool last_couple(const Tile* tile_a, const Tile* tile_b) const;
|
|
43
44
|
|
|
44
45
|
protected:
|
|
45
46
|
Queue<Tile*> tiles_;
|
|
46
47
|
int maximum_width_;
|
|
47
48
|
int height = 0;
|
|
48
49
|
Tile* whole_tile = nullptr;
|
|
49
|
-
void process_tiles();
|
|
50
|
+
void process_tiles(bool deterministic = false);
|
|
50
51
|
|
|
51
52
|
public:
|
|
52
53
|
using Poolable::Poolable;
|
|
@@ -48,7 +48,7 @@ void Merger::add_tile(ProcessResult& result)
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
int end_x = this->current_x + result.width;
|
|
51
|
-
Tile* tile = new Tile(this, this->current_x, end_x, std::to_string(tiles_.size()), Benchmarks {0, 0});
|
|
51
|
+
Tile* tile = new Tile(this, this->current_x, end_x, std::to_string(tiles_.size()), tiles_.size(), Benchmarks {0, 0});
|
|
52
52
|
tile->assign_raw_polygons(result.polygons, result.treemap);
|
|
53
53
|
tiles_.queue_push(tile);
|
|
54
54
|
|
|
@@ -20,10 +20,12 @@
|
|
|
20
20
|
class Shape;
|
|
21
21
|
class Polyline;
|
|
22
22
|
|
|
23
|
-
Tile::Tile(Finder *finder, int start_x, int end_x, std::string name, const Benchmarks& b)
|
|
23
|
+
Tile::Tile(Finder *finder, int start_x, int end_x, std::string name, int index, const Benchmarks& b, int order)
|
|
24
24
|
: finder(finder),
|
|
25
25
|
start_x_(start_x),
|
|
26
26
|
end_x_(end_x),
|
|
27
|
+
index_(index),
|
|
28
|
+
order_(order),
|
|
27
29
|
name_(name),
|
|
28
30
|
benchmarks(b) {
|
|
29
31
|
this->shapes_pool = new ShapePool();
|
|
@@ -29,11 +29,13 @@ class Tile {
|
|
|
29
29
|
Finder *finder;
|
|
30
30
|
int start_x_;
|
|
31
31
|
int end_x_;
|
|
32
|
+
int index_;
|
|
33
|
+
int order_;
|
|
32
34
|
std::string name_;
|
|
33
35
|
std::vector<Shape*> shapes_;
|
|
34
36
|
|
|
35
37
|
public:
|
|
36
|
-
Tile(Finder *finder, int start_x, int end_x, std::string name, const Benchmarks& b);
|
|
38
|
+
Tile(Finder *finder, int start_x, int end_x, std::string name, int index, const Benchmarks& b, int order = 0);
|
|
37
39
|
virtual ~Tile();
|
|
38
40
|
Tile *prev = nullptr;
|
|
39
41
|
Tile *next = nullptr;
|
|
@@ -41,6 +43,8 @@ class Tile {
|
|
|
41
43
|
Cluster *cluster = nullptr;
|
|
42
44
|
int start_x() const { return start_x_; }
|
|
43
45
|
int end_x() const { return end_x_; }
|
|
46
|
+
int index() const { return index_; }
|
|
47
|
+
int order() const { return order_; }
|
|
44
48
|
std::string name() const { return name_; }
|
|
45
49
|
const std::vector<Shape*>& shapes() const { return shapes_; }
|
|
46
50
|
std::vector<Shape*>& shapes() { return shapes_; }
|
|
@@ -232,6 +232,7 @@ class To_Ruby<ProcessResult*>
|
|
|
232
232
|
return_me[Symbol("height")] = pr->height;
|
|
233
233
|
return_me[Symbol("versus")] = Symbol(pr->versus == Node::O ? "o" : "a");
|
|
234
234
|
return_me[Symbol("options")] = options_to_ruby(pr->options);
|
|
235
|
+
return_me[Symbol("number_of_threads")] = pr->number_of_threads;
|
|
235
236
|
|
|
236
237
|
Rice::Array out;
|
|
237
238
|
for (Polygon& x : pr->polygons)
|
|
@@ -106,6 +106,8 @@ module Contrek
|
|
|
106
106
|
start_x: @tiles.first.start_x,
|
|
107
107
|
end_x: @tiles.last.end_x,
|
|
108
108
|
benchmarks: {outer: tot_outer + past_tot_outer, inner: tot_inner + past_tot_inner},
|
|
109
|
+
index: @tiles.first.index / 2,
|
|
110
|
+
order: @tiles.first.order + 1,
|
|
109
111
|
name: @tiles.first.name + @tiles.last.name
|
|
110
112
|
)
|
|
111
113
|
|
|
@@ -39,6 +39,7 @@ module Contrek
|
|
|
39
39
|
x = 0
|
|
40
40
|
current_versus = options[:versus]
|
|
41
41
|
raise "Define versus!" if current_versus.nil?
|
|
42
|
+
raise ArgumentError, "Deterministic mode requires an even number of tiles!" if options[:deterministic] && @number_of_tiles.odd?
|
|
42
43
|
|
|
43
44
|
@number_of_tiles.times do |tile_index|
|
|
44
45
|
tile_end_x = (cw * (tile_index + 1)).to_i
|
|
@@ -62,7 +63,7 @@ module Contrek
|
|
|
62
63
|
finder: self,
|
|
63
64
|
start_x: payload[:tile_start_x],
|
|
64
65
|
end_x: payload[:tile_end_x],
|
|
65
|
-
|
|
66
|
+
index: payload[:tile_index]
|
|
66
67
|
)
|
|
67
68
|
tile.initial_process!(finder)
|
|
68
69
|
@tiles << tile
|
|
@@ -70,7 +71,7 @@ module Contrek
|
|
|
70
71
|
|
|
71
72
|
x = tile_end_x - 1
|
|
72
73
|
end
|
|
73
|
-
process_tiles!(bitmap, height: bitmap.h)
|
|
74
|
+
process_tiles!(bitmap, height: bitmap.h, deterministic: @options[:deterministic])
|
|
74
75
|
end.real
|
|
75
76
|
end
|
|
76
77
|
|
|
@@ -94,7 +95,8 @@ module Contrek
|
|
|
94
95
|
width: @maximum_width,
|
|
95
96
|
height: @height,
|
|
96
97
|
versus: options[:versus],
|
|
97
|
-
options: options
|
|
98
|
+
options: options,
|
|
99
|
+
number_of_threads: @number_of_threads
|
|
98
100
|
}
|
|
99
101
|
metadata[:treemap] = @whole_tile.compute_treemap if options[:treemap]
|
|
100
102
|
Contrek::Finder::Result.new(raw_polygons, metadata)
|
|
@@ -106,7 +108,12 @@ module Contrek
|
|
|
106
108
|
|
|
107
109
|
private
|
|
108
110
|
|
|
109
|
-
def
|
|
111
|
+
def last_couple?(tile_a, tile_b)
|
|
112
|
+
ordereds = [tile_a, tile_b].sort_by(&:index)
|
|
113
|
+
ordereds.first.start_x == 0 && ordereds.last.end_x == maximum_width
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def process_tiles!(bitmap, height:, deterministic: false)
|
|
110
117
|
arriving_tiles = []
|
|
111
118
|
loop do
|
|
112
119
|
tile = @tiles.pop
|
|
@@ -114,7 +121,17 @@ module Contrek
|
|
|
114
121
|
@whole_tile = tile
|
|
115
122
|
return
|
|
116
123
|
end
|
|
117
|
-
|
|
124
|
+
twin_tile = arriving_tiles.find do |t|
|
|
125
|
+
is_adjacent = (t.start_x == (tile.end_x - 1)) || ((t.end_x - 1) == tile.start_x)
|
|
126
|
+
next false unless is_adjacent
|
|
127
|
+
if deterministic && !last_couple?(tile, t)
|
|
128
|
+
next false if tile.order != t.order
|
|
129
|
+
next false if [t.index, tile.index].min.odd?
|
|
130
|
+
end
|
|
131
|
+
true
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if twin_tile
|
|
118
135
|
cluster = Cluster.new(finder: self, height: height)
|
|
119
136
|
if twin_tile.start_x == (tile.end_x - 1)
|
|
120
137
|
cluster.add(tile)
|
|
@@ -126,13 +143,12 @@ module Contrek
|
|
|
126
143
|
enqueue!(cluster: cluster) do |payload|
|
|
127
144
|
merged_tile = payload[:cluster].merge_tiles!
|
|
128
145
|
@tiles << merged_tile
|
|
129
|
-
# usefull external access to each merged_tile
|
|
130
|
-
@block&.call(merged_tile, bitmap)
|
|
146
|
+
@block&.call(merged_tile, bitmap) # usefull external access to each merged_tile
|
|
131
147
|
end
|
|
132
148
|
arriving_tiles.delete(twin_tile)
|
|
133
|
-
|
|
149
|
+
else
|
|
150
|
+
arriving_tiles << tile
|
|
134
151
|
end
|
|
135
|
-
arriving_tiles << tile
|
|
136
152
|
end
|
|
137
153
|
end
|
|
138
154
|
end
|
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
module Contrek
|
|
4
4
|
module Concurrent
|
|
5
5
|
class Tile
|
|
6
|
-
attr_reader :start_x, :end_x, :benchmarks, :shapes, :name
|
|
6
|
+
attr_reader :start_x, :end_x, :benchmarks, :shapes, :name, :index, :order
|
|
7
7
|
attr_accessor :prev, :next, :circular_next, :cluster
|
|
8
8
|
|
|
9
|
-
def initialize(finder:, start_x:, end_x:,
|
|
9
|
+
def initialize(finder:, start_x:, end_x:, index:, name: nil, benchmarks: {}, order: 0)
|
|
10
10
|
@finder = finder
|
|
11
11
|
@start_x = start_x
|
|
12
12
|
@end_x = end_x
|
|
13
|
-
@
|
|
13
|
+
@index = index
|
|
14
|
+
@order = order
|
|
15
|
+
@name = name || index.to_s
|
|
14
16
|
@prev = nil
|
|
15
17
|
@next = nil
|
|
16
18
|
@benchmarks = {outer: 0, inner: 0}.merge(benchmarks)
|
|
@@ -54,7 +54,8 @@ module Contrek
|
|
|
54
54
|
height: @source_bitmap.h,
|
|
55
55
|
treemap: (@node_cluster.treemap if @options.has_key?(:treemap)),
|
|
56
56
|
options: @ori_options,
|
|
57
|
-
versus: @options[:versus]
|
|
57
|
+
versus: @options[:versus],
|
|
58
|
+
number_of_threads: 0
|
|
58
59
|
}
|
|
59
60
|
Result.new(@node_cluster.polygons, metadata)
|
|
60
61
|
end
|
data/lib/contrek/version.rb
CHANGED