| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Algorithm::Classifier::IsolationForest::App::Command::accel; |
|
2
|
|
|
|
|
|
|
|
|
3
|
22
|
|
|
22
|
|
270075
|
use strict; |
|
|
22
|
|
|
|
|
39
|
|
|
|
22
|
|
|
|
|
762
|
|
|
4
|
22
|
|
|
22
|
|
84
|
use warnings; |
|
|
22
|
|
|
|
|
40
|
|
|
|
22
|
|
|
|
|
1127
|
|
|
5
|
22
|
|
|
22
|
|
19708
|
use Algorithm::Classifier::IsolationForest (); |
|
|
22
|
|
|
|
|
107
|
|
|
|
22
|
|
|
|
|
816
|
|
|
6
|
22
|
|
|
22
|
|
142
|
use Algorithm::Classifier::IsolationForest::App -command; |
|
|
22
|
|
|
|
|
48
|
|
|
|
22
|
|
|
|
|
318
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
1
|
25279
|
sub opt_spec { () } |
|
9
|
|
|
|
|
|
|
|
|
10
|
0
|
|
|
0
|
1
|
0
|
sub abstract { 'Report which (if any) native acceleration backend is active' } |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub description { |
|
13
|
0
|
|
|
0
|
1
|
0
|
'Initialises Algorithm::Classifier::IsolationForest, fits |
|
14
|
|
|
|
|
|
|
a tiny synthetic dataset to exercise the optional native code path, then |
|
15
|
|
|
|
|
|
|
reports which acceleration (if any) is wired up: |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
* Inline::C -- C scoring backend compiled at module load |
|
18
|
|
|
|
|
|
|
* OpenMP -- parallel tree-walk across CPU cores (requires libgomp) |
|
19
|
|
|
|
|
|
|
* SIMD -- vectorised oblique dot product in extended mode |
|
20
|
|
|
|
|
|
|
(gated on OpenMP 4.0+; relies on `#pragma omp simd`) |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
The detection happens automatically the first time the module is loaded. |
|
23
|
|
|
|
|
|
|
When the distribution was installed with Inline::C available, a prebuilt |
|
24
|
|
|
|
|
|
|
object compiled at `make` time is loaded directly; otherwise the backend |
|
25
|
|
|
|
|
|
|
is compiled on first load and cached under _Inline/. If no backend is |
|
26
|
|
|
|
|
|
|
active the module falls back to a pure-Perl implementation. |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Build flags are tunable via environment variables set before first load |
|
29
|
|
|
|
|
|
|
(and, to pick what gets baked into the prebuilt object, before |
|
30
|
|
|
|
|
|
|
`perl Makefile.PL`): |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
* IF_ARCH= -- -march= (e.g. x86-64-v3, skylake, znver3) |
|
33
|
|
|
|
|
|
|
* IF_NATIVE=1 -- shorthand for IF_ARCH=native; ignored if IF_ARCH is set |
|
34
|
|
|
|
|
|
|
* IF_OPT=<-Olevel> -- override the default -O3 |
|
35
|
|
|
|
|
|
|
* IF_NO_OPENMP=1 -- serial C backend: no libgomp linkage at all |
|
36
|
|
|
|
|
|
|
* IF_RUNTIME_BUILD=1 -- ignore the prebuilt object, compile at first load |
|
37
|
|
|
|
|
|
|
* IF_NO_C=1 -- skip the C backend entirely |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
See "NATIVE ACCELERATION" in perldoc Algorithm::Classifier::IsolationForest |
|
40
|
|
|
|
|
|
|
for details and tradeoffs (in particular, why IF_NATIVE is not always a |
|
41
|
|
|
|
|
|
|
safe default choice). |
|
42
|
|
|
|
|
|
|
'; |
|
43
|
|
|
|
|
|
|
} ## end sub description |
|
44
|
|
|
|
|
|
|
|
|
45
|
1
|
|
|
1
|
0
|
3
|
sub validate { 1 } |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub execute { |
|
48
|
1
|
|
|
1
|
1
|
6
|
my ( $self, $opt, $args ) = @_; |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Tiny deterministic dataset. Fitting + scoring confirms the chosen |
|
51
|
|
|
|
|
|
|
# backend is callable end-to-end, not merely that it compiled. We |
|
52
|
|
|
|
|
|
|
# exercise both axis mode (covers score_all_xs's axis branch) and |
|
53
|
|
|
|
|
|
|
# extended mode (covers the oblique branch -- where the |
|
54
|
|
|
|
|
|
|
# `#pragma omp simd` reduction lives, so this is the only path |
|
55
|
|
|
|
|
|
|
# SIMD actually matters for). |
|
56
|
1
|
|
|
|
|
2
|
srand(1); |
|
57
|
1
|
|
|
|
|
4
|
my @data = map { [ rand(), rand(), rand() ] } 1 .. 30; |
|
|
30
|
|
|
|
|
47
|
|
|
58
|
1
|
|
|
|
|
4
|
push @data, [ 10, 10, 10 ], [ -10, -10, -10 ]; |
|
59
|
|
|
|
|
|
|
|
|
60
|
1
|
|
|
|
|
9
|
my $axis = Algorithm::Classifier::IsolationForest->new( |
|
61
|
|
|
|
|
|
|
n_trees => 10, |
|
62
|
|
|
|
|
|
|
sample_size => 32, |
|
63
|
|
|
|
|
|
|
seed => 1, |
|
64
|
|
|
|
|
|
|
); |
|
65
|
1
|
|
|
|
|
5
|
$axis->fit( \@data ); |
|
66
|
1
|
|
|
|
|
5
|
$axis->score_samples( [ [ 0.5, 0.5, 0.5 ] ] ); |
|
67
|
|
|
|
|
|
|
|
|
68
|
1
|
|
|
|
|
17
|
my $ext = Algorithm::Classifier::IsolationForest->new( |
|
69
|
|
|
|
|
|
|
n_trees => 10, |
|
70
|
|
|
|
|
|
|
sample_size => 32, |
|
71
|
|
|
|
|
|
|
seed => 1, |
|
72
|
|
|
|
|
|
|
mode => 'extended', |
|
73
|
|
|
|
|
|
|
); |
|
74
|
1
|
|
|
|
|
4
|
$ext->fit( \@data ); |
|
75
|
1
|
|
|
|
|
5
|
$ext->score_samples( [ [ 0.5, 0.5, 0.5 ] ] ); |
|
76
|
|
|
|
|
|
|
|
|
77
|
1
|
50
|
|
|
|
5
|
my $has_c = $Algorithm::Classifier::IsolationForest::HAS_C ? 1 : 0; |
|
78
|
1
|
50
|
|
|
|
2
|
my $has_openmp = $Algorithm::Classifier::IsolationForest::HAS_OPENMP ? 1 : 0; |
|
79
|
1
|
50
|
|
|
|
3
|
my $has_simd = $Algorithm::Classifier::IsolationForest::HAS_SIMD ? 1 : 0; |
|
80
|
|
|
|
|
|
|
|
|
81
|
1
|
|
|
|
|
3
|
my $c_source = $Algorithm::Classifier::IsolationForest::C_SOURCE; |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Whether a prebuilt object was installed with the dist at all -- |
|
84
|
|
|
|
|
|
|
# independent of whether this process ended up using it. |
|
85
|
1
|
|
|
|
|
2
|
my $prebuilt_installed = 0; |
|
86
|
|
|
|
|
|
|
{ |
|
87
|
1
|
|
|
|
|
1
|
local $@; |
|
|
1
|
|
|
|
|
2
|
|
|
88
|
1
|
|
|
|
|
2
|
my $bf = eval { |
|
89
|
1
|
|
|
|
|
8
|
require Algorithm::Classifier::IsolationForest::BuildFlags; |
|
90
|
1
|
|
|
|
|
5
|
Algorithm::Classifier::IsolationForest::BuildFlags::flags(); |
|
91
|
|
|
|
|
|
|
}; |
|
92
|
1
|
50
|
33
|
|
|
10
|
$prebuilt_installed = 1 if ref $bf eq 'HASH' && $bf->{prebuilt}; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
1
|
50
|
|
|
|
4
|
my $source_desc |
|
|
|
50
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
= $c_source eq 'prebuilt' ? 'prebuilt at install time' |
|
97
|
|
|
|
|
|
|
: $c_source eq 'runtime' ? 'compiled at run time (cached under _Inline/)' |
|
98
|
|
|
|
|
|
|
: 'none (pure-Perl fallback)'; |
|
99
|
|
|
|
|
|
|
|
|
100
|
1
|
|
|
|
|
15
|
print "Algorithm::Classifier::IsolationForest acceleration status\n"; |
|
101
|
1
|
50
|
|
|
|
3
|
print " Inline::C : ", ( $has_c ? "available\n" : "not available\n" ); |
|
102
|
1
|
50
|
|
|
|
3
|
print " OpenMP : ", ( $has_openmp ? "available\n" : "not available\n" ); |
|
103
|
1
|
50
|
|
|
|
2
|
print " SIMD : ", ( $has_simd ? "available\n" : "not available\n" ); |
|
104
|
1
|
|
|
|
|
3
|
print " C object : $source_desc\n"; |
|
105
|
1
|
50
|
33
|
|
|
9
|
if ( $c_source ne 'prebuilt' && $prebuilt_installed ) { |
|
106
|
0
|
|
|
|
|
0
|
my @why; |
|
107
|
0
|
0
|
|
|
|
0
|
push @why, 'IF_RUNTIME_BUILD' if $ENV{IF_RUNTIME_BUILD}; |
|
108
|
0
|
0
|
|
|
|
0
|
push @why, 'IF_NO_C' if $ENV{IF_NO_C}; |
|
109
|
0
|
|
|
|
|
0
|
push @why, grep { defined $ENV{$_} } qw(IF_OPT IF_ARCH IF_NATIVE IF_NO_OPENMP); |
|
|
0
|
|
|
|
|
0
|
|
|
110
|
0
|
0
|
|
|
|
0
|
print " (a prebuilt object is installed but was not used", |
|
111
|
|
|
|
|
|
|
( |
|
112
|
|
|
|
|
|
|
@why |
|
113
|
|
|
|
|
|
|
? '; runtime overrides active: ' . join( ', ', @why ) |
|
114
|
|
|
|
|
|
|
: '; it failed to load' |
|
115
|
|
|
|
|
|
|
), |
|
116
|
|
|
|
|
|
|
")\n"; |
|
117
|
|
|
|
|
|
|
} ## end if ( $c_source ne 'prebuilt' && $prebuilt_installed) |
|
118
|
1
|
50
|
|
|
|
18
|
if ($has_c) { |
|
119
|
1
|
|
|
|
|
5
|
printf " Build flags: %s\n", $Algorithm::Classifier::IsolationForest::OPT_LEVEL; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
1
|
|
|
|
|
2
|
print "\n"; |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Build a one-line backend summary that lists every active feature |
|
124
|
|
|
|
|
|
|
# and where the C object came from. |
|
125
|
1
|
|
|
|
|
2
|
my @features; |
|
126
|
1
|
50
|
|
|
|
3
|
push @features, 'OpenMP' if $has_openmp; |
|
127
|
1
|
50
|
|
|
|
4
|
push @features, 'SIMD' if $has_simd; |
|
128
|
|
|
|
|
|
|
|
|
129
|
1
|
50
|
|
|
|
4
|
my $suffix |
|
|
|
50
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
= $c_source eq 'prebuilt' ? ' -- prebuilt at install time' |
|
131
|
|
|
|
|
|
|
: $c_source eq 'runtime' ? ' -- compiled at run time' |
|
132
|
|
|
|
|
|
|
: ''; |
|
133
|
|
|
|
|
|
|
|
|
134
|
1
|
50
|
33
|
|
|
9
|
if ( $has_c && @features ) { |
|
|
|
0
|
|
|
|
|
|
|
135
|
1
|
|
|
|
|
22
|
printf "Active backend: Inline::C with %s%s\n", join( ' + ', @features ), $suffix; |
|
136
|
|
|
|
|
|
|
} elsif ($has_c) { |
|
137
|
0
|
|
|
|
|
0
|
print "Active backend: Inline::C (serial, scalar)$suffix\n"; |
|
138
|
|
|
|
|
|
|
} else { |
|
139
|
0
|
|
|
|
|
0
|
print "Active backend: pure Perl (no native acceleration)\n"; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
1
|
|
|
|
|
182
|
return 1; |
|
143
|
|
|
|
|
|
|
} ## end sub execute |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
return 1; |