@@ -15,6 +15,7 @@ import (
1515 "runtime"
1616 "strings"
1717 "sync"
18+ "sync/atomic"
1819 "testing"
1920 "time"
2021
@@ -95,6 +96,298 @@ var opts = Opts{
9596
9697const N = 500
9798
99+ func BenchmarkPerformance_naive (b * testing.B ) {
100+ var err error
101+
102+ conn := test_helpers .ConnectWithValidation (b , dialer , opts )
103+ defer conn .Close ()
104+
105+ _ , err = conn .Replace (spaceNo , []interface {}{uint (1111 ), "hello" , "world" })
106+ if err != nil {
107+ b .Fatalf ("failed to inialize database: %s" , err )
108+ }
109+
110+ b .ResetTimer ()
111+
112+ for b .Loop () {
113+ req := NewSelectRequest (spaceNo ).
114+ Index (indexNo ).
115+ Iterator (IterEq ).
116+ Key ([]interface {}{uint (1111 )})
117+ data , err := conn .Do (req ).Get ()
118+ if err != nil {
119+ b .Errorf ("request error: %s" , err )
120+ }
121+
122+ tuple := data [0 ].([]any )
123+ if tuple [0 ].(uint16 ) != uint16 (1111 ) {
124+ b .Errorf ("invalid result" )
125+ }
126+ }
127+ }
128+
129+ func BenchmarkPerformance_naive_with_single_request (b * testing.B ) {
130+ var err error
131+
132+ conn := test_helpers .ConnectWithValidation (b , dialer , opts )
133+ defer conn .Close ()
134+
135+ _ , err = conn .Replace (spaceNo , []interface {}{uint (1111 ), "hello" , "world" })
136+ if err != nil {
137+ b .Fatalf ("failed to inialize database: %s" , err )
138+ }
139+
140+ req := NewSelectRequest (spaceNo ).
141+ Index (indexNo ).
142+ Iterator (IterEq ).
143+ Key (UintKey {I : 1111 })
144+
145+ b .ResetTimer ()
146+
147+ for b .Loop () {
148+ data , err := conn .Do (req ).Get ()
149+ if err != nil {
150+ b .Errorf ("request error: %s" , err )
151+ }
152+
153+ tuple := data [0 ].([]any )
154+ if tuple [0 ].(uint16 ) != uint16 (1111 ) {
155+ b .Errorf ("invalid result" )
156+ }
157+ }
158+ }
159+
160+ type benchTuple struct {
161+ id uint
162+ }
163+
164+ func (t * benchTuple ) DecodeMsgpack (dec * msgpack.Decoder ) error {
165+ l , err := dec .DecodeArrayLen ()
166+ if err != nil {
167+ return fmt .Errorf ("failed to decode tuples array: %w" , err )
168+ }
169+
170+ if l != 1 {
171+ return fmt .Errorf ("unexpected tuples array with len %d" , l )
172+ }
173+
174+ l , err = dec .DecodeArrayLen ()
175+ if err != nil {
176+ return fmt .Errorf ("failed to decode tuple array: %w" , err )
177+ }
178+
179+ if l < 1 {
180+ return fmt .Errorf ("too smal tuple have 0 fields" )
181+ }
182+
183+ t .id , err = dec .DecodeUint ()
184+ if err != nil {
185+ return fmt .Errorf ("failed to decode id: %w" , err )
186+ }
187+
188+ return nil
189+ }
190+
191+ func BenchmarkPerformance_naive_with_custom_type (b * testing.B ) {
192+ var err error
193+
194+ conn := test_helpers .ConnectWithValidation (b , dialer , opts )
195+ defer conn .Close ()
196+
197+ _ , err = conn .Replace (spaceNo , []interface {}{uint (1111 ), "hello" , "world" })
198+ if err != nil {
199+ b .Fatalf ("failed to inialize database: %s" , err )
200+ }
201+
202+ req := NewSelectRequest (spaceNo ).
203+ Index (indexNo ).
204+ Iterator (IterEq ).
205+ Key (UintKey {I : 1111 })
206+
207+ var tuple benchTuple
208+
209+ b .ResetTimer ()
210+
211+ for b .Loop () {
212+ err := conn .Do (req ).GetTyped (& tuple )
213+ if err != nil {
214+ b .Errorf ("request error: %s" , err )
215+ }
216+
217+ if tuple .id != 1111 {
218+ b .Errorf ("invalid result" )
219+ }
220+ }
221+ }
222+
223+ func BenchmarkPerformance_multithread (b * testing.B ) {
224+ var err error
225+
226+ conn := test_helpers .ConnectWithValidation (b , dialer , opts )
227+ defer conn .Close ()
228+
229+ _ , err = conn .Replace (spaceNo , []interface {}{uint (1111 ), "hello" , "world" })
230+ if err != nil {
231+ b .Fatalf ("failed to inialize database: %s" , err )
232+ }
233+
234+ req := NewSelectRequest (spaceNo ).
235+ Index (indexNo ).
236+ Iterator (IterEq ).
237+ Key (UintKey {I : 1111 })
238+
239+ b .ResetTimer ()
240+
241+ b .RunParallel (func (pb * testing.PB ) {
242+ var tuple benchTuple
243+
244+ for pb .Next () {
245+ err := conn .Do (req ).GetTyped (& tuple )
246+ if err != nil {
247+ b .Errorf ("request error: %s" , err )
248+ }
249+
250+ if tuple .id != 1111 {
251+ b .Errorf ("invalid result" )
252+ }
253+ }
254+ })
255+ }
256+
257+ func BenchmarkPerformance_multithread_parallelism (b * testing.B ) {
258+ var err error
259+
260+ conn := test_helpers .ConnectWithValidation (b , dialer , opts )
261+ defer conn .Close ()
262+
263+ _ , err = conn .Replace (spaceNo , []interface {}{uint (1111 ), "hello" , "world" })
264+ if err != nil {
265+ b .Fatalf ("failed to inialize database: %s" , err )
266+ }
267+
268+ req := NewSelectRequest (spaceNo ).
269+ Index (indexNo ).
270+ Iterator (IterEq ).
271+ Key (UintKey {I : 1111 })
272+
273+ b .ResetTimer ()
274+
275+ for p := 1 ; p <= 1024 ; p *= 2 {
276+ b .Run (fmt .Sprintf ("%d" , p ), func (b * testing.B ) {
277+ b .SetParallelism (p )
278+ b .ResetTimer ()
279+
280+ b .RunParallel (func (pb * testing.PB ) {
281+ var tuple benchTuple
282+
283+ for pb .Next () {
284+ err := conn .Do (req ).GetTyped (& tuple )
285+ if err != nil {
286+ b .Errorf ("request error: %s" , err )
287+ }
288+
289+ if tuple .id != 1111 {
290+ b .Errorf ("invalid result" )
291+ }
292+ }
293+ })
294+ })
295+ }
296+ }
297+
298+ func TestPerformance (t * testing.T ) {
299+ requests := int64 (10_000_000 )
300+ connections := 16
301+
302+ ops := opts
303+ ops .Concurrency = 8
304+
305+ conns := make ([]* Connection , 0 , connections )
306+ for range connections {
307+ conn := test_helpers .ConnectWithValidation (t , dialer , ops )
308+ defer conn .Close ()
309+
310+ conns = append (conns , conn )
311+ }
312+
313+ _ , err := conns [0 ].Replace (spaceNo , []interface {}{uint (1111 )})
314+ if err != nil {
315+ t .Fatalf ("failed to inialize database: %s" , err )
316+ }
317+
318+ req := NewSelectRequest (spaceNo ).
319+ Index (indexNo ).
320+ Iterator (IterEq ).
321+ Key (UintKey {I : 1111 })
322+
323+ maxRps := float64 (0 )
324+ maxConnections := 0
325+ maxConcurrency := 0
326+
327+ for cn := 1 ; cn <= connections ; cn *= 2 {
328+ for cc := 1 ; cc <= 512 ; cc *= 2 {
329+ var wg sync.WaitGroup
330+
331+ curRequests := requests
332+
333+ start := time .Now ()
334+
335+ for i := range cc {
336+ wg .Add (1 )
337+
338+ ch := make (chan * Future , 1024 )
339+
340+ go func (i int ) {
341+ defer close (ch )
342+
343+ for atomic .AddInt64 (& curRequests , - 1 ) >= 0 {
344+ ch <- conns [i % cn ].Do (req )
345+ }
346+ }(i )
347+
348+ go func () {
349+ defer wg .Done ()
350+
351+ var tuple benchTuple
352+
353+ for fut := range ch {
354+ err := fut .GetTyped (& tuple )
355+ if err != nil {
356+ t .Errorf ("request error: %s" , err )
357+ }
358+
359+ if tuple .id != 1111 {
360+ t .Errorf ("invalid result" )
361+ }
362+ }
363+ }()
364+ }
365+
366+ wg .Wait ()
367+
368+ duration := time .Since (start )
369+
370+ rps := float64 (requests )/ duration .Seconds ()
371+ fmt .Println ("requests :" , requests )
372+ fmt .Println ("concurrency:" , cc )
373+ fmt .Println ("connections:" , cn )
374+ fmt .Printf ("duration : %.2f\n " , duration .Seconds ())
375+ fmt .Printf ("requests/s : %.2f\n " , rps )
376+ fmt .Println ("============" )
377+
378+ if maxRps < rps {
379+ maxRps = rps
380+ maxConnections = cn
381+ maxConcurrency = cc
382+ }
383+ }
384+ }
385+
386+ fmt .Println ("max connections:" , maxConnections )
387+ fmt .Println ("max concurrency:" , maxConcurrency )
388+ fmt .Printf ("max requests/s : %.2f" , maxRps )
389+ }
390+
98391func BenchmarkClientSerial (b * testing.B ) {
99392 var err error
100393
0 commit comments