Skip to content

Commit 61f1527

Browse files
authored
Merge pull request #15 from koss-null/1.0.7
1.0.7
2 parents 532c8e3 + 5363a8d commit 61f1527

File tree

13 files changed

+580
-103
lines changed

13 files changed

+580
-103
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ The following functions can be used to create a new `Pipe` (this is how I call t
134134
- :frog: `Sort(less func(x, y *T) bool) Pipe`: sorts the elements of the `Pipe` using the provided `less` function as the comparison function.
135135

136136
#### Retrieve a single element or perform a boolean check
137-
- :frog: `Any(fn func(x T) bool) bool`: returns `true` if any element of the `Pipe` satisfies the predicate `fn`, and `false` otherwise. *Available for unknown length.*
137+
- :frog: `Any() T`: returns a random element existing in the pipe. *Available for unknown length.*
138138
- :frog: `First() T`: returns the first element of the `Pipe`, or `nil` if the `Pipe` is empty. *Available for unknown length.*
139139
- :frog: `Count() int`: returns the number of elements in the `Pipe`. It does not allocate memory for the elements, but instead simply returns the number of elements in the `Pipe`.
140140

internal/internalpipe/any.go

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package internalpipe
22

3-
import "sync"
3+
import (
4+
"sync"
5+
"time"
6+
)
47

5-
const infiniteLenStep = 1 << 15
8+
const hugeLenStep = 1 << 15
69

7-
func anySingleThread[T any](lenSet bool, limit int, fn GeneratorFn[T]) *T {
10+
func anySingleThread[T any](limit int, fn GeneratorFn[T]) *T {
811
var obj *T
912
var skipped bool
10-
for i := 0; (!lenSet && i >= 0) || (i < limit); i++ {
13+
14+
for i := 0; i < limit; i++ {
1115
if obj, skipped = fn(i); !skipped {
1216
return obj
1317
}
@@ -17,64 +21,91 @@ func anySingleThread[T any](lenSet bool, limit int, fn GeneratorFn[T]) *T {
1721

1822
// Any returns a pointer to a random element in the pipe or nil if none left.
1923
func (p Pipe[T]) Any() *T {
24+
const mutexUpdateCoef = 18
25+
2026
limit := p.limit()
21-
lenSet := p.lenSet()
2227
if p.GoroutinesCnt == 1 {
23-
return anySingleThread(lenSet, limit, p.Fn)
28+
return anySingleThread(limit, p.Fn)
2429
}
2530

26-
step := infiniteLenStep
31+
lenSet := p.lenSet()
32+
step := hugeLenStep
2733
if lenSet {
2834
step = max(divUp(limit, p.GoroutinesCnt), 1)
2935
}
36+
3037
var (
31-
res = make(chan *T)
32-
// if p.len is not set, we need tickets to control the amount of goroutines
33-
tickets = genTickets(p.GoroutinesCnt)
38+
resSet bool
39+
resCh = make(chan *T, 1)
40+
mx sync.Mutex
3441

35-
done = make(chan struct{})
36-
wg sync.WaitGroup
42+
tickets = genTickets(p.GoroutinesCnt)
43+
wg sync.WaitGroup
3744
)
38-
if !lenSet {
39-
step = infiniteLenStep
40-
}
41-
45+
defer close(resCh)
4246
setObj := func(obj *T) {
43-
select {
44-
case <-done:
45-
return
46-
default:
47-
close(done)
48-
res <- obj
47+
mx.Lock()
48+
if !resSet {
49+
resSet = true
50+
resCh <- obj
4951
}
52+
mx.Unlock()
5053
}
5154

5255
go func() {
5356
// i >= 0 is for an int owerflow case
5457
for i := 0; i >= 0 && (!lenSet || i < limit); i += step {
5558
wg.Add(1)
5659
<-tickets
60+
5761
go func(lf, rg int) {
5862
defer func() {
59-
wg.Done()
6063
tickets <- struct{}{}
64+
wg.Done()
6165
}()
62-
63-
// accounting int owerflow case with max(rg, 0)
66+
// int owerflow case
6467
rg = max(rg, 0)
6568
if lenSet {
6669
rg = min(rg, limit)
6770
}
71+
72+
var avgFnTime time.Duration
73+
var avgUpdResSetTime time.Duration
74+
resSetUpdCnt := int64(0)
75+
beforeLastResSetUpd := 0
76+
77+
getResSet := func() bool {
78+
start := time.Now()
79+
mx.Lock()
80+
rs := resSet
81+
mx.Unlock()
82+
avgUpdResSetTime = time.Duration(
83+
(int64(time.Since(start)) + int64(avgUpdResSetTime)*(resSetUpdCnt)) / (resSetUpdCnt + 1),
84+
)
85+
resSetUpdCnt++
86+
beforeLastResSetUpd = 0
87+
return rs
88+
}
89+
rs := getResSet()
90+
cnt := 0
6891
for j := lf; j < rg; j++ {
69-
select {
70-
case <-done:
71-
return
72-
default:
92+
beforeLastResSetUpd++
93+
if j != lf &&
94+
avgFnTime != 0 &&
95+
int64(beforeLastResSetUpd) > (mutexUpdateCoef*int64(avgUpdResSetTime)/int64(avgFnTime)) {
96+
rs = getResSet()
97+
cnt++
98+
}
99+
if !rs {
100+
start := time.Now()
73101
obj, skipped := p.Fn(j)
74102
if !skipped {
75103
setObj(obj)
76104
return
77105
}
106+
avgFnTime = time.Duration(
107+
(int64(time.Since(start)) + int64(avgFnTime)*int64(j-lf)) / int64(j-lf+1),
108+
)
78109
}
79110
}
80111
}(i, i+step)
@@ -83,8 +114,9 @@ func (p Pipe[T]) Any() *T {
83114
go func() {
84115
wg.Wait()
85116
setObj(nil)
117+
defer close(tickets)
86118
}()
87119
}()
88120

89-
return <-res
121+
return <-resCh
90122
}

internal/internalpipe/any_test.go

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import (
55
"testing"
66

77
"github.com/stretchr/testify/require"
8-
9-
"github.com/koss-null/funcfrog/internal/primitive/pointer"
108
)
119

1210
var (
@@ -28,48 +26,60 @@ func TestAny(t *testing.T) {
2826
t.Parallel()
2927

3028
t.Run("Single thread no limit", func(t *testing.T) {
29+
t.Parallel()
30+
3131
p := Func(func(i int) (float64, bool) {
32-
return a100k[i], a100k[i] <= 90_000.0
32+
return a100k[i], a100k[i] > 90_000.0
3333
})
3434
s := p.Any()
3535
require.NotNil(t, s)
36-
require.Greater(t, 90_000.0, *s)
36+
require.Greater(t, *s, 90_000.0)
3737
})
3838

39-
t.Run("Seven thread no limit", func(t *testing.T) {
39+
t.Run("Single thread limit", func(t *testing.T) {
40+
t.Parallel()
41+
4042
p := Func(func(i int) (float64, bool) {
41-
if i >= len(a100k) {
42-
return 0., false
43-
}
44-
return a100k[i], a100k[i] <= 90_000.0
45-
}).Parallel(7)
43+
return a100k[i], a100k[i] > 90_000.0
44+
}).Gen(len(a100k))
4645
s := p.Any()
4746
require.NotNil(t, s)
48-
require.Greater(t, 90_000.0, *s)
47+
require.Greater(t, *s, 90_000.0)
4948
})
5049

51-
t.Run("Single thread limit", func(t *testing.T) {
50+
t.Run("Seven thread no limit", func(t *testing.T) {
51+
t.Parallel()
52+
5253
p := Func(func(i int) (float64, bool) {
53-
return a100k[i], a100k[i] <= 90_000.0
54-
}).Gen(len(a100k))
54+
if i >= len(a100k) {
55+
return 0., false
56+
}
57+
return a100k[i], true
58+
}).
59+
Filter(func(x *float64) bool { return *x > 90_000. }).
60+
Parallel(7)
5561
s := p.Any()
5662
require.NotNil(t, s)
57-
require.Greater(t, 90_000.0, pointer.From(s))
63+
require.Greater(t, *s, 90_000.0)
5864
})
5965

6066
t.Run("Seven thread limit", func(t *testing.T) {
67+
t.Parallel()
68+
6169
p := Func(func(i int) (float64, bool) {
6270
if i >= len(a100k) {
6371
return 0., false
6472
}
65-
return a100k[i], a100k[i] <= 90_000.0
73+
return a100k[i], a100k[i] > 90_000.0
6674
}).Gen(len(a100k)).Parallel(7)
6775
s := p.Any()
6876
require.NotNil(t, s)
69-
require.Greater(t, 90_000.0, pointer.From(s))
77+
require.Greater(t, *s, 90_000.0)
7078
})
7179

7280
t.Run("Single thread NF limit", func(t *testing.T) {
81+
t.Parallel()
82+
7383
p := Func(func(i int) (float64, bool) {
7484
return a100k[i], false
7585
}).Gen(len(a100k))
@@ -78,6 +88,8 @@ func TestAny(t *testing.T) {
7888
})
7989

8090
t.Run("Seven thread NF limit", func(t *testing.T) {
91+
t.Parallel()
92+
8193
p := Func(func(i int) (float64, bool) {
8294
if i >= len(a100k) {
8395
return 0., false
@@ -89,6 +101,8 @@ func TestAny(t *testing.T) {
89101
})
90102

91103
t.Run("Single thread bounded limit", func(t *testing.T) {
104+
t.Parallel()
105+
92106
p := Func(func(i int) (float64, bool) {
93107
return a100k[i], false
94108
}).Gen(len(a100k))
@@ -97,6 +111,8 @@ func TestAny(t *testing.T) {
97111
})
98112

99113
t.Run("Seven thread bounded limit", func(t *testing.T) {
114+
t.Parallel()
115+
100116
p := Func(func(i int) (float64, bool) {
101117
if i >= len(a100k) {
102118
return 0., false
@@ -109,6 +125,8 @@ func TestAny(t *testing.T) {
109125
})
110126

111127
t.Run("Single thread bounded no limit", func(t *testing.T) {
128+
t.Parallel()
129+
112130
p := Func(func(i int) (float64, bool) {
113131
if i >= len(a100k) {
114132
return 0., false
@@ -121,6 +139,8 @@ func TestAny(t *testing.T) {
121139
})
122140

123141
t.Run("Seven thread bounded no limit", func(t *testing.T) {
142+
t.Parallel()
143+
124144
p := Func(func(i int) (float64, bool) {
125145
if i >= len(a100k) {
126146
return 0., false
@@ -131,4 +151,19 @@ func TestAny(t *testing.T) {
131151
require.NotNil(t, s)
132152
require.Equal(t, 90_001., *s)
133153
})
154+
155+
t.Run("Ten thread bounded no limit filter", func(t *testing.T) {
156+
t.Parallel()
157+
158+
p := Func(func(i int) (float64, bool) {
159+
if i >= len(a100k) {
160+
return 0., false
161+
}
162+
return a100k[i], a100k[i] > 90_000.0 && a100k[i] < 190_000.0
163+
}).Filter(func(x *float64) bool { return int(*x)%2 == 0 }).Parallel(10)
164+
s := p.Any()
165+
require.NotNil(t, s)
166+
require.Greater(t, *s, 90_000.)
167+
require.Less(t, *s, 190_001.)
168+
})
134169
}

internal/internalpipe/filter_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func Test_Filter(t *testing.T) {
2222
}
2323

2424
res := p.Filter(func(x *int) bool {
25-
return pointer.From(x)%2 == 0
25+
return pointer.Deref(x)%2 == 0
2626
}).Do()
2727
j := 0
2828
for i := 0; i < 100_000; i += 2 {
@@ -41,7 +41,7 @@ func Test_Filter(t *testing.T) {
4141
}
4242

4343
res := p.Filter(func(x *int) bool {
44-
return pointer.From(x)%2 == 0
44+
return pointer.Deref(x)%2 == 0
4545
}).Do()
4646
j := 0
4747
for i := 0; i < 100_000; i += 2 {
@@ -50,7 +50,7 @@ func Test_Filter(t *testing.T) {
5050
}
5151
})
5252
t.Run("single thread even numbers empty res filter", func(t *testing.T) {
53-
pts := pointer.To(7)
53+
pts := pointer.Ref(7)
5454
p := Pipe[int]{
5555
Fn: func(i int) (*int, bool) {
5656
return pts, false
@@ -61,12 +61,12 @@ func Test_Filter(t *testing.T) {
6161
}
6262

6363
res := p.Filter(func(x *int) bool {
64-
return pointer.From(x)%2 == 0
64+
return pointer.Deref(x)%2 == 0
6565
}).Do()
6666
require.Equal(t, 0, len(res))
6767
})
6868
t.Run("seven thread even numbers empty res filter", func(t *testing.T) {
69-
pts := pointer.To(7)
69+
pts := pointer.Ref(7)
7070
p := Pipe[int]{
7171
Fn: func(i int) (*int, bool) {
7272
return pts, false
@@ -77,12 +77,12 @@ func Test_Filter(t *testing.T) {
7777
}
7878

7979
res := p.Filter(func(x *int) bool {
80-
return pointer.From(x)%2 == 0
80+
return pointer.Deref(x)%2 == 0
8181
}).Do()
8282
require.Equal(t, 0, len(res))
8383
})
8484
t.Run("seven thread even numbers empty res double filter", func(t *testing.T) {
85-
pts := pointer.To(7)
85+
pts := pointer.Ref(7)
8686
p := Pipe[int]{
8787
Fn: func(i int) (*int, bool) {
8888
return pts, false
@@ -93,9 +93,9 @@ func Test_Filter(t *testing.T) {
9393
}
9494

9595
res := p.Filter(func(x *int) bool {
96-
return pointer.From(x)%2 == 0
96+
return pointer.Deref(x)%2 == 0
9797
}).Filter(func(x *int) bool {
98-
return pointer.From(x)%2 == 0
98+
return pointer.Deref(x)%2 == 0
9999
}).Do()
100100
require.Equal(t, 0, len(res))
101101
})

0 commit comments

Comments
 (0)