x86_64 및 AArch64의 알고리즘 선택 LAB 5 - 파트 1
안녕하세요 저는 Tecca입니다. 이 게시물에서는 AArch64 및 x86_64 시스템의 여러 구현에 걸쳐 동일한 시스템에서 다양한 알고리즘의 상대적인 성능을 비교할 것입니다.
소스 코드
vol.h
/* This is the number of samples to be processed */
#define SAMPLES 16
/* This is the volume scaling factor to be used */
#define VOLUME 50.0 // Percent of original volume
/* Function prototype to fill an array sample of
* length sample_count with random int16_t numbers
* to simulate an audio buffer */
void vol_createsample(int16_t* sample, int32_t sample_count);
vol.h는 처리할 샘플 수(16)와 사용할 볼륨 레벨(50)을 제어합니다.
vol.h에서는 알고리즘이 처리할 많은 수의 샘플이 합리적으로 보입니다. 성능 측면에서 차이를 훨씬 쉽게 분석할 수 있기 때문입니다.
vol0.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "vol.h"
int16_t scale_sample(int16_t sample, int volume) {
return (int16_t) ((float) (volume/100.0) * (float) sample);
}
int main() {
int x;
int ttl=0;
// ---- Create in[] and out[] arrays
int16_t* in;
int16_t* out;
in=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
out=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
// ---- Create dummy samples in in[]
vol_createsample(in, SAMPLES);
// ---- Scale the samples from in[], placing results in out[]
for (x = 0; x < SAMPLES; x++) {
out[x]=scale_sample(in[x], VOLUME);
}
// ---- This part sums the samples
for (x = 0; x < SAMPLES; x++) {
ttl=(ttl+out[x])%1000;
}
// ---- Print the sum of the samples
printf("Result: %d\n", ttl);
return 0;
}
vol0.c에서 오디오 샘플은 부호 있는 16비트 정수와 부동 소수점 값 사이에서 캐스팅하여 볼륨 배율 인수로 곱해집니다. 이 방법은 많은 리소스를 차지합니다.
vol1.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "vol.h"
int16_t scale_sample(int16_t sample, int volume) {
return ((((int32_t) sample) * ((int32_t) (32767 * volume / 100) <<1) ) >> 16);
}
int main() {
int x;
int ttl=0;
// ---- Create in[] and out[] arrays
int16_t* in;
int16_t* out;
in=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
out=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
// ---- Create dummy samples in in[]
vol_createsample(in, SAMPLES);
// ---- Scale the samples from in[], placing results in out[]
for (x = 0; x < SAMPLES; x++) {
out[x]=scale_sample(in[x], VOLUME);
}
// ---- This part sums the samples
for (x = 0; x < SAMPLES; x++) {
ttl=(ttl+out[x])%1000;
}
// ---- Print the sum of the samples
printf("Result: %d\n", ttl);
return 0;
}
vol1.c는 고정 소수점 계산을 사용합니다. 이렇게 하면 정수와 부동 소수점 사이를 반복적으로 캐스팅하는 비용을 피할 수 있습니다.
vol2.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "vol.h"
int main() {
int x;
int ttl=0;
// ---- Create in[] and out[] arrays
int16_t* in;
int16_t* out;
in=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
out=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
static int16_t* precalc;
// ---- Create dummy samples in in[]
vol_createsample(in, SAMPLES);
// ---- Scale the samples from in[], placing results in out[]
precalc = (int16_t*) calloc(65536,2);
if (precalc == NULL) {
printf("malloc failed!\n");
return 1;
}
for (x = -32768; x <= 32767; x++) {
precalc[(uint16_t) x] = (int16_t) ((float) x * VOLUME / 100.0);
}
for (x = 0; x < SAMPLES; x++) {
out[x]=precalc[(uint16_t) in[x]];
}
// ---- This part sums the samples
for (x = 0; x < SAMPLES; x++) {
ttl=(ttl+out[x])%1000;
}
// ---- Print the sum of the samples
printf("Result: %d\n", ttl);
return 0;
}
vol0.c 및 vol1.c와 달리 vol2.c는 65535개의 모든 결과를 미리 계산하고 나중에 각 입력 값에 대한 답을 찾습니다.
vol3.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "vol.h"
int16_t scale_sample(int16_t sample, int volume) {
return (int16_t) 100;
}
int main() {
int x;
int ttl=0;
// ---- Create in[] and out[] arrays
int16_t* in;
int16_t* out;
in=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
out=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
// ---- Create dummy samples in in[]
vol_createsample(in, SAMPLES);
// ---- Scale the samples from in[], placing results in out[]
for (x = 0; x < SAMPLES; x++) {
out[x]=scale_sample(in[x], VOLUME);
}
// ---- This part sum the samples
for (x = 0; x < SAMPLES; x++) {
ttl=(ttl+out[x])%1000;
}
// ---- Print the sum of the samples
printf("Result: %d\n", ttl);
return 0;
}
vol3.c는 동일한 샘플 값을 반환하므로 이 프로그램의 목적은 다른 스케일링 볼륨 알고리즘과 비교하기 위한 기준선으로 보입니다.
vol4.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "vol.h"
int main() {
#ifndef __aarch64__
printf("Wrong architecture - written for aarch64 only.\n");
#else
// these variables will also be accessed by our assembler code
int16_t* in_cursor; // input cursor
int16_t* out_cursor; // output cursor
int16_t vol_int; // volume as int16_t
int16_t* limit; // end of input array
int x; // array interator
int ttl=0 ; // array total
// ---- Create in[] and out[] arrays
int16_t* in;
int16_t* out;
in=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
out=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
// ---- Create dummy samples in in[]
vol_createsample(in, SAMPLES);
// ---- Scale the samples from in[], placing results in out[]
// set vol_int to fixed-point representation of the volume factor
vol_int = (int16_t)(VOLUME/100.0 * 32767.0);
in_cursor = in;
out_cursor = out;
limit = in + SAMPLES;
__asm__ ("dup v1.8h,%w0"::"r"(vol_int)); // duplicate vol_int into v1.8h
while ( in_cursor < limit ) {
__asm__ (
"ldr q0, [%[in_cursor]], #16 \n\t"
// load eight samples into q0 (same as v0.8h)
// from [in_cursor]
// post-increment in_cursor by 16 bytes
// ans store back into the pointer register
"sqrdmulh v0.8h, v0.8h, v1.8h \n\t"
// with 32 signed integer output,
// multiply each lane in v0 * v1 * 2
// saturate results
// store upper 16 bits of results into
// the corresponding lane in v0
"str q0, [%[out_cursor]],#16 \n\t"
// store eight samples to [out_cursor]
// post-increment out_cursor by 16 bytes
// and store back into the pointer register
: [in_cursor]"+r"(in_cursor), [out_cursor]"+r"(out_cursor)
: "r"(in_cursor),"r"(out_cursor)
: "memory"
);
}
// --------------------------------------------------------------------
for (x = 0; x < SAMPLES; x++) {
ttl=(ttl+out[x])%1000;
}
printf("Result: %d\n", ttl);
return 0;
#endif
}
vol4.c는 인라인 어셈블리를 통해 액세스되는 SIMD(Single Input, Multiple Data) 명령어를 사용합니다. AArch64 아키텍처에서만 사용할 수 있습니다.
vol5.c
#include <stdint.h>
#ifdef __aarch64__
#include <arm_neon.h>
#endif
#include "vol.h"
int main() {
#ifndef __aarch64__
printf("Wrong architecture - written for aarch64 only.\n");
#else
register int16_t* in_cursor asm("r20"); // input cursor (pointer)
register int16_t* out_cursor asm("r21"); // output cursor (pointer)
register int16_t vol_int asm("r22"); // volume as int16_t
int16_t* limit; // end of input array
int x; // array interator
int ttl=0; // array total
// ---- Create in[] and out[] arrays
int16_t* in;
int16_t* out;
in=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
out=(int16_t*) calloc(SAMPLES, sizeof(int16_t));
// ---- Create dummy samples in in[]
vol_createsample(in, SAMPLES);
// ---- Scale the samples from in[], placing results in out[]
vol_int = (int16_t) (VOLUME/100.0 * 32767.0);
in_cursor = in;
out_cursor = out;
limit = in + SAMPLES ;
while ( in_cursor < limit ) {
vst1q_s16(out_cursor, vqrdmulhq_s16(vld1q_s16(in_cursor), vdupq_n_s16(vol_int)));
in_cursor += 8;
out_cursor += 8;
}
// --------------------------------------------------------------------
for (x = 0; x < SAMPLES; x++) {
ttl=(ttl+out[x])%1000;
}
printf("Result: %d\n", ttl);
return 0;
#endif
}
vol4.c와 같은 vol5.c도 SIMD 명령을 사용하지만 컴파일러에 고유한 컴파일러가 내장되어 있습니다. vol5.c는 또한 AArch64 아키텍처의 고유한 명령을 사용하기 때문에 AArch64에만 해당됩니다.
위의 vol0.c에서 vol5.c까지의 코드는 성능을 테스트하는 데 사용할 다양한 알고리즘을 구현합니다.
vol_createsample.c
#include <stdlib.h>
#include <stdint.h>
#include "vol.h"
void vol_createsample(int16_t* sample, int32_t sample_count) {
int i;
for (i=0; i<sample_count; i++) {
sample[i] = (rand()%65536)-32768;
}
return;
}
vol_createsample.c에는 알고리즘을 실행할 더미 샘플을 만드는 데 사용할 vol_createsample(int16_t* sample, int32_t sample_count) 함수가 포함되어 있습니다.
결론
이 게시물에서는 볼륨 샘플을 조정하기 위한 몇 가지 알고리즘을 살펴보았습니다. 우리는 동일한 목표를 달성하는 접근 방식이 각 알고리즘이 어떻게 다른지 알고 있습니다. 다음 포스팅에서는 각 프로그램의 성능이 어떤지 알아보고 벤치마킹하여 그 기대치를 증명해보도록 하겠습니다.
Reference
이 문제에 관하여(x86_64 및 AArch64의 알고리즘 선택 LAB 5 - 파트 1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/pykedot/lab-5-1omi텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)