github.com/klaytn/klaytn@v1.12.1/tests/addtx_test.go (about) 1 // Copyright 2018 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tests 18 19 import ( 20 "crypto/ecdsa" 21 "flag" 22 "fmt" 23 "math/big" 24 "math/rand" 25 "os" 26 "runtime/pprof" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/klaytn/klaytn/blockchain" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/common" 34 "github.com/klaytn/klaytn/common/profile" 35 "github.com/klaytn/klaytn/params" 36 ) 37 38 var cpuprofile bool = false 39 40 func init() { 41 flag.BoolVar(&cpuprofile, "addtx_profile", false, "Enable cpu profiling for AddTx") 42 } 43 44 //////////////////////////////////////////////////////////////////////////////// 45 // BenchmarkAddTx 46 //////////////////////////////////////////////////////////////////////////////// 47 func BenchmarkAddTx(b *testing.B) { 48 cacheSender := []bool{false, true} 49 maxAccounts := []int{1000 + 100, 1000 + 1000, 1000 + 10000} 50 numValidators := []int{4} 51 parallels := []string{"parallel", "sequential", "queueing"} 52 numQueues := []int{1, 2, 4, 8} 53 54 fmt.Printf("addtx_profile = %t\n", cpuprofile) 55 56 for _, cs := range cacheSender { 57 for _, ma := range maxAccounts { 58 for _, nv := range numValidators { 59 for _, p := range parallels { 60 61 numQueues := numQueues 62 if p != "queueing" { 63 numQueues = []int{1} 64 } 65 for _, nq := range numQueues { 66 testName := fmt.Sprintf("%t,%d,%d,%s,%d", cs, ma-1000, nv, p, nq) 67 b.Run(testName, func(b *testing.B) { 68 benchAddTx(b, ma, nv, p, nq, cs) 69 }) 70 } 71 } 72 } 73 } 74 } 75 } 76 77 func txDispatcher(ch <-chan *types.Transaction, txpool *blockchain.TxPool, wait *sync.WaitGroup) { 78 for { 79 if t, ok := <-ch; ok { 80 err := txpool.AddLocal(t) 81 if err != nil { 82 fmt.Fprintf(os.Stderr, "%s\n", err) 83 } 84 wait.Done() 85 } else { 86 break 87 } 88 } 89 } 90 91 func benchAddTx(b *testing.B, maxAccounts, numValidators int, parallel string, numQueue int, 92 cacheSender bool, 93 ) { 94 // Initialize blockchain 95 start := time.Now() 96 bcdata, err := NewBCData(maxAccounts, numValidators) 97 if err != nil { 98 b.Fatal(err) 99 } 100 profile.Prof.Profile("main_init_blockchain", time.Now().Sub(start)) 101 defer bcdata.Shutdown() 102 103 // Initialize address-balance map for verification 104 start = time.Now() 105 accountMap := NewAccountMap() 106 if err := accountMap.Initialize(bcdata); err != nil { 107 b.Fatal(err) 108 } 109 profile.Prof.Profile("main_init_accountMap", time.Now().Sub(start)) 110 111 poolConfig := blockchain.TxPoolConfig{ 112 Journal: transactionsJournalFilename, 113 JournalInterval: time.Hour, 114 115 PriceLimit: 1, 116 PriceBump: 10, 117 118 ExecSlotsAccount: 16, 119 ExecSlotsAll: 40000, 120 NonExecSlotsAccount: 64, 121 NonExecSlotsAll: 40000, 122 123 Lifetime: 5 * time.Minute, 124 } 125 txpool := blockchain.NewTxPool(poolConfig, bcdata.bc.Config(), bcdata.bc) 126 127 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentBlock().Number()) 128 129 var wait sync.WaitGroup 130 131 var txChs []chan *types.Transaction 132 133 if parallel == "queueing" { 134 txChs = make([]chan *types.Transaction, numQueue) 135 for i := 0; i < numQueue; i++ { 136 txChs[i] = make(chan *types.Transaction, 100) 137 } 138 139 for i := 0; i < numQueue; i++ { 140 go txDispatcher(txChs[i], txpool, &wait) 141 } 142 } 143 144 var f *os.File = nil 145 if cpuprofile { 146 f, err = os.Create(fmt.Sprintf("profile_cpu_%t_%d_%d_%s_%d.out", 147 cacheSender, maxAccounts-1000, numValidators, parallel, numQueue)) 148 if err != nil { 149 b.Fatal("could not create CPU profile :", err) 150 } 151 } 152 153 txs := make([]types.Transactions, b.N) 154 for i := 0; i < b.N; i++ { 155 txs[i], err = makeTransactions(accountMap, 156 bcdata.addrs[1000:maxAccounts], bcdata.privKeys[1000:maxAccounts], 157 signer, bcdata.addrs[0:maxAccounts-1000], nil, i, cacheSender) 158 if err != nil { 159 b.Fatal(err) 160 } 161 } 162 163 b.ResetTimer() 164 if cpuprofile { 165 if err := pprof.StartCPUProfile(f); err != nil { 166 b.Fatal("could not start CPU profile : ", err) 167 } 168 } 169 for i := 0; i < b.N; i++ { 170 switch parallel { 171 case "parallel": 172 txParallel(txs[i], txpool) 173 174 case "sequential": 175 txSequential(txs[i], txpool) 176 177 case "queueing": 178 txQueue(txs[i], txpool, txChs, numQueue, &wait) 179 } 180 181 pending, _ := txpool.Stats() 182 if pending%(maxAccounts-1000) != 0 { 183 b.Fatalf("pending(%d) should be divided by %d!\n", pending, maxAccounts-1000) 184 } 185 } 186 b.StopTimer() 187 if cpuprofile { 188 pprof.StopCPUProfile() 189 } 190 191 txpool.Stop() 192 if cpuprofile { 193 f.Close() 194 } 195 196 if testing.Verbose() { 197 profile.Prof.PrintProfileInfo() 198 } 199 } 200 201 func makeTransactions(accountMap *AccountMap, fromAddrs []*common.Address, privKeys []*ecdsa.PrivateKey, 202 signer types.Signer, toAddrs []*common.Address, amount *big.Int, additionalNonce int, 203 cacheSender bool, 204 ) (types.Transactions, error) { 205 txs := make(types.Transactions, 0, len(toAddrs)) 206 for i, from := range fromAddrs { 207 nonce := accountMap.GetNonce(*from) 208 nonce += uint64(additionalNonce) 209 210 txamount := amount 211 if txamount == nil { 212 txamount = big.NewInt(rand.Int63n(10)) 213 txamount = txamount.Add(txamount, big.NewInt(1)) 214 } 215 216 var gasLimit uint64 = 1000000 217 gasPrice := new(big.Int).SetInt64(25 * params.Ston) 218 data := []byte{} 219 220 tx := types.NewTransaction(nonce, *toAddrs[i], txamount, gasLimit, gasPrice, data) 221 signedTx, err := types.SignTx(tx, signer, privKeys[i]) 222 if err != nil { 223 return nil, err 224 } 225 226 if cacheSender { 227 signed_addr, err := types.Sender(signer, signedTx) 228 if signed_addr != *from { 229 fmt.Printf("signed address(%s) != from(%s)\n", signed_addr.Hex(), from.Hex()) 230 } 231 if err != nil { 232 return nil, err 233 } 234 } 235 236 txs = append(txs, signedTx) 237 } 238 239 return txs, nil 240 } 241 242 func txParallel(txs types.Transactions, txpool *blockchain.TxPool) { 243 var wait sync.WaitGroup 244 245 wait.Add(len(txs)) 246 247 for _, tx := range txs { 248 go func(t *types.Transaction) { 249 err := txpool.AddLocal(t) 250 if err != nil { 251 fmt.Fprintf(os.Stderr, "%s\n", err) 252 } 253 wait.Done() 254 }(tx) 255 } 256 257 wait.Wait() 258 } 259 260 func txSequential(txs types.Transactions, txpool *blockchain.TxPool) { 261 for _, tx := range txs { 262 err := txpool.AddLocal(tx) 263 if err != nil { 264 fmt.Fprintf(os.Stderr, "%s\n", err) 265 } 266 } 267 } 268 269 func txQueue(txs types.Transactions, txpool *blockchain.TxPool, 270 txChs []chan *types.Transaction, numQueue int, wait *sync.WaitGroup, 271 ) { 272 wait.Add(len(txs)) 273 274 for i, tx := range txs { 275 txChs[i%numQueue] <- tx 276 } 277 278 wait.Wait() 279 }