contrek 1.4.0 → 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 +4 -1
- data/Gemfile.lock +1 -1
- data/PERFORMANCE.md +40 -4
- data/README.md +4 -23
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +35 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +1 -0
- 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 -3
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +1 -0
- data/lib/contrek/finder/concurrent/finder.rb +2 -1
- 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,7 +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
153
|
|
|
154
154
|
## [1.4.0] - 2026-08-13
|
|
155
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
|
|
|
@@ -600,7 +601,8 @@ Typical information includes
|
|
|
600
601
|
benchmarks:{...},
|
|
601
602
|
groups:1,
|
|
602
603
|
treemap:[],
|
|
603
|
-
options:{...}
|
|
604
|
+
options:{...},
|
|
605
|
+
number_of_threads: 4
|
|
604
606
|
}
|
|
605
607
|
```
|
|
606
608
|
|
|
@@ -717,27 +719,6 @@ Instead, it repeatedly combines smaller topologically consistent pieces into a l
|
|
|
717
719
|
This strategy makes it possible to process images whose full size would otherwise exceed the available memory.
|
|
718
720
|
|
|
719
721
|
|
|
720
|
-
## Merge order
|
|
721
|
-
|
|
722
|
-
The merge order is intentionally left to the thread scheduler.
|
|
723
|
-
|
|
724
|
-
For three stripes, both of the following execution orders are valid:
|
|
725
|
-
|
|
726
|
-
```
|
|
727
|
-
(B1 + B2) + B3
|
|
728
|
-
```
|
|
729
|
-
|
|
730
|
-
or
|
|
731
|
-
|
|
732
|
-
```
|
|
733
|
-
B1 + (B2 + B3)
|
|
734
|
-
```
|
|
735
|
-
|
|
736
|
-
Although the intermediate merge sequence changes, the reconstructed polygons remain geometrically equivalent.
|
|
737
|
-
|
|
738
|
-
The exact ordering of coordinates may differ because polygon simplification is applied after merging.
|
|
739
|
-
|
|
740
|
-
|
|
741
722
|
## Polygon reconstruction
|
|
742
723
|
|
|
743
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
|
|
|
@@ -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;
|
|
@@ -166,6 +166,7 @@ ProcessResult* Finder::process_info() {
|
|
|
166
166
|
pr->has_bounds = this->options_.bounds;
|
|
167
167
|
pr->versus = this->options_.versus;
|
|
168
168
|
pr->options = this->input_options;
|
|
169
|
+
pr->number_of_threads = this->number_of_threads();
|
|
169
170
|
FakeCluster fake_cluster(pr->polygons, this->options_);
|
|
170
171
|
cpu_timer.start();
|
|
171
172
|
fake_cluster.compress_coords(pr->polygons, this->options_);
|
|
@@ -24,10 +24,10 @@ Tile::Tile(Finder *finder, int start_x, int end_x, std::string name, int index,
|
|
|
24
24
|
: finder(finder),
|
|
25
25
|
start_x_(start_x),
|
|
26
26
|
end_x_(end_x),
|
|
27
|
-
name_(name),
|
|
28
27
|
index_(index),
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
order_(order),
|
|
29
|
+
name_(name),
|
|
30
|
+
benchmarks(b) {
|
|
31
31
|
this->shapes_pool = new ShapePool();
|
|
32
32
|
this->shapes_pool->set_owner(this);
|
|
33
33
|
this->shapes_pools.push_back(this->shapes_pool);
|
|
@@ -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)
|
|
@@ -95,7 +95,8 @@ module Contrek
|
|
|
95
95
|
width: @maximum_width,
|
|
96
96
|
height: @height,
|
|
97
97
|
versus: options[:versus],
|
|
98
|
-
options: options
|
|
98
|
+
options: options,
|
|
99
|
+
number_of_threads: @number_of_threads
|
|
99
100
|
}
|
|
100
101
|
metadata[:treemap] = @whole_tile.compute_treemap if options[:treemap]
|
|
101
102
|
Contrek::Finder::Result.new(raw_polygons, metadata)
|
|
@@ -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