Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

common/trinary: Accelerate trit tryte conversion with SIMD SSE4.2 #1443

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion common/trinary/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ cc_library(
],
)

cc_library(
name = "trit_tryte_sse42",
hdrs = ["trit_tryte_sse42.h"],
deps = [
":tryte",
"//common:defs",
],
)

cc_library(
name = "trits",
hdrs = ["trits.h"],
Expand Down Expand Up @@ -102,7 +111,10 @@ cc_library(
cc_library(
name = "trit_tryte",
srcs = ["trit_tryte.c"],
hdrs = ["trit_tryte.h"],
hdrs = [
"trit_tryte.h",
"trit_tryte_sse42.h",
],
deps = [
":trits",
":tryte",
Expand Down
8 changes: 8 additions & 0 deletions common/trinary/benchmark/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cc_test(
name = "bench_trit_tryte",
timeout = "short",
srcs = ["bench_trit_tryte.c"],
deps = [
"//common/trinary:trit_tryte",
],
)
127 changes: 127 additions & 0 deletions common/trinary/benchmark/bench_trit_tryte.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2020 IOTA Stiftung
* https://github.com/iotaledger/entangled
*
* Refer to the LICENSE file for licensing information
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "common/trinary/trit_tryte.h"

// The minimum and maximum input/output tryte size for perfomance testing
#define MIN_TRYTE_SIZE 16
#define MAX_TRYTE_SIZE 2048
// The number of times of the same input size testing
#define TEST_TIMES 20

tryte_t tryte_chars[27] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '9'};
trit_t trit_nums[3] = {-1, 0, 1};

long diff_in_nanosec(struct timespec start, struct timespec end) {
struct timespec diff;

if (end.tv_nsec - start.tv_nsec < 0) {
diff.tv_sec = end.tv_sec - start.tv_sec - 1;
diff.tv_nsec = end.tv_nsec - start.tv_nsec + 1000000000;
} else {
diff.tv_sec = end.tv_sec - start.tv_sec;
diff.tv_nsec = end.tv_nsec - start.tv_nsec;
}

return (diff.tv_sec * 1000000000 + diff.tv_nsec);
}

void test_trits_to_trytes(unsigned int trit_size, unsigned int times) {
struct timespec start, end;
long run_time;
long min = 0, max = 0, sum = 0;

trit_t *trits = malloc(sizeof(trit_t) * trit_size);
tryte_t *trytes = malloc(sizeof(tryte_t) * trit_size / 3);

for (unsigned int count = 0; count < times; count++) {
// Generate random trits
for (unsigned int idx = 0; idx < trit_size; idx++) {
memset(trits + idx, trit_nums[rand() % 3], 1);
}

// Execution time measurement
clock_gettime(CLOCK_MONOTONIC, &start);
trits_to_trytes(trits, trytes, trit_size);
clock_gettime(CLOCK_MONOTONIC, &end);
run_time = diff_in_nanosec(start, end);

max = (count == 0 || run_time > max) ? run_time : max;
min = (count == 0 || run_time < min) ? run_time : min;
sum += run_time;
}

printf("Input trit size: %d\n", trit_size);
printf(" minimum: %ld nsec\n", min);
printf(" maximum: %ld nsec\n", max);
printf(" average: %ld nsec\n", sum / times);

free(trits);
free(trytes);
}

void test_trytes_to_trits(unsigned int tryte_size, unsigned int times) {
struct timespec start, end;
long run_time;
long min = 0, max = 0, sum = 0;

tryte_t *trytes = malloc(sizeof(tryte_t) * tryte_size);
trit_t *trits = malloc(sizeof(trit_t) * 3 * tryte_size);

for (unsigned int count = 0; count < times; count++) {
// Generate random trytes
for (unsigned int idx = 0; idx < tryte_size; idx++) {
memset(trytes + idx, tryte_chars[rand() % 27], 1);
}

// Execution time measurement
clock_gettime(CLOCK_MONOTONIC, &start);
trytes_to_trits(trytes, trits, tryte_size);
clock_gettime(CLOCK_MONOTONIC, &end);
run_time = diff_in_nanosec(start, end);

max = (count == 0 || run_time > max) ? run_time : max;
min = (count == 0 || run_time < min) ? run_time : min;
sum += run_time;
}

printf("Input tryte size: %d\n", tryte_size);
printf(" minimum: %ld nsec\n", min);
printf(" maximum: %ld nsec\n", max);
printf(" average: %ld nsec\n", sum / times);

free(trytes);
free(trits);
}

int main(void) {
unsigned int size;

// Set random seed
srand(time(NULL));

printf("trytes_to_trits\n");

for (size = MIN_TRYTE_SIZE; size <= MAX_TRYTE_SIZE; size++) {
test_trytes_to_trits(size, TEST_TIMES);
}

printf("\n");
printf("trits_to_trytes\n");

for (size = MIN_TRYTE_SIZE; size <= MAX_TRYTE_SIZE; size++) {
test_trits_to_trytes(size * 3, TEST_TIMES);
}

return 0;
}
41 changes: 33 additions & 8 deletions common/trinary/tests/test_trit_tryte.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,47 @@
#include <unity/unity.h>

#include "common/trinary/trit_tryte.h"
#include "common/trinary/trit_tryte_sse42.h"

#define TRYTES_IN "AZN9"
#define EXP 1, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, 0
#define TRYTES_IN "AZN9AZN9AZN9AZN9AZN9"
#define EXP \
1, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, \
0, 1, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, -1, -1, -1, 0, 0, 0

void test_trits_to_trytes(void) {
trit_t trits[] = {EXP};
tryte_t trytes[4];
tryte_t trytes[20];
tryte_t exp[] = {TRYTES_IN};
trits_to_trytes(trits, trytes, 12);
TEST_ASSERT_EQUAL_MEMORY(exp, trytes, 4);
trits_to_trytes(trits, trytes, 60);
TEST_ASSERT_EQUAL_MEMORY(exp, trytes, 20);
}

void test_trytes_to_trits(void) {
tryte_t trytes[] = {TRYTES_IN};
trit_t trits[12];
trit_t trits[60];
trit_t exp[] = {EXP};
trytes_to_trits(trytes, trits, 4);
TEST_ASSERT_EQUAL_MEMORY(exp, trits, 12);
trytes_to_trits(trytes, trits, 20);
TEST_ASSERT_EQUAL_MEMORY(exp, trits, 60);
}

#if defined(__SSE4_2__)
void test_trits_to_trytes_sse42(void) {
trit_t trits[] = {EXP};
tryte_t trytes[20];
tryte_t exp[] = {TRYTES_IN};
trits_to_trytes_sse42(trits, trytes, 60);
TEST_ASSERT_EQUAL_MEMORY(exp, trytes, 20);
}

void test_trytes_to_trits_sse42(void) {
tryte_t trytes[] = {TRYTES_IN};
trit_t trits[60];
trit_t exp[] = {EXP};
trytes_to_trits_sse42(trytes, trits, 20);
TEST_ASSERT_EQUAL_MEMORY(exp, trits, 60);
}
#endif

void test_get_trit_at(void) {
tryte_t trytes[] = {TRYTES_IN};
trit_t trit;
Expand Down Expand Up @@ -57,6 +78,10 @@ int main(void) {

RUN_TEST(test_trits_to_trytes);
RUN_TEST(test_trytes_to_trits);
#if defined(__SSE4_2__)
RUN_TEST(test_trits_to_trytes_sse42);
RUN_TEST(test_trytes_to_trits_sse42);
#endif
RUN_TEST(test_get_trit_at);
RUN_TEST(test_set_trit_at);

Expand Down
24 changes: 19 additions & 5 deletions common/trinary/trit_tryte.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
#include <string.h>

#include "common/trinary/trit_tryte.h"

static const trit_t TRYTES_TRITS_LUT[TRYTE_SPACE_SIZE][NUMBER_OF_TRITS_IN_A_TRYTE] = {
{0, 0, 0}, {1, 0, 0}, {-1, 1, 0}, {0, 1, 0}, {1, 1, 0}, {-1, -1, 1}, {0, -1, 1}, {1, -1, 1}, {-1, 0, 1},
{0, 0, 1}, {1, 0, 1}, {-1, 1, 1}, {0, 1, 1}, {1, 1, 1}, {-1, -1, -1}, {0, -1, -1}, {1, -1, -1}, {-1, 0, -1},
{0, 0, -1}, {1, 0, -1}, {-1, 1, -1}, {0, 1, -1}, {1, 1, -1}, {-1, -1, 0}, {0, -1, 0}, {1, -1, 0}, {-1, 0, 0}};
#if defined(__SSE4_2__)
#include "common/trinary/trit_tryte_sse42.h"
#endif

trit_t get_trit_at(tryte_t const *const trytes, size_t const length, size_t const index) {
size_t tindex = index / 3U;
Expand Down Expand Up @@ -50,6 +48,16 @@ uint8_t set_trit_at(tryte_t *const trytes, size_t const length, size_t const ind
}

void trits_to_trytes(trit_t const *const trits, tryte_t *const trytes, size_t const length) {
if (length == 0) {
return;
}

#if defined(__SSE4_2__)
if (length >= TRITS_TO_TRYTES_THRESHOLD) {
trits_to_trytes_sse42(trits, trytes, length);
return;
}
#endif
int k = 0;

for (size_t i = 0, j = 0; i < length; i += RADIX, j++) {
Expand All @@ -70,6 +78,12 @@ void trytes_to_trits(tryte_t const *const trytes, trit_t *const trits, size_t co
return;
}

#if defined(__SSE4_2__)
if (length >= TRYTES_TO_TRITS_THRESHOLD) {
trytes_to_trits_sse42(trytes, trits, length);
return;
}
#endif
for (size_t i = 0, j = 0; i < length; i++, j += RADIX) {
memcpy(trits + j, TRYTES_TRITS_LUT[INDEX_OF_TRYTE(trytes[i])], NUMBER_OF_TRITS_IN_A_TRYTE);
}
Expand Down
8 changes: 8 additions & 0 deletions common/trinary/trit_tryte.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@
#include "common/trinary/trits.h"
#include "common/trinary/tryte.h"

#define TRITS_TO_TRYTES_THRESHOLD 192
#define TRYTES_TO_TRITS_THRESHOLD 736

#ifdef __cplusplus
extern "C" {
#endif

static const trit_t TRYTES_TRITS_LUT[TRYTE_SPACE_SIZE][NUMBER_OF_TRITS_IN_A_TRYTE] = {
{0, 0, 0}, {1, 0, 0}, {-1, 1, 0}, {0, 1, 0}, {1, 1, 0}, {-1, -1, 1}, {0, -1, 1}, {1, -1, 1}, {-1, 0, 1},
{0, 0, 1}, {1, 0, 1}, {-1, 1, 1}, {0, 1, 1}, {1, 1, 1}, {-1, -1, -1}, {0, -1, -1}, {1, -1, -1}, {-1, 0, -1},
{0, 0, -1}, {1, 0, -1}, {-1, 1, -1}, {0, 1, -1}, {1, 1, -1}, {-1, -1, 0}, {0, -1, 0}, {1, -1, 0}, {-1, 0, 0}};

static inline size_t num_trytes_for_trits(size_t num_trits) {
return (num_trits + NUMBER_OF_TRITS_IN_A_TRYTE - 1) / NUMBER_OF_TRITS_IN_A_TRYTE;
}
Expand Down
Loading