-
Notifications
You must be signed in to change notification settings - Fork 525
performance: dynamic lambda #5654
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
Changes from all commits
f4c9674
8a987a4
02fd9f3
3eb1c79
03a3c90
0502b7c
b81bbd3
7ab2db7
1fc2f90
7e9f9fc
b59136b
c3371fd
fc5877b
b7f7146
6781648
87096cb
27d9e38
4077e11
db1386c
412e2ee
2f4eee1
48d1cd4
83d26c9
3039b1e
ce6db01
0b8e8a5
75c8081
3a5a1ed
32a30fa
aa603c9
cc5a872
69633c7
1379b72
201dff2
4d811fb
ebbd32b
be9acf6
c51731a
e676f0a
9e0a3ff
7d39273
272f0a6
089f4cf
5dd8764
0a3fbea
1ace788
911979f
83489b5
93c6a49
c7b9d96
951ecd0
905551b
2778bdd
d508405
6a31232
6a4ea85
e178503
bb94590
b161d7d
83bf00f
1404411
3580617
53838ab
ea35ee3
0bcdf19
4cfb6aa
d459e78
bbfe2ae
b88eab3
d14bde2
b53d93e
85585bc
5bce862
786e779
e02464f
b7feca1
b999bee
15eaa70
358c0eb
be5fe0f
58ac6e8
f679c57
a8b9998
05cbd97
06b0acd
32c9423
5f049d7
85dba70
4a258eb
cbd2bbf
9c3c43b
60e7bf3
0d7ea4b
c8b59c3
1c60257
fbb4234
f3aa450
822e946
a9e9bfc
97fcbc2
8681dca
ffc75d2
a3b1f48
23941a2
005dfbf
8a89821
f88b521
8f884e8
cf06cad
3b61b8a
9ce34be
1d7e6e4
974a74b
b9c9613
effc907
52621f5
1962351
2d74e27
34d5f94
e5da589
1c99da0
2aef8af
4af3e2e
7b0b9d2
d47671e
292284e
be77e7e
b84d3ab
fe288a3
a9ef051
d4604d1
950de96
ba6a55a
23d26ac
7016377
50a9ac0
21ecfec
df15f6b
5c2ece2
9413913
bab29e7
17c48d1
57c2e39
a5bb314
7a24b27
e32460b
d8438b7
7e51ae8
e01ffe6
20d3c2a
f2edc3f
5ca8a27
bd2c576
4c1e706
f6979bc
6a6eec0
b1495d2
98524f0
0d72d56
79e1904
40935ed
c1dea69
0ce9fea
7c1032d
ba1fc25
b79b14c
529278a
4fe6b66
6243be5
869ee41
254f768
ab7c83b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| // Copyright (C) 2019-2023 Algorand, Inc. | ||
| // This file is part of go-algorand | ||
| // | ||
| // go-algorand is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU Affero General Public License as | ||
| // published by the Free Software Foundation, either version 3 of the | ||
| // License, or (at your option) any later version. | ||
| // | ||
| // go-algorand is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU Affero General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Affero General Public License | ||
| // along with go-algorand. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| package agreement | ||
|
|
||
| import ( | ||
| "sort" | ||
| "time" | ||
| ) | ||
|
|
||
| // credentialArrivalHistory maintains a circular buffer of time.Duration samples. | ||
| type credentialArrivalHistory struct { | ||
| history []time.Duration | ||
| writePtr int | ||
| full bool | ||
| } | ||
|
|
||
| func makeCredentialArrivalHistory(size int) credentialArrivalHistory { | ||
| if size < 0 { | ||
| panic("can't create CredentialArrivalHistory with negative size") | ||
| } | ||
| history := credentialArrivalHistory{history: make([]time.Duration, size)} | ||
| history.reset() | ||
| return history | ||
| } | ||
|
|
||
| // store saves a new sample into the circular buffer. | ||
| // If the buffer is full, it overwrites the oldest sample. | ||
| func (history *credentialArrivalHistory) store(sample time.Duration) { | ||
|
yossigi marked this conversation as resolved.
|
||
| if len(history.history) == 0 { | ||
| return | ||
| } | ||
|
|
||
| history.history[history.writePtr] = sample | ||
| history.writePtr++ | ||
| if history.writePtr == len(history.history) { | ||
| history.full = true | ||
| history.writePtr = 0 | ||
| } | ||
| } | ||
|
|
||
| // reset marks the history buffer as empty | ||
| func (history *credentialArrivalHistory) reset() { | ||
| history.writePtr = 0 | ||
| history.full = false | ||
| } | ||
|
|
||
| // isFull checks if the circular buffer has been fully populated at least once. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose this is a bit silly, but if you want the 37th best of 40 and you're trying to avoid answering too low, you could start answering once you have 37 samples, and return the worst. We could call this |
||
| func (history *credentialArrivalHistory) isFull() bool { | ||
|
yossigi marked this conversation as resolved.
|
||
| return history.full | ||
| } | ||
|
|
||
| // orderStatistics returns the idx'th time duration in the sorted history array. | ||
| // It assumes that history is full and the idx is within the array bounds, and | ||
| // panics if either of these assumptions doesn't hold. | ||
| func (history *credentialArrivalHistory) orderStatistics(idx int) time.Duration { | ||
|
yossigi marked this conversation as resolved.
|
||
| if !history.isFull() { | ||
| panic("history not full") | ||
| } | ||
| if idx < 0 || idx >= len(history.history) { | ||
| panic("index out of bounds") | ||
| } | ||
|
|
||
| // if history.history is long, then we could optimize this function to use | ||
| // the linear time order statistics algorithm. | ||
| sortedArrivals := make([]time.Duration, len(history.history)) | ||
| copy(sortedArrivals[:], history.history[:]) | ||
| sort.Slice(sortedArrivals, func(i, j int) bool { return sortedArrivals[i] < sortedArrivals[j] }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm told |
||
| return sortedArrivals[idx] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| // Copyright (C) 2019-2023 Algorand, Inc. | ||
| // This file is part of go-algorand | ||
| // | ||
| // go-algorand is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU Affero General Public License as | ||
| // published by the Free Software Foundation, either version 3 of the | ||
| // License, or (at your option) any later version. | ||
| // | ||
| // go-algorand is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU Affero General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Affero General Public License | ||
| // along with go-algorand. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| package agreement | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/algorand/go-algorand/test/partitiontest" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestCredentialHistoryStore(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
|
|
||
| size := 5 | ||
| buffer := makeCredentialArrivalHistory(size) | ||
| // last store call overwrites the first one | ||
| for i := 0; i < size+1; i++ { | ||
| buffer.store(time.Duration(i)) | ||
| } | ||
|
|
||
| require.True(t, buffer.isFull()) | ||
| require.Equal(t, time.Duration(size), buffer.history[0]) | ||
| for i := 1; i < size; i++ { | ||
| require.Equal(t, time.Duration(i), buffer.history[i]) | ||
| } | ||
| } | ||
|
|
||
| func TestCredentialHistoryReset(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
|
|
||
| size := 5 | ||
| buffer := makeCredentialArrivalHistory(size) | ||
| // last store call overwrites the first one | ||
| for i := 0; i < size+1; i++ { | ||
| buffer.store(time.Duration(i)) | ||
| } | ||
|
|
||
| require.Equal(t, time.Duration(size), buffer.history[0]) | ||
| for i := 1; i < size; i++ { | ||
| require.Equal(t, time.Duration(i), buffer.history[i]) | ||
| } | ||
| require.True(t, buffer.isFull()) | ||
| buffer.reset() | ||
| require.False(t, buffer.isFull()) | ||
| buffer.store(time.Duration(100)) | ||
| require.Equal(t, time.Duration(100), buffer.history[0]) | ||
| } | ||
|
|
||
| func TestCredentialHistoryIsFull(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
| var buffer credentialArrivalHistory | ||
| require.False(t, buffer.isFull()) | ||
|
|
||
| size := 5 | ||
| buffer = makeCredentialArrivalHistory(size) | ||
| require.False(t, buffer.isFull()) | ||
|
|
||
| for i := 1; i < size+10; i++ { | ||
| buffer.store(time.Duration(i)) | ||
| if i < size { | ||
| require.False(t, buffer.isFull()) | ||
| } else { | ||
| require.True(t, buffer.isFull()) | ||
| } | ||
| } | ||
|
|
||
| // reset the buffer and then fill it again | ||
| buffer.reset() | ||
| require.False(t, buffer.isFull()) | ||
|
|
||
| for i := 1; i < size+10; i++ { | ||
| buffer.store(time.Duration(i)) | ||
| if i < size { | ||
| require.False(t, buffer.isFull()) | ||
| } else { | ||
| require.True(t, buffer.isFull()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestCredentialHisotyZeroSize(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
|
|
||
| var buffer credentialArrivalHistory | ||
| require.False(t, buffer.isFull()) | ||
|
|
||
| size := 0 | ||
| buffer = makeCredentialArrivalHistory(size) | ||
| require.False(t, buffer.isFull()) | ||
|
|
||
| // trying to store new samples won't panic but the history is never full | ||
| for i := 0; i < size+10; i++ { | ||
| buffer.store(time.Duration(i)) | ||
| require.False(t, buffer.isFull()) | ||
| } | ||
| } | ||
|
|
||
| func TestOrderStatistics(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
|
|
||
| size := 5 | ||
| buffer := makeCredentialArrivalHistory(size) | ||
| require.False(t, buffer.isFull()) | ||
|
|
||
| for i := 0; i < size; i++ { | ||
| buffer.store(time.Duration(size - i)) | ||
| } | ||
| require.True(t, buffer.isFull()) | ||
|
|
||
| for i := 0; i < size; i++ { | ||
| require.Equal(t, time.Duration(i+1), buffer.orderStatistics(i)) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright (C) 2019-2023 Algorand, Inc. | ||
| // This file is part of go-algorand | ||
| // | ||
| // go-algorand is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU Affero General Public License as | ||
| // published by the Free Software Foundation, either version 3 of the | ||
| // License, or (at your option) any later version. | ||
| // | ||
| // go-algorand is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU Affero General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Affero General Public License | ||
| // along with go-algorand. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| package agreement | ||
|
|
||
| import "time" | ||
|
|
||
| // This file contains parameters for the dynamic filter timeout mechanism. When | ||
| // this feature is enabled (dynamicFilterTimeout is true), these parameters | ||
| // should migrate to be consensus params. | ||
|
|
||
| // DynamicFilterCredentialArrivalHistory specifies the number of past | ||
| // credential arrivals that are measured to determine the next filter | ||
| // timeout. If DynamicFilterCredentialArrivalHistory <= 0, then the dynamic | ||
| // timeout feature is off and the filter step timeout is calculated using | ||
| // the static configuration. | ||
| const dynamicFilterCredentialArrivalHistory int = 40 | ||
|
|
||
| // DynamicFilterTimeoutLowerBound specifies a minimal duration that the | ||
| // filter timeout must meet. | ||
| const dynamicFilterTimeoutLowerBound time.Duration = 500 * time.Millisecond | ||
|
|
||
| // DynamicFilterTimeoutCredentialArrivalHistoryIdx specified which sample to use | ||
| // out of a sorted DynamicFilterCredentialArrivalHistory-sized array of time | ||
| // samples. The 95th percentile of dynamicFilterCredentialArrivalHistory = 40 | ||
| // sorted samples, is at index 37. | ||
| const dynamicFilterTimeoutCredentialArrivalHistoryIdx int = 37 | ||
|
yossigi marked this conversation as resolved.
|
||
|
|
||
| // DynamicFilterTimeoutGraceInterval is additional extension to the dynamic | ||
| // filter time atop the one calculated based on the history of credential | ||
| // arrivals. | ||
| const dynamicFilterTimeoutGraceInterval time.Duration = 50 * time.Millisecond | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // Copyright (C) 2019-2023 Algorand, Inc. | ||
| // This file is part of go-algorand | ||
| // | ||
| // go-algorand is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU Affero General Public License as | ||
| // published by the Free Software Foundation, either version 3 of the | ||
| // License, or (at your option) any later version. | ||
| // | ||
| // go-algorand is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU Affero General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Affero General Public License | ||
| // along with go-algorand. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| package agreement | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/algorand/go-algorand/test/partitiontest" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestSampleIndexIsValid(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
|
|
||
| require.GreaterOrEqual(t, dynamicFilterCredentialArrivalHistory, 0) | ||
| require.GreaterOrEqual(t, dynamicFilterTimeoutCredentialArrivalHistoryIdx, 0) | ||
| if dynamicFilterCredentialArrivalHistory > 0 { | ||
| require.Less(t, dynamicFilterTimeoutCredentialArrivalHistoryIdx, dynamicFilterCredentialArrivalHistory) | ||
| } | ||
| } | ||
|
|
||
| func TestLowerBound(t *testing.T) { | ||
| partitiontest.PartitionTest(t) | ||
|
|
||
| require.Less(t, 20*time.Millisecond, dynamicFilterTimeoutLowerBound) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.