DevSight

C#, 고성능 Tag Search

태그(tag) 조회 성능을 위해 Aho-Corasick 알고리즘을 사용하는데 이를 위해 .NET 9 이상에서 제공하는 SearchValues<string> 함수를 이용하는 예제이다.

최적화 핵심은 그룹화, 길이 내림차순 정렬, FrozenDictionary (읽기 전용 최적화 컬렉션) 자료구조이다. IndexOfAny로 특정 단어로 시작하는 단어들만 루프를 돌게 한다. Greedy Matching (긴 단어 우선), ReadOnlySpan<char>으로 메모리(GC) 최적화 등을 포함한다.

성능을 위해 Parallel.ForEach로 문서를 문단 단위로 나누어 병렬처리할 수 있다.

유의어(Alias) 사전 운영은 {"ID": 1, "Name": "LG", "Aliases": ["LG전자", "LG디스플레이", "LGCNS", "LG CNS"]}으로 관리하고, SearchValues 엔진에는 Aliases에 있는 모든 단어를 다 때려 넣고 무엇이 걸리든 결과는 "ID": 1(LG)로 리턴하게 만드는 방식을 사용한다.

Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;  
using System.Collections.Generic;  
using System.Threading.Tasks;  
  
namespace ConsoleApp;  
  
internal class Program  
{  
    private static async Task Main()  
    {  
        // tag dictionary는 DB에서 불러온다고 가정  
        string[] myDictionary =  
            ["삼성전자", "현대차", "LG", "SK하이닉스", "C#", ".NET", "삼성"];  
  
        const string document = "삼성전자는 이번 분기 실적 발표에서 현대차와의 협업을 발표했습니다. 또한 C#과 NET 환경에서의 소프트웨어 최적화가 중요해지고 있으며, SK하이닉스의 반도체 기술도 언급되었습니다."; 
  
        // ~ 10,000 태그 가정  
        TagProcessor tagProcessor = new(myDictionary);  
        List<string> tags = tagProcessor.ExtractAllTags(document);  
        Console.WriteLine($"추출된 태그 개수 : {tags.Count}");  
  
        foreach (string tag in tags)  
        {  
            Console.WriteLine($"- {tag}");  
        }  
  
        Console.WriteLine("----------------------------");  
  
        // 10,000 ~ 100,000개 태그 가정  
        Console.WriteLine("태그 추출 시작 ...");  
          
        List<string> extractTags = await Task.Run(() =>  
        {  
            HighDensityTagProcessor hightProcessor = new(myDictionary);  
            return hightProcessor.ExtractTags(document);  
        });  
          
        Console.WriteLine($"추출된 태그 개수(High) : {extractTags.Count}");  
  
        foreach (string tag in extractTags)  
        {  
            Console.WriteLine($"- {tag}");  
        }  
          
        Console.WriteLine("태그 추출 완료");  
    }  
}
Read More ···