programing

컴파일러가 reloc을 최적화하는 것이 허용됩니까?

codeshow 2023. 9. 9. 10:23
반응형

컴파일러가 reloc을 최적화하는 것이 허용됩니까?

불필요한 전화를 걸면 도움이 되는 상황을 발견했습니다.realloc최적화되어 있습니다.그러나 Clang과 GCC 중 어느 쪽도 그런 일을 하지 않는 것 같습니다(Compiler Explorer(godbolt.org ) - 여러 번 통화하면서 최적화가 이루어지고 있는 것을 볼 수는 있습니다.malloc.

예:

void *myfunc() {
    void *data;
    data = malloc(100);
    data = realloc(data, 200);
    return data;
}

다음과 같은 것에 최적화될 것으로 예상했습니다.

void *myfunc() {
    return malloc(200);
}

왜 Clang도 GCC도 그것을 최적화하지 않는 것일까요? - 그들은 그렇게 하는 것이 허용되지 않는 것일까요?

그들은 그렇게 하는 것이 허용되지 않습니까?

그럴 수도 있지만, 이 경우 최적화가 이루어지지 않은 것은 코너 기능의 차이 때문일 수도 있습니다.


남아 , 150 한 가 의 ,
data = malloc(100); data = realloc(data, 200);NULL100바이트가 소비(및 누출)되고 50바이트가 남아 있습니다.

data = malloc(200);NULL0바이트가 소모되고(누설된 바이트 수) 150개가 남아 있습니다.

이 좁은 경우에 다른 기능을 사용하면 최적화를 방해할 수 있습니다.


컴파일러가 재할당 최적화를 허용합니까?

아마도 - 나는 그것이 허용되기를 기대합니다.그러나 컴파일러가 언제 가능한지를 결정하기 위해 컴파일러를 향상시키는 것은 효과적이지 않을 수 있습니다.

인 인.malloc(n); ... realloc(p, 2*n)다와 차이가 malloc(2*n);...메모리 일부를 설정했을 수도 있습니다.

그 컴파일러의 설계 범위를 넘어서서...를 설정하지 않았습니다. , 가 를 하지 하지 를 가

malloc/calloc/free/realloc의 자체 포함 버전을 번들로 제공하는 컴파일러는 저자들이 그렇게 하는 것이 노력할 가치가 있다고 생각한다면 표시된 최적화를 합법적으로 수행할 수 있을 것입니다.외부에서 공급된 함수에 연결하는 컴파일러는 이러한 함수에 대한 정확한 호출 순서를 관측 가능한 부작용으로 간주하지 않는다는 것을 문서화할 경우에도 여전히 이러한 최적화를 수행할 수 있지만, 그러한 처리는 다소 제한적일 수 있습니다.

malloc()와 realoc() 사이에 할당되거나 할당 해제된 저장소가 없고, malloc()가 수행될 때 realoc()의 크기가 알려지며, realoc()의 크기가 malloc()의 크기보다 더 큰 경우, malloc() 작업과 realoc() 작업을 하나의 더 큰 할당으로 통합하는 것이 타당할 수 있습니다.그러나 메모리 상태가 그 사이에 변경될 수 있는 경우 이러한 최적화는 성공해야 할 작업의 실패를 초래할 수 있습니다.예를 들어, 다음과 같은 순서가 주어졌을 때:

void *p1 = malloc(2000000000);
void *p2 = malloc(2);
free(p1);
p2 = realloc(p2, 2000000000);

시스템은 p1이 해제된 후까지 p2에 대해 2000000 바이트를 사용할 수 없을 수 있습니다.코드를 다음과 같이 변경할 경우:

void *p1 = malloc(2000000000);
void *p2 = malloc(2000000000);
free(p1);

p2의 할당이 실패하는 결과를 초래할 것입니다.이 기준서는 배분요구가 성공할 것이라는 보장을 전혀 하지 않기 때문에 그러한 행태가 부적합하지는 않을 것입니다.한편, 다음은 "적합" 구현이기도 합니다.

void *malloc(size_t size) { return 0; }
void *calloc(size_t size, size_t count) { return 0; }
void free(void *p) {  }
void *realloc(void *p, size_t size) { return 0; }

이러한 구현은 거의 틀림없이 대부분의 다른 구현들보다 더 "효율적"으로 간주될 수 있지만, 위의 함수들이 실행되지 않는 코드 경로에서 호출되는 드문 상황을 제외하고는 매우 유용하다고 간주하기에는 다소 둔감해야 할 것입니다.

적어도 원래 질문에 나온 것처럼 간단한 경우에는 이 표준이 최적화를 분명히 허용할 것이라고 생각합니다.그렇지 않으면 성공할 수도 있었던 작업이 실패로 이어질 수도 있는 경우에도 표준은 이를 허용합니다.대부분의 컴파일러가 최적화 작업을 수행하지 않는 이유는 저자들이 그 이점이 안전하고 유용한 경우를 식별하는 데 필요한 노력을 정당화하기에 충분하지 않다고 생각했기 때문일 것입니다.

컴파일러는 순수 함수로 간주되는 함수, 즉 부작용이 없는 함수에 대한 여러 호출을 최적화할 수 있습니다.

그래서 문제는 그들이realloc()순함수인지 아닌지를 나타냅니다.

초안 은 C11 N1570 과 합니다 합니다 과 realloc함수:

7.22.3.5 realoc 함수 ... 2. Thereallocfunction은 ptr이 가리키는 이전 개체의 할당을 해제하고 크기가 지정된 새 개체로 포인터를 반환합니다.새 개체의 내용물은 할당 해제 전의 이전 개체의 내용물과 동일해야 하며, 새 개체와 새 개체의 크기 중 작은 크기를 사용해야 합니다.이전 개체의 크기를 초과하는 새 개체의 모든 바이트에는 불확정한 값이 있습니다.

리턴즈 4. 더realloc함수는 새 개체에 대한 포인터(이전 개체에 대한 포인터와 동일한 값을 가질 수 있음)를 반환하거나 새 개체를 할당할 수 없는 경우 null 포인터를 반환합니다.

컴파일러는 각 호출에서 반환될 컴파일 시간에 포인터의 값을 예측할 수 없습니다.

이 말은realloc()순수 함수로 간주될 수 없으며, 컴파일러에 의해 함수에 대한 여러 호출이 최적화될 수 없습니다.

그러나 두 번째 realoc()에서 사용 중인 첫 번째 malloc()의 반환 값을 확인하지 않습니다.NULL일 수도 있습니다.

컴파일러가 첫 번째 호출의 반환 값에 대한 부당한 가정을 하지 않고 두 호출을 하나의 호출로 최적화할 수 있었던 방법은 무엇입니까?

그리고 또 다른 가능한 시나리오가 있습니다.FreeBSD는 과거에realloc()기본적으로 malloc + memcpy + free old pointer였습니다.

사용 가능한 메모리가 230바이트밖에 남지 않았다고 가정해 보겠습니다.그 구현에서,ptr = malloc(100)뒤를realloc(ptr, 200)실패하겠지만, 단 한명의malloc(200)성공할 것입니다.

내가 이해하는 것은 그러한 최적화가 금지될 수도 있다는 것입니다. (특히 실제로는 불가능한 경우에)malloc성공을 하지만 다음과 같습니다.realloc실패).

당신은 그렇게 추측할 수 있습니다.malloc그리고.realloc항상 성공합니다(이것은 C11 표준에 어긋납니다, n1570; 나의 농담 구현도 살펴보세요).이 가설(틀린 것을 감지하기에는 엄격하지만 일부 Linux 시스템에는 메모리 오버커밋이 있어 이 환상을 제공합니다)에서 GCC를 사용하는 경우 이러한 최적화를 수행하기 위해 자신의 GCC 플러그인을 작성할 수 있습니다.

나는 그러한 GCC 플러그인을 코딩하는 데 몇 주 또는 몇 달을 소비할 가치가 있는지 확신할 수 없습니다. (실제로, 당신은 아마도 그것이 때때로 다음과 같은 코드를 처리하기를 원할 것입니다.malloc그리고.realloc, 그런 다음, 그 중간 코드가 무엇인지를 특성화하고 감지해야 하기 때문에 그렇게 간단하지는 않지만, 그 선택은 당신의 몫입니다.

언급URL : https://stackoverflow.com/questions/53373421/are-compilers-allowed-to-optimize-out-realloc

반응형