Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

C# - 10진수 숫자를 담은 문자열을 숫자로 변환하는 방법

아래와 같은 질문이 있군요.

아스키로 구성된 바이트를 long으로 변환 문의
; https://www.sysnet.pe.kr/3/0/5664

정리해 보면, 숫자를 "문자열"로 가지고 있는 데이터를 다시 숫자로 변환하고 싶은 건데요, 가령 "0000000123" 문자열이 보관된 byte 배열을,

byte [] bytes = /* 소켓 Receive로 받은 바이트 배열 */ Encoding.ASCII.GetBytes("0000000123");

숫자 123으로 변환하고 싶은 것입니다.

string tmp = Encoding.ASCII.GetString(bytes);
long myint = Convert.ToInt64(tmp);

질문자는 위에 대한 코드보다 더 빠른 것을 원하는 건데요, 사실 이런 경우, BCL에서 제공하는 메서드를 다루는 것이 최적화가 잘 된 코드라서 대부분은 충분히 빠르므로 그냥 쓰셔도 좋습니다.

굳이, 아주 미세하게 빠른 속도를 원한다면 GetString 절차를 생략하고 곧바로 바이트 배열로부터 숫자를 바꿀 수 있을 텐데요, 대충 다음과 같은 식으로 만들 수 있습니다.

private static long ConvertToLong(byte[] bytes)
{
    long n = 1;
    long result = 0;

    for (int i = bytes.Length - 1; i >= 0; i --)
    {
        result = result + (bytes[i] - '0') * n;
        n *= 10;
    }

    return result;
}

성능 측정을 해보면 딱히 "감동적인" 수준으로 빠르지는 않습니다. 어쨌든 이렇게 테스트 코드를 구성해서,

using System.Diagnostics;
using System.Text;

internal class Program
{
    static void Main(string[] args)
    {
        Action<int, string, Func<byte[], long>, byte[]> action = (loopCount, title, work, arg) =>
        {
            Stopwatch st = new Stopwatch();
            st.Start();

            Random rand = new Random(Environment.TickCount);

            for (int i = 0; i < loopCount; i++)
            {
                work(arg);
            }

            st.Stop();

            Console.WriteLine(title + " : " + st.ElapsedMilliseconds);
        };

        byte[] buf = Encoding.ASCII.GetBytes("1000000123");

        action(1, "touch-JIT", ConvertNormal, buf);
        action(1, "touch-JIT", ConvertToLong, buf);

        Console.WriteLine();

        action(1000000, "ConvertNormal", ConvertNormal, buf);
        action(1000000, "ConvertToLong", ConvertToLong, buf);
    }

    private static long ConvertNormal(byte[] bytes)
    {
        string tmp = Encoding.ASCII.GetString(bytes);
        return Convert.ToInt64(tmp);
    }
    
    // ...[생략 ConvertToLong]...
}

실행하면 이런 결과가 나옵니다.

ConvertNormal : 36
ConvertToLong : 12

수치상으로 3배 빨라지긴 했는데, 백만 번 수행한 속도 차이이기 때문에 웬만한 고성능을 요구하는 경우가 아니라면 차이는 미미합니다. 게다가, 저렇게나 성능이 요구되는 시스템이라면 애당초 서버 측에 "숫자 문자열"이 아닌, "숫자"로 데이터를 정정해서 보내달라고 하는 편이 더 효과를 거둘 수 있습니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 5/11/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2022-05-11 09시41분
[차가워] 와우 고맙습니다.
서버단을 못 건드리는 상황에서 빠른 처리가 필요할때 정말 요긴합니다.
감사합니다.
[guest]
2022-05-11 10시06분
[차가워] 그런데 선생님 pc는 매우 빠르시네요.
저도 위의 코드를 돌려보니
77
17
나오네요.
제pc도 최신cpu인데 시간차 크네요.
[guest]
2022-05-11 10시20분
"인텔 코어i9-12세대 12900K"입니다. (별도 튜닝은 하지 않았습니다.)

2021년 새로운 PC
; https://www.sysnet.pe.kr/0/0/535

