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 그러게요, 제시하신 코드가 더 깔끔하군요. ^^
정성태

... 166  167  168  169  170  171  172  173  174  175  176  177  [178]  179  180  ...
NoWriterDateCnt.TitleFile(s)
536정성태9/12/200732358.NET Framework: 97. WCF : netTcpBinding에서의 각종 Timeout 값 설명 [11]
535정성태9/11/200729814.NET Framework: 96. WCF - PerSession에서의 클라이언트 연결 관리 [5]
534정성태9/3/200725315개발 환경 구성: 29. VHD 파일 크기 줄이기
533정성태9/2/200728021개발 환경 구성: 28. CA 서비스 - 사용자 정의 템플릿 유형 추가
532정성태9/2/200730555개발 환경 구성: 27. AD CA에서 Code Signing 인증서 유형 추가 방법
531정성태9/2/200726300.NET Framework: 95. WCF에서의 DataTable 사용
530정성태9/1/200722813.NET Framework: 94. WCF 예외에 대한 시행착오
529정성태8/31/200725707.NET Framework: 93. WCF - DataContract와 KnownType 특성 [1]
528정성태8/30/200720363오류 유형: 47. VPC - 네트워크 어댑터 MAC 주소 중복 오류
527정성태8/30/200730437Team Foundation Server: 20. 잠긴 파일을 강제로 해제 [2]
526정성태8/29/200720332오류 유형: 46. VS.NET 2008 - ASP.NET 디버깅 : Strong name validation failed.
525정성태8/27/200722573VS.NET IDE: 54. VS.NET 2008 - 새롭게 도입되는 XSD Schema Designer
524정성태8/23/200740077오류 유형: 45. 요청한 작업은, 사용자가 매핑한 구역이 열려 있는...
523정성태8/16/200722746VS.NET IDE: 53. VS.NET 2008 - 서비스 참조 시 기존 데이터 컨테이너 DLL 사용
522정성태8/13/200726378VS.NET IDE: 52. VS.NET 2008 - WCF를 위한 디버깅 환경 개선
521정성태8/8/200726386.NET Framework: 92. XmlSerializer 생성자의 실행 속도를 올리는 방법 - 두 번째 이야기 [3]
520정성태8/7/200721590VS.NET IDE: 51. Visual Studio 2008 베타 2 설치
519정성태7/27/200727966오류 유형: 44. System.BadImageFormatException [2]
518정성태7/26/200728985오류 유형: 43. System.ComponentModel.LicenseException [1]
517정성태7/19/200717287개발 환경 구성: 26. VPC - 일반 사용자 계정으로 구동
516정성태7/19/200720400오류 유형: 42. TFS - Error loading menu: Index was outside the bounds of the array [2]
515정성태7/18/200728137오류 유형: 41. SSL 서버 자격 증명을 만드는 동안 심각한 오류가 발생했습니다.
514정성태7/14/200720830Team Foundation Server: 19. Orcas에서 개선되는 TFS 기능들
513정성태7/4/200731805.NET Framework: 91. Foreground Thread / Background Thread [1]
512정성태6/27/200721731오류 유형: 40. error PRJ0050: Failed to register output.
511정성태6/25/200729741.NET Framework: 90. XmlSerializer 생성자의 실행 속도를 올리는 방법 [2]
... 166  167  168  169  170  171  172  173  174  175  176  177  [178]  179  180  ...