체인의정석

hardhat 환경에서 잘 작동하지 않는 benchmark와 그에 대한 해결 방안 본문

블록체인/Ethers & web3

hardhat 환경에서 잘 작동하지 않는 benchmark와 그에 대한 해결 방안

체인의정석 2023. 2. 21. 16:15
728x90
반응형

여기 블록체인 트랜잭션을 가져오면서 더 빠른 속도로 가져오기 위해 

chatGPT로 최적화를 시켜 다듬은 코드 5개가 있다.

 

이런 식으로 파일을 여러개 만들어두고 어느게 가장 빠른지 알려면 어떻게 해야 할까?

 

일단 각 함수를 모듈화 해준 후에 이를 가져오는 작업을 하고

해당 모듈의 함수에 같은 입력값을 넣은 후 초당 몇번씩 실행이 가능한지 보면 될 것이다.

const Benchmark = require('benchmark');
// const batchGetRecentBlockInfo1 = require('./localscan');
const batchGetRecentBlockInfo2 = require('./localscanGPT');
const batchGetRecentBlockInfo4 = require('./localscanGPT4');
const batchGetRecentBlockInfo3 = require('./localscanGPT2');
const batchGetRecentBlockInfo5 = require('./localscanGPT3');
// const getTxInfosBetween = require('./getScanInfo');

// Create a new benchmark suite
const suite = new Benchmark.Suite;
const input1 = "address";
const input2 = [fromBlock, toBlock]