그나저나, 본문에서는 Debug 빌드로 한 것인데, Release 빌드로 돌리니 좀 더 빠르긴 하군요. ^^;

ConvertNormal : 33
ConvertToLong : 6
정성태
2022-05-11 11시16분
[차가워] 충격 !!
저도 12900K @5.2 고정
DDR4 3600 CL14

제가 두배 이상 늦네요.
문제가 뭘까요? ㅠㅠ
[guest]
2022-05-11 11시44분
[차가워] 선생님이랑 저랑 속도 차이가 심하게 나는데요.
혹시 avx 명령어랑 관련 있을가요?
[guest]
2022-05-12 09시13분
글쎄요, AVX 명령어라고 해도 어차피 JIT 컴파일러에서 그 명령어를 사용하도록 번역해야 하는데, 그런 환경이라면 @차가워 님이랑 별다를 바가 없을 듯합니다.

아마도, 보드 상에서 기본 적용된 CPU나 메모리의 최적화 변수가 다른 것이 아닐까요?
정성태
2022-05-12 07시17분
[차가워] 이상하네요.
12900k pc가 5대 있는데요. 5대 모두 테스트 해보니 연산 소요시간이 비슷하게 나옵니다.
12900k@5.4 ddr5@6000 cl28 이 pc가 73, 16으로 제일 잘 나옵니다.

 private void button4_Click(object sender, EventArgs e)
        {
            Main2();
        }

        void Main2()
        {
            Action<int, string, Func<byte[], long>, byte[]> action = (loopCount, title, work, arg) =>
            {
                Stopwatch st = new Stopwatch();
                st.Start();

                Random rand = new Random(Environment.TickCount);

                for (int i = 0; i < loopCount; i++)
                {
                    work(arg);
                }

                st.Stop();

                Console.WriteLine(title + " : " + st.ElapsedMilliseconds);
            };

            byte[] buf = Encoding.ASCII.GetBytes("1000000123");

            action(1, "touch-JIT", ConvertNormal, buf);
            action(1, "touch-JIT", ConvertToLong, buf);

            Console.WriteLine();

            action(1000000, "ConvertNormal", ConvertNormal, buf);
            action(1000000, "ConvertToLong", ConvertToLong, buf);
        }

        long ConvertNormal(byte[] bytes)
        {
            string tmp = Encoding.ASCII.GetString(bytes);
            return Convert.ToInt64(tmp);
        }

        long ConvertToLong(byte[] bytes)
        {
            long n = 1;
            long result = 0;

            for (int i = bytes.Length - 1; i >= 0; i--)
            {
                result = result + (bytes[i] - '0') * n;
                n *= 10;
            }

            return result;
        }
[guest]
2022-05-12 09시03분
@차가워 Release 빌드로 테스트 하신 거 맞나요? 제가 가진 "AMD Ryzen 7 PRO 4750G"로도 다음의 성능이 나옵니다.

ConvertNormal : 62
ConvertToLong : 9
정성태
2022-05-12 09시56분
[차가워] 12900k@5.4
ddr5@6000 cl28     
ASUS MAXIMUS Z690 EXTREME
e코어 전부 끔, 하이퍼스레딩 전부 끔

닷넷프레임워크 4.8
릴리즈모드, 64비트, 최적화체크
ConvertNormal : 67
ConvertToLong : 5

ConvertNormal 값이 선생님 보다 너무 안나오네요.
[guest]
2022-05-29 04시02분
[al6uiz] .NET Core, .NET 6이상을 사용한다면 Span과 Utf8Parser를 이용해도 성능은 비슷(아주 조금 느린)하지만 깔끔한 코드가 될 것 같네요.

private static long ConvertToLongSpan(byte[] bytes)
{
  Utf8Parser.TryParse(bytes.AsSpan(), out long value, out var count);
  return value;
}
[guest]
2022-05-29 11시09분
@al6uiz 그러게요, 제시하신 코드가 더 깔끔하군요. ^^
정성태

