#python #GoLang #ProtoBuf #Performance
Python vs. GoLang ? ! ? !
한번쯤 파이썬을 사용하면서 느끼는 목마름... Performance!
이미 좋은 라이브러리가 있기 때문에 거의 모든 것들이 해결되는 요즘이지만,
뭔가 파이썬은 느리다는 대중의 시선(?)은 사라지지 않는듯 하다.
그래서!
ProtoBuf 를 활용해서 Go 언어의 성능을 파이썬에서 사용해보면 어떨까?
라는 질문에서 시작한 토이 프로젝트를 남겨본다.
전체적인 디렉토리 구조
뭔가 복잡해보이지만... 하나씩 해보면 금방 적응이 가능하다.
0. fib_proto_dir 생성 및 이동
1. fib.proto 파일 생성
syntax = "proto3";
package fibonacci;
option go_package = "david/fibpackage";
service FibonacciService {
rpc CalculateSumOfFibonacci (FibonacciRequest) returns (FibonacciResponse) {}
}
message FibonacciRequest {
int32 repeats = 1;
int32 n = 2;
}
message FibonacciResponse {
string result = 1;
}
2. pb 파일 생성하기 --> .go & .py 파일들이 생성된다.
>> protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative fib.proto
>> python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. fib.proto
3. fib.py 파일 생성
import grpc
import time
import fib_pb2
import fib_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = fib_pb2_grpc.FibonacciServiceStub(channel)
request = fib_pb2.FibonacciRequest(repeats=10000, n=10000)
start_time = time.time()
response = stub.CalculateSumOfFibonacci(request)
end_time = time.time()
print("Sum of Fibonacci repeats (gRPC):", response.result)
print("Execution time (gRPC):", end_time - start_time, "seconds")
if __name__ == '__main__':
run()
4. go.mod 생성
>> go mod init david/fibpackage
>> go mod tidy
현재까지 fib_proto_dir 에서 작업을 진행했고, 이제 상위 디렉토리로 이동 후에 나머지 작업을 마무리한다.
5. fib.go 파일 생성
package main
import (
"context"
"fmt"
"log"
"math/big"
"net"
pb "david/fibpackage"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedFibonacciServiceServer
}
func fibonacci(n int) *big.Int {
a, b := big.NewInt(0), big.NewInt(1)
for i := 0; i < n; i++ {
a.Add(a, b)
a, b = b, a
}
return a
}
func calculateSumOfFibonacciRepeats(repeats int, n int) *big.Int {
total := big.NewInt(0)
for i := 0; i < repeats; i++ {
total.Add(total, fibonacci(n))
}
return total
}
func (s *server) CalculateSumOfFibonacci(ctx context.Context, in *pb.FibonacciRequest) (*pb.FibonacciResponse, error) {
total := calculateSumOfFibonacciRepeats(int(in.Repeats), int(in.N))
return &pb.FibonacciResponse{Result: total.String()}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterFibonacciServiceServer(s, &server{})
fmt.Println("Server is running on port 50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
GoLang 은 데충 코드 적어도 fmt 잘 되더라...
여기서! big int 를 사용하는데 이유는 fibonacci 결과가 너무 커서 오버플로우가 발생할 수 있어서!!
그래서! proto 파일에서도 return 결과의 타입을 string 으로 지정해 두었음!!
6. 4번 스텝에서 생성한 david/fibpackage 사용할 수 있도록 mod 생성
>> go mod init david/fibproject
>> go mod edit -replace david/fibpackage=./fib_proto_dir
>> go mod tidy
이게 끝!
성능비교를 해보면,
시나리오는 10_000 번의 피보나치 합을 구하면 되는데... 숫자가 엄청 크다...
먼저,
파이썬에서 실행하면
나름 극한의 테스트...
10_000번의 피보나치를 10_000회 실행...
결과는 약 33초!
이걸 grpc 통신을 통해서 GoLang 으로 돌려보면(분명 grpc 통신에서 생성되는 로드가 존재)
실행방법은
>> go run fib.go
>> python fib_proto_dir/fib.py
5초?!
(함정) 실제로 작은 변수로 돌렸을 때는 순수 파이썬이 더 빠를 수 있다.
이걸로 뭘 기대할 수 있을까?!...
ProtoBuf 도 배워야 하고,
GoLang 도 배워야 하고,
관련된 Python 도 배워야 하고...
그냥 numpy, pandas, torch 라이브러리를 열심히 공부해서 사용하는게 더 이득일 수 있겠다... ㅜ
즐거웠으면 그걸로 끝이다.