성능을위한 동적 컴파일
-
20-09-2019 - |
문제
동적 코드 생성으로 성능을 향상시킬 수있는 방법에 대한 아이디어가 있지만이 문제에 접근하는 가장 좋은 방법이 확실하지 않습니다.
수업이 있다고 가정 해 봅시다
class Calculator
{
int Value1;
int Value2;
//..........
int ValueN;
void DoCalc()
{
if (Value1 > 0)
{
DoValue1RelatedStuff();
}
if (Value2 > 0)
{
DoValue2RelatedStuff();
}
//....
//....
//....
if (ValueN > 0)
{
DoValueNRelatedStuff();
}
}
}
DOCALC 방법은 가장 낮은 수준이며 계산 중에 여러 번 호출됩니다. 또 다른 중요한 측면은 Valuen이 처음에만 설정되어 계산 중에 변경되지 않는다는 것입니다. 많은 valuen이 0이므로 DOCALC 방법의 많은 IFS가 불필요합니다. 따라서 동적 코드 생성이 성능을 향상시키는 데 도움이되기를 바랐습니다.
예를 들어 메소드를 작성하는 경우
void DoCalc_Specific()
{
const Value1 = 0;
const Value2 = 0;
const ValueN = 1;
if (Value1 > 0)
{
DoValue1RelatedStuff();
}
if (Value2 > 0)
{
DoValue2RelatedStuff();
}
....
....
....
if (ValueN > 0)
{
DoValueNRelatedStuff();
}
}
그리고 C# 컴파일러를 켜는 최적화로 컴파일하는 것은 필요한 것들만 유지하기에 충분히 똑똑합니다. 따라서 Valuen 값을 기반으로 실행 시간에 그러한 방법을 만들고 계산 중에 생성 된 방법을 사용하고 싶습니다.
나는 그것을 위해 표현 나무를 사용할 수 있다고 생각하지만, 표현 나무는 단순한 람다 기능으로 만 작동하므로 기능 본문 내부에서 IF 등을 사용할 수 없습니다. 따라서이 경우이 방법을 적절한 방식으로 변경해야합니다.
또 다른 가능성은 필요한 코드를 문자열로 만들고 동적으로 컴파일하는 것입니다. 그러나 기존 방법을 취하고 그에 따라 수정할 수 있다면 훨씬 나을 것입니다.
반사도 있습니다.
BTW. 나는 c#에만 국한되지 않습니다. 그래서 저는 이런 종류의 문제에 가장 적합한 프로그래밍 언어의 제안에 개방적입니다. 몇 가지 이유로 LISP를 제외하고.
하나의 중요한 설명. DoValue1RelatedStuff ()는 내 알고리즘에서 메소드 호출이 아닙니다. 그것은 단지 일부 공식 기반 계산 일 뿐이며 매우 빠릅니다. 나는 이렇게 썼어 야 했어
if (Value1 > 0)
{
// Do Value1 Related Stuff
}
성능 테스트를 실행했으며 하나의 IF가 비활성화 된 경우 최적화 된 방법이 중복 IF보다 약 2 배 빠릅니다.
테스트에 사용한 코드는 다음과 같습니다.
public class Program
{
static void Main(string[] args)
{
int x = 0, y = 2;
var if_st = DateTime.Now.Ticks;
for (var i = 0; i < 10000000; i++)
{
WithIf(x, y);
}
var if_et = DateTime.Now.Ticks - if_st;
Console.WriteLine(if_et.ToString());
var noif_st = DateTime.Now.Ticks;
for (var i = 0; i < 10000000; i++)
{
Without(x, y);
}
var noif_et = DateTime.Now.Ticks - noif_st;
Console.WriteLine(noif_et.ToString());
Console.ReadLine();
}
static double WithIf(int x, int y)
{
var result = 0.0;
for (var i = 0; i < 100; i++)
{
if (x > 0)
{
result += x * 0.01;
}
if (y > 0)
{
result += y * 0.01;
}
}
return result;
}
static double Without(int x, int y)
{
var result = 0.0;
for (var i = 0; i < 100; i++)
{
result += y * 0.01;
}
return result;
}
}
해결책
나는 보통 그러한 최적화에 대해 생각조차하지 않을 것입니다. 얼마나 많은 일이 있습니다 DoValueXRelatedStuff()
하다? 10 ~ 50 개 이상의 프로세서주기? 예? 즉, 10% 미만의 실행 시간을 절약하기 위해 상당히 복잡한 시스템을 구축한다는 것을 의미합니다 (그리고 이것은 나에게 상당히 낙관적 인 것 같습니다). 이것은 쉽게 1%이하로 내려갈 수 있습니다.
다른 최적화의 여지가 없습니까? 더 나은 알고리즘? 단일 프로세서주기 만 취하는 단일 분기를 제거해야합니까 (분기 예측이 올바른 경우)? 예? .NET을 사용하는 대신 코드를 어셈블러로 작성하거나 다른 기계에 구체적이라고 생각하지 않아야합니까?
당신은의 순서를 줄 수 있습니까? N
, 일반적인 방법의 복잡성과 일반적으로 진실로 평가하는 표현의 비율?
다른 팁
당신이 실제로 코드 최적화에 빠지면 - 당신이 무엇이든하기 전에 - 프로파일 러를 실행하십시오! 병목 현상이 어디에 있고 어떤 영역이 최적화 할 가치가 있는지 보여줍니다.
또한 - 언어 선택이 제한되지 않으면 (LISP를 제외하고) 성능 측면에서 어셈블러를 이길 수는 없습니다.)
어셈블러를 사용하여 내부 기능을 다시 작성하여 성능 마법을 달성 한 것을 기억합니다.
무엇이든하기 전에 실제로 문제가 있습니까??
즉, 당신을 괴롭힐 정도로 오래 걸리나요?
그렇다면 실제로 시간이 걸리는 것이 무엇인지 알아보십시오. 당신이 추측하는 것이 아닙니다. 이것 시간이 어디로 가는지 보는 데 사용하는 빠르고 더럽고 효과적인 방법입니다.
이제 해석 대 컴파일에 대해 이야기하고 있습니다. 해석 된 코드는 일반적으로 컴파일 된 코드보다 1 배의 순서가 느립니다. 그 이유는 통역사가 다음에 무엇을 해야하는지 지속적으로 알아 내기 때문입니다. 그리고 잊어 버렸습니다, 컴파일 된 코드 그냥 알고 있습니다.
당신 이이 상황에 있다면, 컴파일 된 코드의 속도를 얻기 위해 번역 가격을 지불하는 것이 합리적 일 수 있습니다.