// // add tests
new Benchmark.Suite()
.add('Function 2 test', function() {
  return batchGetRecentBlockInfo2(input1,input2)
}) 
.add('Function 3 test', function() {
  return batchGetRecentBlockInfo3(input1,input2)
})
.add('Function 4 test', function() { 
  return batchGetRecentBlockInfo4(input1,input2)
})
.add('Function 5 test', function() {
  return batchGetRecentBlockInfo5(input1,input2)
})
// add listeners
.on('cycle', function(event) {
  console.log(String(event.target));
})
.on('complete', function() {
  console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': false })

// console.log(batchGetRecentBlockInfo2(input1,input2))

요런 식으로 benchmark를 사용하면 테스트가 가능하다.

 

➜ local_block_test npx hardhat run scripts/benchmark.js --network 네트워크이름
You are using a version of Node.js that is not supported by Hardhat, and it may work incorrectly, or not work at all.

Please, make sure you are using a supported version of Node.js.

To learn more about which versions of Node.js are supported go to https://hardhat.org/nodejs-versions
Function 2 test x 19.97 ops/sec ±2.52% (37 runs sampled)
Function 3 test x 736 ops/sec ±3.67% (69 runs sampled)
Function 4 test x 699 ops/sec ±4.99% (82 runs sampled)
Function 5 test x 2.83 ops/sec ±10.48% (12 runs sampled)
Fastest is Function 3 test

이런식으로 하면 초당 함수가 몇번이 실행되었는지 알 수 있다.

보면 3번 함수가 초당 736번 실행되어서 속도가 가장 빠른 것을 알 수 있다.

 

일단 응답값을 동기화 시키고 싶었기 때문에

// // add tests
new Benchmark.Suite()
.add('Function 2 test', async function() {
  return await batchGetRecentBlockInfo2(input1,input2)
}) 
.add('Function 3 test', async function() {
  return await batchGetRecentBlockInfo3(input1,input2)
})
.add('Function 4 test', async function() { 
  return await batchGetRecentBlockInfo4(input1,input2)
})
.add('Function 5 test', async function() {
  return await batchGetRecentBlockInfo5(input1,input2)
})
.add('original test', async function() {
  return await originalFunction(input1,input2)
})
// add listeners
.on('cycle', function(event) {
  console.log(String(event.target));
})
.on('complete', function() {
  console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': false })

요런식으로 동기화를 시켜주고 실행 시켜봤다.

Function 2 test x 30.02 ops/sec ±4.21% (54 runs sampled)
Function 3 test x 1,023 ops/sec ±1.62% (93 runs sampled)
Function 4 test x 965 ops/sec ±3.63% (88 runs sampled)
Function 5 test x 3.25 ops/sec ±1.63% (13 runs sampled)
original test x 1,970 ops/sec ±14.09% (57 runs sampled)
Fastest is original test

그 결과 실행 결과가 약간은 줄어들긴 했지만 function3의 성능을 보면 크게는 차이가 안남은 것으로 보이긴 한다.

 

근데 실행해보니까 좀 이상하다. 1초에 한번도 안되는데 이렇게 많이?

아무래도 블록체인쪽의 통신을 제외한 값을 테스트하는게 아닌가 싶다.

그럼 진짜 트랜잭션까지 실행한 결과인지 테스트해보려면 로컬 노드를 띄워보고 한번 실행해 보면 될 것이다.

npx hardhat node

 

이 명령어를 치면 노컬 노드가 실행이 되게 되고

 

팁) 로컬 노드의 경우 설정값을 아예 없이 해야 에러가 안난다. 에러가 난다면 설정값을 빼주자!

network에 hardhat이 명시되어 있다면 빼주면 된다.

Error HH8: There's one or more errors in your config file:

  * HardhatConfig.networks.hardhat can't have an url
  * Invalid account: #0 for network: hardhat - Expected object, received string

To learn more about Hardhat's configuration, please go to https://hardhat.org/config/

 

아무트 빼고 실행해보면 

 

아무것도 안찍힌다.

 

그치만 단일 함수 실행은

요런식으로 찍히게 된다.

 

따라서 해당 성능 비교의 경우 리턴값이 온 경우를 가정한 리턴 값으로 어떤걸 선택하면 좋을지에는 도움이 되겠지만 실제 실행 시간을 측정 할 수는 없다. 

 

그런 경우 일단 함수를 만들고 api benchmark를 쓰면 될 것 같다.

https://www.npmjs.com/package/api-benchmark

 

api-benchmark

A simple nodejs tool to measure and compare performances of api services. Latest version: 1.0.1, last published: 2 years ago. Start using api-benchmark in your project by running `npm i api-benchmark`. There are 4 other projects in the npm registry using a

www.npmjs.com

종합지어 결론을 내리자면 로컬 로직이 빠른걸 먼저 선택한 후에

 

실제 성능 테스트때는 api benchmark를 쓰면 되는 것 같다.

 

또한 로컬에서 밴치마크를 쓰게 되면 실제 블록체인 네트워크와의 응답 시간은 기록되지 않으므로 오프체인 로직의 속도만 비교가 가능하다. 이에 따라 오프체인 로직의 속도 비교 후에 온체인 간의 비교는 실제 api 제작 후에 얼마나 걸리는지 api-benchmark로 체크해 보겠다.

 

근데 만약 가볍게 로컬에서 실행하고 싶다면?

console.time('batchGetRecentBlockInfo3');   // 시작
batchGetRecentBlockInfo3(
    input1,
    input2,
    ).then(console.timeEnd('batchGetRecentBlockInfo3'))
    .catch((error) => {
    console.error(error);
    process.exitCode = 1;
  });

요런식으로 실행하면 결과값을 알 수 있다.

batchGetRecentBlockInfo3: 828.003ms

0.8초 정도 걸리는 것을 볼 수 있다.

 

다시 정리하자면

1. benchmark로 오프체인 연산비교

2. 테스트넷에서 직접 함수 실행 (시간 로그로 찍어서)

3. api로 만든 후에 벤치마크로 속도 측정

 

이런 식으로 하면 될 것 같다.

 

 

일단 백엔드 반영 전에 간단히 테스트 해본 코드는 아래와 같다.

 

벤치마크 없이 진행한것이며 실제 실행 결과 벤치마크와는 다른 결과가 나옴을 알 수 있었다.

const batchGetRecentBlockInfo2 = require('./localscanGPT');
const batchGetRecentBlockInfo4 = require('./localscanGPT4');
const batchGetRecentBlockInfo3 = require('./localscanGPT2');
const batchGetRecentBlockInfo5 = require('./localscanGPT3');

// Create a new benchmark suite
const input1 = "지갑주소";
const input2 = [16730481, 16731481]

async function testBlockInfo2() {
  console.time('batchGetRecentBlockInfo2');   // 시작
  const answer = await batchGetRecentBlockInfo2(
      input1,
      input2,
  ).then( (r) =>{
    console.timeEnd('batchGetRecentBlockInfo2')
    console.log("r is " ,r)
  })
  return answer
}

async function testBlockInfo3() {
  console.time('batchGetRecentBlockInfo3');   // 시작
  const answer = await batchGetRecentBlockInfo3(
      input1,
      input2,
  ).then( (r) =>{
    console.timeEnd('batchGetRecentBlockInfo3')
    console.log("r is " ,r)
  })
  return answer
}

async function testBlockInfo4() {
  console.time('batchGetRecentBlockInfo4');   // 시작
  const answer = await batchGetRecentBlockInfo4(
      input1,
      input2,
  ).then( (r) =>{
    console.timeEnd('batchGetRecentBlockInfo4')
    console.log("r is " ,r)
  })
  return answer
}

async function testBlockInfo5() {
  console.time('batchGetRecentBlockInfo5');   // 시작
  const answer = await batchGetRecentBlockInfo5(
      input1,
      input2,
  ).then( (r) =>{
    console.timeEnd('batchGetRecentBlockInfo5')
    console.log("r is " ,r)
  })
  return answer
}


  testBlockInfo2()
  testBlockInfo3()
  testBlockInfo4()
  testBlockInfo5()

batchGetRecentBlockInfo2: 2.994s

2번 함수가 가장 강력함을 볼 수 있었다.

 

*오늘의 결론 벤치마크의 경우 블록체인과 실제 통신이 안되기 때문에 온체인이 포함되어 있는 연산이라면 console로 time을 찍어서 확인해보자!

 

결론

1. 테스트넷에서 직접 함수 실행 (시간 로그로 찍어서 실행하기)

2. 1번을 여러번 실행시켜보고 가장 빠른 api로 만든 후에 벤치마크로 속도 측정하기

 

이런 식으로 하면 될 것 같다.

728x90
반응형
Comments