... 31  32  33  34  35  36  37  38  [39]  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12670정성태6/13/20219224.NET Framework: 1071. DLL Surrogate를 이용한 Out-of-process COM 개체에서의 CoInitializeSecurity 문제파일 다운로드1
12669정성태6/11/20219264.NET Framework: 1070. 사용자 정의 GetHashCode 메서드 구현은 C# 9.0의 record 또는 리팩터링에 맡기세요.
12668정성태6/11/202110973.NET Framework: 1069. C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작파일 다운로드2
12667정성태6/10/20219611.NET Framework: 1068. COM+ 서버 응용 프로그램을 이용해 CoInitializeSecurity 제약 해결파일 다운로드1
12666정성태6/10/20218159.NET Framework: 1067. 별도 DLL에 포함된 타입을 STAThread Main 메서드에서 사용하는 경우 CoInitializeSecurity 자동 호출파일 다운로드1
12665정성태6/9/20219490.NET Framework: 1066. Wslhub.Sdk 사용으로 알아보는 CoInitializeSecurity 사용 제약파일 다운로드1
12664정성태6/9/20217874오류 유형: 723. COM+ PIA 참조 시 "This operation failed because the QueryInterface call on the COM component" 오류
12663정성태6/9/20219441.NET Framework: 1065. Windows Forms - 속성 창의 디자인 설정 지원: 문자열 목록 내에서 항목을 선택하는 TypeConverter 제작파일 다운로드1
12662정성태6/8/20218471.NET Framework: 1064. C# COM 개체를 PIA(Primary Interop Assembly)로써 "Embed Interop Types" 참조하는 방법파일 다운로드1
12661정성태6/4/202119172.NET Framework: 1063. C# - MQTT를 이용한 클라이언트/서버(Broker) 통신 예제 [4]파일 다운로드1
12660정성태6/3/202110237.NET Framework: 1062. Windows Forms - 폼 내에서 발생하는 마우스 이벤트를 자식 컨트롤 영역에 상관없이 수신하는 방법 [1]파일 다운로드1
12659정성태6/2/202111457Linux: 40. 우분투 설치 후 MBR 디스크 드라이브 여유 공간이 인식되지 않은 경우 - Logical Volume Management
12658정성태6/2/20218891Windows: 194. Microsoft Store에 있는 구글의 공식 Youtube App
12657정성태6/2/202110191Windows: 193. 윈도우 패키지 관리자 - winget 설치
12656정성태6/1/20218452.NET Framework: 1061. 서버 유형의 COM+에 적용할 수 없는 Server GC
12655정성태6/1/20217883오류 유형: 722. windbg/sos - savemodule - Fail to read memory
12654정성태5/31/20217878오류 유형: 721. Hyper-V - Saved 상태의 VM을 시작 시 오류 발생
12653정성태5/31/202110565.NET Framework: 1060. 닷넷 GC에 새롭게 구현되는 DPAD(Dynamic Promotion And Demotion for GC)
12652정성태5/31/20218713VS.NET IDE: 164. Visual Studio - Web Deploy로 Publish 시 암호창이 매번 뜨는 문제
12651정성태5/31/20218944오류 유형: 720. PostgreSQL - ERROR: 22P02: malformed array literal: "..."
12650정성태5/17/20218254기타: 82. OpenTabletDriver의 버튼에 더블 클릭을 매핑 및 게임에서의 지원 방법
12649정성태5/16/20219596.NET Framework: 1059. 세대 별 GC(Garbage Collection) 방식에서 Card table의 사용 의미 [1]
12648정성태5/16/20218241사물인터넷: 66. PC -> FTDI -> NodeMCU v1 ESP8266 기기를 UART 핀을 연결해 직렬 통신하는 방법파일 다운로드1
12647정성태5/15/20219496.NET Framework: 1058. C# - C++과의 연동을 위한 구조체의 fixed 배열 필드 사용파일 다운로드1
12646정성태5/15/20218613사물인터넷: 65. C# - Arduino IDE의 Serial Monitor 기능 구현파일 다운로드1
12645정성태5/14/20218312사물인터넷: 64. NodeMCU v1 ESP8266 - LittleFS를 이용한 와이파이 접속 정보 업데이트파일 다운로드1
... 31  32  33  34  35  36  37  38  [39]  40  41  42  43  44  45  ...