refine benchmarks
This commit is contained in:
parent
f639be16a0
commit
781601fe9e
15 changed files with 218279 additions and 123512 deletions
|
|
@ -52,31 +52,43 @@ def parse_cycle_deltas(file_path):
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description='Extract cycle deltas from trace files and calculate speedup')
|
parser = argparse.ArgumentParser(description='Extract cycle deltas from trace files and calculate speedup')
|
||||||
parser.add_argument('with_file', help='Path to the trace file (with optimization)')
|
parser.add_argument('files', nargs='+', help='Path to trace file(s). Last file is baseline if more than one.')
|
||||||
parser.add_argument('without_file', help='Path to the trace file (without optimization - baseline)')
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
try:
|
if len(args.files) == 2:
|
||||||
df_with = parse_cycle_deltas(args.with_file)
|
files = args.files
|
||||||
df_without = parse_cycle_deltas(args.without_file)
|
baseline_idx = 1
|
||||||
|
elif len(args.files) >= 3:
|
||||||
|
files = args.files
|
||||||
|
baseline_idx = len(files) - 1
|
||||||
|
else:
|
||||||
|
print("Error: At least 2 files required")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
if len(df_with) != len(df_without):
|
try:
|
||||||
raise ValueError(
|
dfs = [parse_cycle_deltas(f) for f in files]
|
||||||
f"Mismatch in number of deltas: {args.with_file} has {len(df_with)} deltas, "
|
baseline_df = dfs[baseline_idx]
|
||||||
f"but {args.without_file} has {len(df_without)} deltas. Cannot pair them."
|
|
||||||
)
|
for i, df in enumerate(dfs):
|
||||||
|
if len(df) != len(baseline_df):
|
||||||
|
raise ValueError(
|
||||||
|
f"Mismatch in number of deltas: {files[i]} has {len(df)} deltas, "
|
||||||
|
f"but {files[baseline_idx]} has {len(baseline_df)} deltas. Cannot pair them."
|
||||||
|
)
|
||||||
|
|
||||||
result = pd.DataFrame({
|
result = pd.DataFrame({
|
||||||
'start_cycle_with': df_with['start_cycle'],
|
'start_cycle_baseline': baseline_df['start_cycle'],
|
||||||
'end_cycle_with': df_with['end_cycle'],
|
'end_cycle_baseline': baseline_df['end_cycle'],
|
||||||
'delta_with': df_with['delta'],
|
'delta_baseline': baseline_df['delta']
|
||||||
'start_cycle_without': df_without['start_cycle'],
|
|
||||||
'end_cycle_without': df_without['end_cycle'],
|
|
||||||
'delta_without': df_without['delta'],
|
|
||||||
'speedup': df_without['delta'] / df_with['delta']
|
|
||||||
})
|
})
|
||||||
|
|
||||||
print("Cycle Delta Analysis:")
|
for i, df in enumerate(dfs):
|
||||||
|
if i == baseline_idx:
|
||||||
|
result[f'speedup_{i}'] = 1.0
|
||||||
|
else:
|
||||||
|
result[f'speedup_{i}'] = baseline_df['delta'] / df['delta']
|
||||||
|
|
||||||
|
print("Cycle Delta Analysis (baseline: " + files[baseline_idx] + "):")
|
||||||
print(result)
|
print(result)
|
||||||
|
|
||||||
result.to_parquet("result.parquet")
|
result.to_parquet("result.parquet")
|
||||||
|
|
|
||||||
43075
traces/with_czero.txt
43075
traces/with_czero.txt
File diff suppressed because it is too large
Load diff
44267
traces/with_zfa.txt
44267
traces/with_zfa.txt
File diff suppressed because it is too large
Load diff
16547
traces/with_zfhmin.txt
16547
traces/with_zfhmin.txt
File diff suppressed because it is too large
Load diff
20184
traces/with_zvfhmin.txt
Normal file
20184
traces/with_zvfhmin.txt
Normal file
File diff suppressed because it is too large
Load diff
62151
traces/without_czero.txt
62151
traces/without_czero.txt
File diff suppressed because it is too large
Load diff
48017
traces/without_zfa.txt
48017
traces/without_zfa.txt
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
24122
traces/without_zvfhmin.txt
Normal file
24122
traces/without_zvfhmin.txt
Normal file
File diff suppressed because it is too large
Load diff
20756
traces/without_zvfhmin_but_zfhmin.txt
Normal file
20756
traces/without_zvfhmin_but_zfhmin.txt
Normal file
File diff suppressed because it is too large
Load diff
456
zfa_micro/zfa.c
456
zfa_micro/zfa.c
|
|
@ -6,6 +6,30 @@
|
||||||
|
|
||||||
#define ZFA
|
#define ZFA
|
||||||
|
|
||||||
|
|
||||||
|
int32_t emulate_fcvtmod_w_d(double a) {
|
||||||
|
// 1. Trap NaNs and Infinities (Spec says they must return 0)
|
||||||
|
if (!isfinite(a)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Bring the double into a range that fits in a 64-bit int.
|
||||||
|
// We use fmod to strip away everything above 2^32.
|
||||||
|
// (2^32 is exactly 4294967296.0)
|
||||||
|
double truncated_range = fmod(a, 4294967296.0);
|
||||||
|
|
||||||
|
// 3. Convert to 64-bit integer with truncation (RTZ)
|
||||||
|
int64_t standard_int = (int64_t)truncated_range;
|
||||||
|
|
||||||
|
// 4. Cast to 32-bit signed integer (effectively your SLLI/SRAI)
|
||||||
|
return (int32_t)standard_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int baremetal_errno = 0;
|
||||||
|
int *__errno(void) {
|
||||||
|
return &baremetal_errno;
|
||||||
|
}
|
||||||
|
|
||||||
static inline uint64_t read_cycles() {
|
static inline uint64_t read_cycles() {
|
||||||
uint64_t start;
|
uint64_t start;
|
||||||
asm volatile ("rdcycle %0" : "=r"(start));
|
asm volatile ("rdcycle %0" : "=r"(start));
|
||||||
|
|
@ -13,197 +37,273 @@ static inline uint64_t read_cycles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// fround.s
|
// -- fround.s --
|
||||||
volatile float a;
|
{
|
||||||
volatile float b;
|
volatile float a, b;
|
||||||
volatile double c;
|
read_cycles();
|
||||||
volatile double d;
|
for (int i = 0; i < N; i++) {
|
||||||
|
a = 3.25f;
|
||||||
// fround.s
|
b = round(a);
|
||||||
read_cycles();
|
}
|
||||||
for (int i = 0; i < N; i++) {
|
read_cycles();
|
||||||
a = 3.25f;
|
|
||||||
b = round(a);
|
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
|
|
||||||
// fround.d
|
// -- fround.d --
|
||||||
read_cycles();
|
{
|
||||||
for (int i = 0; i < N; i++) {
|
volatile double a, b;
|
||||||
c = 3.25f;
|
read_cycles();
|
||||||
d = round(c);
|
for (int i = 0; i < N; i++) {
|
||||||
|
a = 3.25;
|
||||||
|
b = round(a);
|
||||||
|
}
|
||||||
|
read_cycles();
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
int res;
|
|
||||||
|
|
||||||
// fleq.s
|
// -- fleq.s --
|
||||||
|
{
|
||||||
read_cycles();
|
volatile float a = 3.25f, b = 3.0f;
|
||||||
for (int i = 0; i < N; i++) {
|
int res;
|
||||||
#ifndef ZFA
|
read_cycles();
|
||||||
asm volatile (
|
for (int i = 0; i < N; i++) {
|
||||||
"fclass.s t0, %1\n\t" // Classify a
|
#ifndef ZFA
|
||||||
"fclass.s t1, %2\n\t" // Classify b
|
asm volatile (
|
||||||
"or t0, t0, t1\n\t" // Combine classes
|
"fclass.s t0, %1\n\t"
|
||||||
"andi t2, t0, 0x200\n\t" // 0x200 is the mask for Quiet NaN
|
"fclass.s t1, %2\n\t"
|
||||||
"bnez t2, 1f\n\t" // If qNaN is present, skip to return 0
|
"or t0, t0, t1\n\t"
|
||||||
"fle.s %0, %1, %2\n\t" // Safe to use signaling comparison
|
"andi t2, t0, 0x200\n\t"
|
||||||
"j 2f\n\t"
|
"bnez t2, 1f\n\t"
|
||||||
"1:\n\t"
|
"fle.s %0, %1, %2\n\t"
|
||||||
"li %0, 0\n\t" // Result is false for NaNs
|
"j 2f\n\t"
|
||||||
"2:\n\t"
|
"1:\n\t"
|
||||||
: "=r" (res)
|
"li %0, 0\n\t"
|
||||||
: "f" (a), "f" (b)
|
"2:\n\t"
|
||||||
: "t0", "t1", "t2"
|
: "=r" (res)
|
||||||
);
|
: "f" (a), "f" (b)
|
||||||
|
: "t0", "t1", "t2"
|
||||||
#else
|
);
|
||||||
asm volatile("fleq.s t0, ft0, ft1");
|
#else
|
||||||
#endif
|
asm volatile ("fleq.s %0, %1, %2" : "=r" (res) : "f" (a), "f" (b));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
read_cycles();
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
|
|
||||||
// fleq.d
|
// -- fleq.d --
|
||||||
read_cycles();
|
{
|
||||||
for (int i = 0; i < N; i++) {
|
volatile double a = 3.25, b = 3.0;
|
||||||
#ifndef ZFA
|
int res;
|
||||||
asm volatile (
|
read_cycles();
|
||||||
"fclass.d t0, %1\n\t" // Classify double a
|
for (int i = 0; i < N; i++) {
|
||||||
"fclass.d t1, %2\n\t" // Classify double b
|
#ifndef ZFA
|
||||||
"or t0, t0, t1\n\t" // Combine classification masks
|
asm volatile (
|
||||||
"andi t2, t0, 0x200\n\t" // 0x200 is the bit for Quiet NaN (qNaN)
|
"fclass.d t0, %1\n\t"
|
||||||
"bnez t2, 1f\n\t" // If a qNaN is detected, skip to return 0
|
"fclass.d t1, %2\n\t"
|
||||||
"fle.d %0, %1, %2\n\t" // Signaling comparison: signals on sNaN, result in %0
|
"or t0, t0, t1\n\t"
|
||||||
"j 2f\n\t"
|
"andi t2, t0, 0x200\n\t"
|
||||||
"1:\n\t"
|
"bnez t2, 1f\n\t"
|
||||||
"li %0, 0\n\t" // Quietly return 0 (false) for qNaNs
|
"fle.d %0, %1, %2\n\t"
|
||||||
"2:\n\t"
|
"j 2f\n\t"
|
||||||
: "=r" (res)
|
"1:\n\t"
|
||||||
: "f" (a), "f" (b)
|
"li %0, 0\n\t"
|
||||||
: "t0", "t1", "t2"
|
"2:\n\t"
|
||||||
);
|
: "=r" (res)
|
||||||
|
: "f" (a), "f" (b)
|
||||||
#else
|
: "t0", "t1", "t2"
|
||||||
asm volatile ("fleq.d t0, ft0, ft1");
|
);
|
||||||
#endif
|
#else
|
||||||
|
asm volatile ("fleq.d %0, %1, %2" : "=r" (res) : "f" (a), "f" (b));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
read_cycles();
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
|
|
||||||
// fminm.s
|
// -- fminm.s --
|
||||||
float a_fmin = 0.0f, b_fmin = -0.0f;
|
{
|
||||||
float res_fmin;
|
volatile float a = 0.0f, b = -0.0f, res;
|
||||||
|
read_cycles();
|
||||||
read_cycles();
|
for (int i = 0; i < N; i++) {
|
||||||
for (int i = 0; i < N; i++) {
|
#ifndef ZFA
|
||||||
#ifndef ZFA
|
asm volatile (
|
||||||
asm volatile (
|
"fclass.s t0, %1\n\t"
|
||||||
"fclass.s t0, %1\n\t" // Classify a
|
"fclass.s t1, %2\n\t"
|
||||||
"fclass.s t1, %2\n\t" // Classify b
|
"li t2, 0x300\n\t"
|
||||||
"li t2, 0x300\n\t" // Mask for any NaN (0x100 sNaN | 0x200 qNaN)
|
"and t3, t0, t2\n\t"
|
||||||
"and t3, t0, t2\n\t" // t3 = is_nan(a)
|
"and t4, t1, t2\n\t"
|
||||||
"and t4, t1, t2\n\t" // t4 = is_nan(b)
|
"bnez t3, 1f\n\t"
|
||||||
"bnez t3, 1f\n\t" // If a is NaN, jump to handle it
|
"bnez t4, 2f\n\t"
|
||||||
"bnez t4, 2f\n\t" // If b is NaN, jump to handle it
|
"fmin.s %0, %1, %2\n\t"
|
||||||
"fmin.s %0, %1, %2\n\t" // Neither is NaN, use standard min
|
"j 3f\n\t"
|
||||||
"j 3f\n\t"
|
"1:\n\t"
|
||||||
"1:\n\t" // Case: a is NaN
|
"bnez t4, 4f\n\t"
|
||||||
"bnez t4, 4f\n\t" // If b is also NaN, jump to both-NaN case
|
"fmv.s %0, %2\n\t"
|
||||||
"fmv.s %0, %2\n\t" // a is NaN, b is number -> return b
|
"j 3f\n\t"
|
||||||
"j 3f\n\t"
|
"2:\n\t"
|
||||||
"2:\n\t" // Case: b is NaN, a is number -> return a
|
"fmv.s %0, %1\n\t"
|
||||||
"fmv.s %0, %1\n\t"
|
"j 3f\n\t"
|
||||||
"j 3f\n\t"
|
"4:\n\t"
|
||||||
"4:\n\t" // Case: Both are NaNs
|
"fmin.s %0, %1, %2\n\t"
|
||||||
"fmin.s %0, %1, %2\n\t" // Standard min handles both-NaNs correctly
|
"3:\n\t"
|
||||||
"3:\n\t"
|
: "=f" (res)
|
||||||
: "=f" (res_fmin)
|
: "f" (a), "f" (b)
|
||||||
: "f" (a_fmin), "f" (b_fmin)
|
: "t0", "t1", "t2", "t3", "t4"
|
||||||
: "t0", "t1", "t2", "t3", "t4"
|
);
|
||||||
);
|
#else
|
||||||
#else
|
asm volatile ("fminm.s %0, %1, %2" : "=f" (res) : "f" (a), "f" (b));
|
||||||
asm volatile ("fminm.s ft0, ft1, ft2");
|
#endif
|
||||||
#endif
|
}
|
||||||
|
read_cycles();
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
// fli.s
|
|
||||||
|
|
||||||
read_cycles();
|
// -- fminm.d --
|
||||||
volatile float res_fli_s[32];
|
{
|
||||||
for (int i = 0; i < N; i++) {
|
volatile double a = 0.0, b = -0.0, res;
|
||||||
res_fli_s[0] = -1.0f;
|
read_cycles();
|
||||||
res_fli_s[1] = -1.0f;
|
for (int i = 0; i < N; i++) {
|
||||||
res_fli_s[2] = 0x1p-16f;
|
#ifndef ZFA
|
||||||
res_fli_s[3] = 0x1p-15f;
|
asm volatile (
|
||||||
res_fli_s[4] = 0x1p-14f;
|
"fclass.d t0, %1\n\t"
|
||||||
res_fli_s[5] = 0x1p-13f;
|
"fclass.d t1, %2\n\t"
|
||||||
res_fli_s[6] = 0x1p-12f;
|
"li t2, 0x300\n\t"
|
||||||
res_fli_s[7] = 0x1p-11f;
|
"and t3, t0, t2\n\t"
|
||||||
res_fli_s[8] = 0x1p-10f;
|
"and t4, t1, t2\n\t"
|
||||||
res_fli_s[9] = 0x1p-9f;
|
"bnez t3, 1f\n\t"
|
||||||
res_fli_s[10] = 0x1p-8f;
|
"bnez t4, 2f\n\t"
|
||||||
res_fli_s[11] = 0x1p-7f;
|
"fmin.d %0, %1, %2\n\t"
|
||||||
res_fli_s[12] = 0x1p-6f;
|
"j 3f\n\t"
|
||||||
res_fli_s[13] = 0x1p-5f;
|
"1:\n\t"
|
||||||
res_fli_s[14] = 0x1p-4f;
|
"bnez t4, 4f\n\t"
|
||||||
res_fli_s[15] = 0x1p-3f;
|
"fmv.d %0, %2\n\t"
|
||||||
res_fli_s[16] = 0.25f;
|
"j 3f\n\t"
|
||||||
res_fli_s[17] = 0.5f;
|
"2:\n\t"
|
||||||
res_fli_s[18] = 0.75f;
|
"fmv.d %0, %1\n\t"
|
||||||
res_fli_s[19] = 1.0f;
|
"j 3f\n\t"
|
||||||
res_fli_s[20] = 1.25f;
|
"4:\n\t"
|
||||||
res_fli_s[21] = 1.5f;
|
"fmin.d %0, %1, %2\n\t"
|
||||||
res_fli_s[22] = 1.75f;
|
"3:\n\t"
|
||||||
res_fli_s[23] = 2.0f;
|
: "=f" (res)
|
||||||
res_fli_s[24] = 2.5f;
|
: "f" (a), "f" (b)
|
||||||
res_fli_s[25] = 3.0f;
|
: "t0", "t1", "t2", "t3", "t4"
|
||||||
res_fli_s[26] = 4.0f;
|
);
|
||||||
res_fli_s[27] = 8.0f;
|
#else
|
||||||
res_fli_s[28] = 16.0f;
|
asm volatile ("fminm.d %0, %1, %2" : "=f" (res) : "f" (a), "f" (b));
|
||||||
res_fli_s[29] = 32.0f;
|
#endif
|
||||||
res_fli_s[30] = INFINITY;
|
}
|
||||||
res_fli_s[31] = NAN;
|
read_cycles();
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
|
|
||||||
// fli.d
|
// -- fli.s --
|
||||||
read_cycles();
|
{
|
||||||
volatile double res_fli_d[32];
|
volatile float res[32];
|
||||||
for (int i = 0; i < N; i++) {
|
read_cycles();
|
||||||
res_fli_s[0] = -1.0f;
|
for (int i = 0; i < N; i++) {
|
||||||
res_fli_s[1] = -1.0f;
|
res[0] = -1.0f;
|
||||||
res_fli_s[2] = 0x1p-16f;
|
res[1] = -1.0f;
|
||||||
res_fli_s[3] = 0x1p-15f;
|
res[2] = 0x1p-16f;
|
||||||
res_fli_s[4] = 0x1p-14f;
|
res[3] = 0x1p-15f;
|
||||||
res_fli_s[5] = 0x1p-13f;
|
res[4] = 0x1p-14f;
|
||||||
res_fli_s[6] = 0x1p-12f;
|
res[5] = 0x1p-13f;
|
||||||
res_fli_s[7] = 0x1p-11f;
|
res[6] = 0x1p-12f;
|
||||||
res_fli_s[8] = 0x1p-10f;
|
res[7] = 0x1p-11f;
|
||||||
res_fli_s[9] = 0x1p-9f;
|
res[8] = 0x1p-10f;
|
||||||
res_fli_s[10] = 0x1p-8f;
|
res[9] = 0x1p-9f;
|
||||||
res_fli_s[11] = 0x1p-7f;
|
res[10] = 0x1p-8f;
|
||||||
res_fli_s[12] = 0x1p-6f;
|
res[11] = 0x1p-7f;
|
||||||
res_fli_s[13] = 0x1p-5f;
|
res[12] = 0x1p-6f;
|
||||||
res_fli_s[14] = 0x1p-4f;
|
res[13] = 0x1p-5f;
|
||||||
res_fli_s[15] = 0x1p-3f;
|
res[14] = 0x1p-4f;
|
||||||
res_fli_s[16] = 0.25f;
|
res[15] = 0x1p-3f;
|
||||||
res_fli_s[17] = 0.5f;
|
res[16] = 0.25f;
|
||||||
res_fli_s[18] = 0.75f;
|
res[17] = 0.5f;
|
||||||
res_fli_s[19] = 1.0f;
|
res[18] = 0.75f;
|
||||||
res_fli_s[20] = 1.25f;
|
res[19] = 1.0f;
|
||||||
res_fli_s[21] = 1.5f;
|
res[20] = 1.25f;
|
||||||
res_fli_s[22] = 1.75f;
|
res[21] = 1.5f;
|
||||||
res_fli_s[23] = 2.0f;
|
res[22] = 1.75f;
|
||||||
res_fli_s[24] = 2.5f;
|
res[23] = 2.0f;
|
||||||
res_fli_s[25] = 3.0f;
|
res[24] = 2.5f;
|
||||||
res_fli_s[26] = 4.0f;
|
res[25] = 3.0f;
|
||||||
res_fli_s[27] = 8.0f;
|
res[26] = 4.0f;
|
||||||
res_fli_s[28] = 16.0f;
|
res[27] = 8.0f;
|
||||||
res_fli_s[29] = 32.0f;
|
res[28] = 16.0f;
|
||||||
res_fli_s[30] = INFINITY;
|
res[29] = 32.0f;
|
||||||
res_fli_s[31] = NAN;
|
res[30] = INFINITY;
|
||||||
|
res[31] = NAN;
|
||||||
|
}
|
||||||
|
read_cycles();
|
||||||
}
|
}
|
||||||
read_cycles();
|
|
||||||
|
|
||||||
// fcvtmod.w.d
|
// -- fli.d --
|
||||||
|
|
||||||
|
{
|
||||||
|
volatile double res[32];
|
||||||
|
read_cycles();
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
res[0] = -1.0;
|
||||||
|
res[1] = -1.0;
|
||||||
|
res[2] = 0x1p-16;
|
||||||
|
res[3] = 0x1p-15;
|
||||||
|
res[4] = 0x1p-14;
|
||||||
|
res[5] = 0x1p-13;
|
||||||
|
res[6] = 0x1p-12;
|
||||||
|
res[7] = 0x1p-11;
|
||||||
|
res[8] = 0x1p-10;
|
||||||
|
res[9] = 0x1p-9;
|
||||||
|
res[10] = 0x1p-8;
|
||||||
|
res[11] = 0x1p-7;
|
||||||
|
res[12] = 0x1p-6;
|
||||||
|
res[13] = 0x1p-5;
|
||||||
|
res[14] = 0x1p-4;
|
||||||
|
res[15] = 0x1p-3;
|
||||||
|
res[16] = 0.25;
|
||||||
|
res[17] = 0.5;
|
||||||
|
res[18] = 0.75;
|
||||||
|
res[19] = 1.0;
|
||||||
|
res[20] = 1.25;
|
||||||
|
res[21] = 1.5;
|
||||||
|
res[22] = 1.75;
|
||||||
|
res[23] = 2.0;
|
||||||
|
res[24] = 2.5;
|
||||||
|
res[25] = 3.0;
|
||||||
|
res[26] = 4.0;
|
||||||
|
res[27] = 8.0;
|
||||||
|
res[28] = 16.0;
|
||||||
|
res[29] = 32.0;
|
||||||
|
res[30] = INFINITY;
|
||||||
|
res[31] = NAN;
|
||||||
|
}
|
||||||
|
read_cycles();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- fcvtmod.w.d --
|
||||||
|
{
|
||||||
|
static const double inputs[N] = {
|
||||||
|
2147483649.0, // 2^31 + 1 -> -2147483647 (mod 2^32)
|
||||||
|
-2147483649.0, // -2^31 - 1 -> 2147483647 (mod 2^32)
|
||||||
|
4294967297.0, // 2^32 + 1 -> 1 (mod 2^32)
|
||||||
|
-4294967297.0, // -2^32 - 1 -> -1 (mod 2^32)
|
||||||
|
3.25, // in-range
|
||||||
|
0.0, // exact zero
|
||||||
|
-0.0, // negative zero
|
||||||
|
INFINITY,
|
||||||
|
-INFINITY,
|
||||||
|
NAN
|
||||||
|
};
|
||||||
|
volatile double a;
|
||||||
|
int32_t res;
|
||||||
|
read_cycles();
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
a = inputs[i];
|
||||||
|
asm volatile("" : "+f"(a));
|
||||||
|
#ifndef ZFA
|
||||||
|
res = emulate_fcvtmod_w_d(a);
|
||||||
|
#else
|
||||||
|
asm volatile ("fcvtmod.w.d %0, %1, rtz" : "=r" (res) : "f" (a));
|
||||||
|
#endif
|
||||||
|
asm volatile("" : : "r"(res));
|
||||||
|
}
|
||||||
|
read_cycles();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
BIN
zfhmin.parquet
Normal file
BIN
zfhmin.parquet
Normal file
Binary file not shown.
|
|
@ -1,7 +1,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define N 10
|
#define N 100
|
||||||
|
|
||||||
static inline uint64_t read_cycles() {
|
static inline uint64_t read_cycles() {
|
||||||
uint64_t start;
|
uint64_t start;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#define N 128
|
#define N 128
|
||||||
#define ITERATIONS 1
|
#define ITERATIONS 1
|
||||||
|
|
||||||
// Static "messy" data to ensure the branch predictor cannot "learn" the pattern
|
// Static "messy" data to ensure the branch predictor cannot "learn" the pattern
|
||||||
static const uint64_t src_a[N] = {
|
static const uint64_t src_a[N] = {
|
||||||
0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1,
|
0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1,
|
||||||
|
|
@ -22,6 +21,17 @@ static const uint64_t src_b[N] = {
|
||||||
1130, 1140, 1150, 1160, 1170, 1180, 1190, 1200, 1210, 1220, 1230, 1240, 1250, 1260, 1270, 1280
|
1130, 1140, 1150, 1160, 1170, 1180, 1190, 1200, 1210, 1220, 1230, 1240, 1250, 1260, 1270, 1280
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint64_t src_c[N] = {
|
||||||
|
10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
|
||||||
|
170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290, 300, 310, 320,
|
||||||
|
330, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480,
|
||||||
|
490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640,
|
||||||
|
650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800,
|
||||||
|
810, 820, 830, 840, 850, 860, 870, 880, 890, 900, 910, 920, 930, 940, 950, 960,
|
||||||
|
970, 980, 990, 1000, 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090, 1100, 1110, 1120,
|
||||||
|
1130, 1140, 1150, 1160, 1170, 1180, 1190, 1200, 1210, 1220, 1230, 1240, 1250, 1260, 1270, 1280
|
||||||
|
};
|
||||||
|
|
||||||
volatile uint64_t results[N];
|
volatile uint64_t results[N];
|
||||||
|
|
||||||
static inline uint64_t read_cycles() {
|
static inline uint64_t read_cycles() {
|
||||||
|
|
@ -36,8 +46,19 @@ int main() {
|
||||||
for (int i = 0; i < N; i++) {
|
for (int i = 0; i < N; i++) {
|
||||||
uint64_t a = src_a[i];
|
uint64_t a = src_a[i];
|
||||||
uint64_t b = src_b[i];
|
uint64_t b = src_b[i];
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
results[i] = (a != 0) ? b : 0;
|
#ifdef ZICOND
|
||||||
|
__asm__ (
|
||||||
|
"czero.eqz %0, %1, %2"
|
||||||
|
: "=r" (res) // %0: output
|
||||||
|
: "r" (b), "r" (a) // %1: rs1, %2: rs2
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
res = (a == 0) ? b : 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
results[i] = res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_cycles();
|
read_cycles();
|
||||||
|
|
@ -47,10 +68,52 @@ int main() {
|
||||||
for (int i = 0; i < N; i++) {
|
for (int i = 0; i < N; i++) {
|
||||||
uint64_t a = src_a[i];
|
uint64_t a = src_a[i];
|
||||||
uint64_t b = src_b[i];
|
uint64_t b = src_b[i];
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
results[i] = (a != 0) ? b : 0;
|
#ifdef ZICOND
|
||||||
|
__asm__ (
|
||||||
|
"czero.nez %0, %1, %2"
|
||||||
|
: "=r" (res) // %0: output
|
||||||
|
: "r" (b), "r" (a) // %1: rs1, %2: rs2
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
res = (a != 0) ? b : 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
results[i] = res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
read_cycles();
|
||||||
|
read_cycles();
|
||||||
|
|
||||||
|
for (int j = 0; j < ITERATIONS; j++) {
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
uint64_t a = src_b[i];
|
||||||
|
uint64_t b = src_c[i];
|
||||||
|
uint64_t c = src_a[i];
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
|
#ifdef ZICOND
|
||||||
|
uint64_t val_if_true = a | b;
|
||||||
|
uint64_t val_if_false = a & b;
|
||||||
|
uint64_t tmp1, tmp2;
|
||||||
|
|
||||||
|
__asm__ (
|
||||||
|
"czero.eqz %0, %3, %2\n\t" // If c != 0, %0 = val_if_true, else 0
|
||||||
|
"czero.nez %1, %4, %2\n\t" // If c == 0, %1 = val_if_false, else 0
|
||||||
|
"or %0, %0, %1" // Combine them
|
||||||
|
: "=&r" (res), "=&r" (tmp2)
|
||||||
|
: "r" (c), "r" (val_if_true), "r" (val_if_false)
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
// Standard C - likely to compile to a branch if not optimized
|
||||||
|
res = (c != 0) ? (a | b) : (a & b);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
results[i] = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
read_cycles();
|
read_cycles();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -17,21 +17,17 @@ static inline uint64_t read_cycles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void benchmark() {
|
void benchmark() {
|
||||||
// 1. Widening: _Float16 -> float
|
read_cycles();
|
||||||
uint64_t t0 = read_cycles();
|
|
||||||
for (int i = 0; i < N; i++) {
|
for (int i = 0; i < N; i++) {
|
||||||
b[i] = (float)a[i];
|
b[i] = (float)a[i];
|
||||||
}
|
}
|
||||||
uint64_t t1 = read_cycles();
|
read_cycles();
|
||||||
|
|
||||||
// 2. Narrowing: float -> _Float16
|
read_cycles();
|
||||||
uint64_t t2 = read_cycles();
|
|
||||||
for (int i = 0; i < N; i++) {
|
for (int i = 0; i < N; i++) {
|
||||||
a[i] = (_Float16)b[i];
|
a[i] = (_Float16)b[i];
|
||||||
}
|
}
|
||||||
uint64_t t3 = read_cycles();
|
read_cycles();
|
||||||
|
|
||||||
// In a real app, print (t1-t0) and (t3-t2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue