memset과 루프가 복수의 배열을 영점화하는 것 중 어느 것이 더 빠르거나 더 좋습니까?
double d[10];
int length = 10;
memset(d, length * sizeof(double), 0);
//or
for (int i = length; i--;)
d[i] = 0.0;
당신이 정말 신경 쓴다면 당신은 시도하고 측정해야 합니다.그러나 가장 휴대하기 쉬운 방법은 std::fill():
std::fill( array, array + numberOfElements, 0.0 );
memset의 경우, 이전의 C 함수이기 때문에 요소의 개수가 아닌 바이트의 개수를 전달해야 합니다.
memset(d, 0, sizeof(double)*length);
memset은 assembler로 작성되기 때문에 더 빠를 수 있지만, 반면에std::fill
는 단순히 내부적으로 루프를 수행하는 템플릿 함수입니다.
하지만 타입 안전과 좀 더 읽을 수 있는 코드를 위해서 추천합니다. std::fill()
- 그것은 일을 하는 c++ 방식이고, 고려합니다.memset
성능 최적화가 필요한 경우 코드에 포함됩니다.
멋지기 위해서라도 한번 써보세요 xD
{
double *to = d;
int n=(length+7)/8;
switch(length%8){
case 0: do{ *to++ = 0.0;
case 7: *to++ = 0.0;
case 6: *to++ = 0.0;
case 5: *to++ = 0.0;
case 4: *to++ = 0.0;
case 3: *to++ = 0.0;
case 2: *to++ = 0.0;
case 1: *to++ = 0.0;
}while(--n>0);
}
}
루프 길이가 적분 상수 표현이라고 가정하면, 가장 가능성 있는 결과는 좋은 최적화자가 for-loop과 memset(0)을 모두 인식할 것입니다.결과적으로 생성된 어셈블리는 본질적으로 동일합니다.레지스터의 선택이 다를 수도 있고 설정이 다를 수도 있습니다.하지만 두 배당 한계비용은 정말로 동일해야 합니다.
코드에 있는 여러 버그와 누락 외에 memset을 사용하는 것은 휴대가 불가능합니다.모든 0비트가 있는 두 배가 0.0이라고 가정할 수는 없습니다.먼저 코드를 올바르게 설정한 다음 최적화에 대해 고민합니다.
memset(d,0,10*sizeof(*d));
더 빠를 것 같습니다.당신도 할 수 있다고 하는 것처럼
std::fill_n(d,10,0.);
루프를 하는게 더 예쁜 방법일거에요
calloc(length, sizeof(double))
IEEE-754에 따르면, 양의 0의 비트 표현은 모두 0 비트이며, IEEE-754의 준수를 요구하는 것은 문제가 없습니다.(다시 사용하기 위해 어레이를 제로화해야 할 경우 위의 솔루션 중 하나를 선택합니다.)
IEEE 754-1975 64비트 부동 소수점에 대한 위키피디아 기사에 따르면 모든 0의 비트 패턴은 실제로 두 배를 0.0으로 적절하게 초기화합니다.안타깝게도 당신의 멤셋 코드는 그렇게 하지 못합니다.
사용해야 할 코드는 다음과 같습니다.
memset(d, 0, length * sizeof(double));
좀 더 완벽한 패키지의 일부로...
{
double *d;
int length = 10;
d = malloc(sizeof(d[0]) * length);
memset(d, 0, length * sizeof(d[0]));
}
물론, malloc의 반품 값에 대한 오류 검사를 수행해야 합니다.sizeof(d[0])
보다 약간 낫습니다.sizeof(double)
d형의 변화에 강하기 때문입니다.
그리고 만약 여러분이calloc(length, sizeof(d[0]))
그러면 메모리가 지워지고 그 이후의 멤셋은 더 이상 필요 없게 됩니다.예문에 사용하지 않은 것은 당신의 질문에 답이 없을 것 같아서 입니다.
Memset은 디버그 모드 또는 낮은 수준의 최적화를 사용하는 경우 항상 더 빠릅니다.더 높은 최적화 수준에서는 std::fill 또는 std::fill_n과 동일합니다.예를 들어, Google 벤치마크의 다음 코드의 경우: (테스트 설정: xubuntu 18, GCC 7.3, Clang 6.0)
#include <cstring>
#include <algorithm>
#include <benchmark/benchmark.h>
double total = 0;
static void memory_memset(benchmark::State& state)
{
int ints[50000];
for (auto _ : state)
{
std::memset(ints, 0, sizeof(int) * 50000);
}
for (int counter = 0; counter != 50000; ++counter)
{
total += ints[counter];
}
}
static void memory_filln(benchmark::State& state)
{
int ints[50000];
for (auto _ : state)
{
std::fill_n(ints, 50000, 0);
}
for (int counter = 0; counter != 50000; ++counter)
{
total += ints[counter];
}
}
static void memory_fill(benchmark::State& state)
{
int ints[50000];
for (auto _ : state)
{
std::fill(std::begin(ints), std::end(ints), 0);
}
for (int counter = 0; counter != 50000; ++counter)
{
total += ints[counter];
}
}
// Register the function as a benchmark
BENCHMARK(memory_filln);
BENCHMARK(memory_fill);
BENCHMARK(memory_memset);
int main (int argc, char ** argv)
{
benchmark::Initialize (&argc, argv);
benchmark::RunSpecifiedBenchmarks ();
printf("Total = %f\n", total);
getchar();
return 0;
}
GCC(-O2;-march= native)의 릴리스 모드에서 다음 결과를 제공합니다.
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
memory_filln 16488 ns 16477 ns 42460
memory_fill 16493 ns 16493 ns 42440
memory_memset 8414 ns 8408 ns 83022
그리고 다음과 같이 디버그 모드(-O0)가 됩니다.
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
memory_filln 87209 ns 87139 ns 8029
memory_fill 94593 ns 94533 ns 7411
memory_memset 8441 ns 8434 ns 82833
-O3에 있거나 -O2에 클랜이 있는 동안 다음을 얻을 수 있습니다.
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
memory_filln 8437 ns 8437 ns 82799
memory_fill 8437 ns 8437 ns 82756
memory_memset 8436 ns 8436 ns 82754
TLDR: 적어도 IEEE-754 부동 소수점이 아닌 POD 유형에 대해 std::fill 또는 for-loop을 사용해야 한다고 절대적으로 말하지 않는 한 memset을 사용합니다.하지 않을 강력한 이유는 없습니다.
(참고: 배열 내용을 계산하는 for loops는 클랜이 구글 벤치마크 루프를 완전히 최적화하지 않기 위해 필요합니다(그렇지 않으면 사용되지 않음을 감지합니다).
어레이에 메모리를 할당해야 하므로 예제가 작동하지 않습니다.스택 또는 힙에서 이 작업을 수행할 수 있습니다.
스택에서 이를 수행하는 예는 다음과 같습니다.
double d[50] = {0.0};
그 이후에는 멤셋이 필요 없습니다.
성능에 정말 관심이 있다면 루프에 적절하게 최적화된 제품을 비교하는 것을 잊지 마십시오.
배열이 충분히 길다면 Duff의 장치의 일부 변형, 접두사 --i not supplus i-- (대부분의 컴파일러가 자동으로 수정할 것입니다.)
이것이 최적화하기에 가장 가치 있는 것인지 의문이 들지만 말입니다.이것이 정말 시스템의 병목 현상입니까?
memset(d, 10, 0)은 10바이트만 널링하므로 올바르지 않습니다.prefer std::fill은 의도가 가장 명확하기 때문입니다.
일반적으로 멤셋은 훨씬 더 빨라질 것입니다. 길이를 정확히 맞추십시오. 분명히 예제에서 복식 배열을 할당하거나 정의하지 않았습니다.이제 정말로 몇 개의 2배로 끝나려면 루프가 더 빨라질 수 있습니다.그러나 채우기 루프가 그림자를 드리우는 지점에 도달함에 따라 소수의 설정 명령어 멤셋은 일반적으로 속도를 최대화하기 위해 더 크고 때로는 정렬된 청크를 사용합니다.
평소와 같이 시험하고 측정합니다.(그러나 이 경우에는 캐시에 저장되어 측정값이 거짓으로 판명될 수도 있습니다.)
이 질문에 답하는 한 가지 방법은 컴파일러 탐색기를 통해 코드를 빠르게 실행하는 것입니다.이 링크를 확인하면 다음 코드에 대한 어셈블리가 표시됩니다.
void do_memset(std::array<char, 1024>& a) {
memset(&a, 'q', a.size());
}
void do_fill(std::array<char, 1024>& a) {
std::fill(a.begin(), a.end(), 'q');
}
void do_loop(std::array<char, 1024>& a) {
for (int i = 0; i < a.size(); ++i) {
a[i] = 'q';
}
}
답은 (적어도 에 대해서는)clang
)는 최적화 수준을 가진 것입니다.-O0
그리고.-O1
, 조립은 다르며,std::fill
반복기의 사용이 최적화되지 않았기 때문에 속도가 느려질 것입니다.-O2
그리고 더 높은 곳에서do_memset
그리고.do_fill
동일한 조립체를 제작합니다.루프는 결국 전화를 걸게 됩니다.memset
배열의 모든 항목에 대해 다음과 같은 경우에도-O3
.
릴리스 빌드가 실행되는 경향이 있다고 가정합니다.-O2
성능에 대한 고려 사항은 없으며 다음을 사용할 것을 권장합니다.std::fill
가능할 때, 그리고memset
C에 대하여
STL을 사용하지 말아야 한다면...
double aValues [10];
ZeroMemory (aValues, sizeof(aValues));
ZeroMemory는 적어도 의도를 분명히 합니다.
제안된 모든 것에 대한 대안으로 시작 시 배열을 모든 0으로 설정하지 않는 것을 제안할 수 있습니다.대신 특정 셀의 값에 처음 액세스할 때만 값을 0으로 설정합니다.이렇게 하면 질문을 피할 수 있고 더 빠를 수 있습니다.
당신 말은
memset(d, 0, length * sizeof(d[0]))
그리고.
for (int i = length; --i >= 0; ) d[i] = 0;
개인적으로 둘 중 하나는 하지만, 아마std::fill()
아마 더 나을 겁니다.
언급URL : https://stackoverflow.com/questions/1373369/which-is-faster-preferred-memset-or-for-loop-to-zero-out-an-array-of-doubles
'programing' 카테고리의 다른 글
jQuery를 사용하여 Ajax를 통해 체크박스 배열의 값을 보내는 방법은? (0) | 2023.11.01 |
---|---|
translatable="false"가 있는 strings.xml의 "여기서 번역되지만 기본 로케일에서는 찾을 수 없습니다" 오류 (0) | 2023.11.01 |
파워셸에서 병렬로 태스크 실행 (0) | 2023.11.01 |
Ora-01427 단일 행 하위 쿼리가 두 개 이상의 행을 선택하여 반환하도록 수정하는 방법은 무엇입니까? (0) | 2023.10.27 |
PowerShell에서 Byte[] 만들기 (0) | 2023.10.27 |