Packages

Wirehair binding for Erlang

Current section

Files

Jump to
wirerlhair c_src wirehair test UnitTest.cpp
Raw

c_src/wirehair/test/UnitTest.cpp

#include <wirehair/wirehair.h>
#include "SiameseTools.h"
#include <iostream>
#include <vector>
#include <atomic>
using namespace std;
#define ENABLE_OMP
#define ENABLE_PARTIAL_FINAL
#define ENABLE_ALL_RECOVERY_FINAL_BLOCK
#define BENCHMARK_SHORT_LIST
static const unsigned kMinN = 2;
static const unsigned kMaxN = 64000;
static const unsigned kMaxBlockBytes = 65;
static const unsigned kExtraWarnThreshold = 4;
static const uint8_t kPadChar = (uint8_t)0xee;
static void FillMessage(uint8_t* message, unsigned bytes, siamese::PCGRandom& prng)
{
while (bytes >= 4)
{
uint32_t* words = reinterpret_cast<uint32_t*>(message);
*words = prng.Next();
message += 4;
bytes -= 4;
}
if (bytes > 0)
{
uint32_t word = prng.Next();
for (unsigned j = 0; j < bytes; ++j)
{
message[j] = (uint8_t)word;
word >>= 8;
}
}
}
#pragma warning(disable: 4505)
static bool Test_EncoderProducesOriginals(
WirehairCodec encoder,
unsigned N,
uint8_t* block,
uint8_t* message,
unsigned blockBytes,
unsigned finalBytes)
{
for (unsigned originalBlockId = 0; originalBlockId < N; ++originalBlockId)
{
if (originalBlockId == N - 1) {
memset(block, kPadChar, blockBytes);
}
uint32_t bytesOut = 0;
WirehairResult encodeResult = wirehair_encode(encoder, originalBlockId, &block[0], blockBytes, &bytesOut);
if (encodeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Encode original failed for N = " << N << ", originalBlockId = " << originalBlockId << endl;
return false;
}
if (originalBlockId == N - 1)
{
if (bytesOut != finalBytes)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Encode original wrong length for N = " << N
<< ", originalBlockId = " << originalBlockId
<< ", bytesOut = " << bytesOut << ", finalBytes = " << finalBytes << endl;
return false;
}
if (0 != memcmp(&message[0] + originalBlockId * blockBytes, &block[0], bytesOut))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Encode original wrong data for N = " << N
<< ", originalBlockId = " << originalBlockId
<< ", bytesOut = " << bytesOut << ", finalBytes = " << finalBytes << endl;
return false;
}
for (unsigned i = finalBytes; i < blockBytes; ++i)
{
if (block[i] != kPadChar)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Encode original buffer overrun for N = " << N
<< ", originalBlockId = " << originalBlockId
<< ", bytesOut = " << bytesOut << ", finalBytes = " << finalBytes << endl;
return false;
}
}
}
else
{
if (bytesOut != blockBytes)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Encode original wrong length for N = " << N
<< ", originalBlockId = " << originalBlockId
<< ", bytesOut = " << bytesOut << ", blockBytes = " << blockBytes << endl;
return false;
}
if (0 != memcmp(&message[0] + originalBlockId * blockBytes, &block[0], bytesOut))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Encode original wrong data for N = " << N
<< ", originalBlockId = " << originalBlockId
<< ", bytesOut = " << bytesOut << ", finalBytes = " << finalBytes << endl;
return false;
}
}
}
return true;
}
static bool DoesCodecProduceOriginals(
WirehairCodec decoder,
unsigned N,
unsigned blockBytes,
unsigned finalBytes,
const uint8_t* message,
uint8_t* decodedMessage,
unsigned messageBytes)
{
// Check buffer overflow
decodedMessage[messageBytes] = kPadChar;
WirehairResult recoverResult = wirehair_recover(decoder, decodedMessage, messageBytes);
if (recoverResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to recover with original pieces for N = " << N << endl;
return false;
}
if (0 != memcmp(message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to recover the data with original pieces for N = " << N << endl;
return false;
}
if (decodedMessage[messageBytes] != kPadChar)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to recover buffer overrun with original pieces for N = " << N << endl;
return false;
}
for (unsigned i = 0; i < N; ++i)
{
decodedMessage[finalBytes] = kPadChar;
uint32_t recoveredBytes = 0;
WirehairResult blockResult = wirehair_recover_block(decoder, i, decodedMessage, &recoveredBytes);
if (blockResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to wirehair_recover_block with original pieces for N = " << N << ", i = " << i << endl;
return false;
}
if ((i == N - 1) && decodedMessage[finalBytes] != kPadChar)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to wirehair_recover_block memory corruption with original pieces for N = " << N << endl;
return false;
}
const unsigned expectedBytes = (i == N - 1) ? finalBytes : blockBytes;
if (0 != memcmp(message + i * blockBytes, decodedMessage, expectedBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to wirehair_recover_block the data with original pieces for N = " << N << endl;
return false;
}
}
return true;
}
static bool TestAllRecoveryData(
WirehairCodec encoder,
WirehairCodec decoder,
unsigned N,
unsigned blockBytes,
unsigned finalBytes,
const uint8_t* message,
uint8_t* decodedMessage,
unsigned messageBytes)
{
unsigned needed = 0;
#ifdef ENABLE_ALL_RECOVERY_FINAL_BLOCK
// Check the special case final block:
{
uint32_t Nm1Len = 0;
WirehairResult encodeResult = wirehair_encode(encoder, N - 1, decodedMessage, blockBytes, &Nm1Len);
if (encodeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed for N-1 and N = " << N << endl;
return false;
}
if (Nm1Len != finalBytes)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed wrong len for N-1 and N = " << N << endl;
return false;
}
WirehairResult decodeResult = wirehair_decode(decoder, N - 1, decodedMessage, Nm1Len);
if (decodeResult != Wirehair_NeedMore)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_decode failed for N-1 and N = " << N << endl;
return false;
}
++needed;
}
#endif
for (unsigned blockId = N;; ++blockId)
{
++needed;
uint32_t writeLen = 0;
WirehairResult encodeResult = wirehair_encode(encoder, blockId, decodedMessage, blockBytes, &writeLen);
if (encodeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed for N = " << N << endl;
return false;
}
if (writeLen != blockBytes)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed wrong len for N-1 and N = " << N << endl;
return false;
}
WirehairResult decodeResult = wirehair_decode(decoder, blockId, decodedMessage, writeLen);
if (decodeResult != Wirehair_NeedMore)
{
if (decodeResult == Wirehair_Success) {
break;
}
SIAMESE_DEBUG_BREAK();
cout << "wirehair_decode failed for " << blockId << " and N = " << N << endl;
return false;
}
}
if (needed >= N + kExtraWarnThreshold) {
//SIAMESE_DEBUG_BREAK();
cout << "TestAllRecoveryData: Too much overhead: " << (needed - N) << " extra for N=" << N << " Seed=n/a BlockBytes=" << blockBytes << endl;
//return false;
}
if (!DoesCodecProduceOriginals(decoder, N, blockBytes, finalBytes, message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!! Decoder2 failed to produce originals" << endl;
return false;
}
return true;
}
// Verify that decoder can recover when all input data is available
// ..and that it can turn into an encoder that reproduces all the original data
// ..and that a decoder fed with recovery data produced by the re-encoder will decode
static bool Test_DecodeAllOriginal(
unsigned N,
unsigned blockBytes,
unsigned finalBytes,
const uint8_t* message,
uint8_t* decodedMessage,
unsigned messageBytes)
{
WirehairCodec decoder = wirehair_decoder_create(nullptr, messageBytes, blockBytes);
if (!decoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create decoder for N = " << N << ", blockBytes = " << blockBytes << endl;
return false;
}
// Feed it all original data (out of order)
for (int originalBlockId = N - 1; originalBlockId >= 0; --originalBlockId)
{
const uint8_t* originalBlock = &message[0] + originalBlockId * blockBytes;
const unsigned originalBlockBytes = ((unsigned)originalBlockId == N - 1) ? finalBytes : blockBytes;
WirehairResult decodeResult = wirehair_decode(decoder, originalBlockId, originalBlock, originalBlockBytes);
// If this is the last one:
if (originalBlockId == 0)
{
if (decodeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to decode with original pieces for N = " << N << ", originalBlockId = " << originalBlockId << endl;
return false;
}
}
else if (decodeResult != Wirehair_NeedMore)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to decode midway with original pieces for N = " << N << ", originalBlockId = " << originalBlockId << endl;
return false;
}
}
if (!DoesCodecProduceOriginals(decoder, N, blockBytes, finalBytes, message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!! Decoder failed to produce originals" << endl;
return false;
}
// ..and that it can turn into an encoder that reproduces all the original data
WirehairResult becomeResult = wirehair_decoder_becomes_encoder(decoder);
if (becomeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to wirehair_decoder_becomes_encoder with original pieces for N = " << N << endl;
return false;
}
// ..and that a decoder fed with recovery data produced by the re-encoder will decode
WirehairCodec decoder2 = wirehair_decoder_create(nullptr, messageBytes, blockBytes);
if (!decoder2)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create decoder2 for N = " << N << ", blockBytes = " << blockBytes << endl;
return false;
}
if (!TestAllRecoveryData(decoder, decoder2, N, blockBytes, finalBytes, message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "TestAllRecoveryData failed for N = " << N << endl;
return false;
}
wirehair_free(decoder2);
wirehair_free(decoder);
return true;
}
// Verify that decoder can recover when no input data is available
// ..and that it can turn into an encoder that reproduces all the original data
// ..and that a decoder fed with recovery data produced by the re-encoder will decode
static bool Test_DecodeAllRecovery(
unsigned N,
unsigned blockBytes,
unsigned finalBytes,
const uint8_t* message,
uint8_t* decodedMessage,
unsigned messageBytes)
{
WirehairCodec encoder = wirehair_encoder_create(nullptr, message, messageBytes, blockBytes);
if (!encoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create encoder for N = " << N << ", blockBytes = " << blockBytes << endl;
return false;
}
WirehairCodec decoder = wirehair_decoder_create(nullptr, messageBytes, blockBytes);
if (!decoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create decoder for N = " << N << ", blockBytes = " << blockBytes << endl;
return false;
}
if (!TestAllRecoveryData(encoder, decoder, N, blockBytes, finalBytes, message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "TestAllRecoveryData failed for N = " << N << endl;
return false;
}
// ..and that it can turn into an encoder that reproduces all the original data
WirehairResult becomeResult = wirehair_decoder_becomes_encoder(decoder);
if (becomeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "Failed to wirehair_decoder_becomes_encoder with original pieces for N = " << N << endl;
return false;
}
if (!DoesCodecProduceOriginals(decoder, N, blockBytes, finalBytes, message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!! Converted Decoder failed to produce originals" << endl;
return false;
}
wirehair_free(decoder);
wirehair_free(encoder);
return true;
}
// Verify that decoder can recover when no input data is available
// Do not try to do decoder_becomes_encoder
static bool Test_DecodeRandomLosses(
siamese::PCGRandom& prng,
uint64_t seed,
unsigned N,
unsigned blockBytes,
unsigned finalBytes,
const uint8_t* message,
uint8_t* decodedMessage,
unsigned messageBytes)
{
WirehairCodec encoder = wirehair_encoder_create(nullptr, message, messageBytes, blockBytes);
if (!encoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create encoder for N = " << N << ", blockBytes = " << blockBytes << endl;
return false;
}
WirehairCodec decoder = wirehair_decoder_create(nullptr, messageBytes, blockBytes);
if (!decoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create decoder for N = " << N << ", blockBytes = " << blockBytes << endl;
return false;
}
unsigned blockId = 0;
unsigned needed = 0;
for (;;)
{
blockId++;
if (prng.Next() % 100 < 10) {
continue;
}
++needed;
uint32_t writeLen = 0;
WirehairResult encodeResult = wirehair_encode(encoder, blockId, decodedMessage, blockBytes, &writeLen);
if (encodeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed for N = " << N << endl;
return false;
}
if (writeLen != blockBytes)
{
if (blockId != N - 1 || writeLen != finalBytes)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed wrong len for " << blockId << " and N = " << N << endl;
return false;
}
}
WirehairResult decodeResult = wirehair_decode(decoder, blockId, decodedMessage, writeLen);
if (decodeResult != Wirehair_NeedMore)
{
if (decodeResult == Wirehair_Success) {
break;
}
SIAMESE_DEBUG_BREAK();
cout << "wirehair_decode failed for " << blockId << " and N = " << N << endl;
return false;
}
}
if (needed >= N + kExtraWarnThreshold) {
//SIAMESE_DEBUG_BREAK();
cout << "Test_DecodeRandomLosses: Too much overhead: " << (needed - N) << " extra for N=" << N << " Seed=" << seed <<" BlockBytes=" << blockBytes << endl;
//return false;
}
if (!DoesCodecProduceOriginals(decoder, N, blockBytes, finalBytes, message, decodedMessage, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!! Decoder2 failed to produce originals" << endl;
return false;
}
wirehair_free(decoder);
wirehair_free(encoder);
return true;
}
static atomic<bool> TestFailed(false);
static void TestN(uint64_t seed, int N, unsigned blockBytes)
{
siamese::PCGRandom prng;
prng.Seed(seed + N, blockBytes);
vector<uint8_t> message(N * blockBytes + 1);
vector<uint8_t> block(blockBytes);
vector<uint8_t> decodedMessageVector(N * blockBytes + 1);
// Final block is partial
#ifdef ENABLE_PARTIAL_FINAL
const unsigned finalBytes = blockBytes == 1 ? 1 : (blockBytes - 1);
#else
const unsigned finalBytes = blockBytes;
#endif
const unsigned messageBytes = blockBytes * (N - 1) + finalBytes;
uint8_t* decodedMessagePtr = &decodedMessageVector[0];
// Set up a random message
FillMessage(&message[0], messageBytes, prng);
WirehairCodec encoder = wirehair_encoder_create(nullptr, &message[0], messageBytes, blockBytes);
if (!encoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create encoder for N = " << N << ", blockBytes = " << blockBytes << endl;
TestFailed = true;
return;
}
// Verify that encoder produces original blocks
if (!Test_EncoderProducesOriginals(encoder, N, &block[0], &message[0], blockBytes, finalBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Test_EncoderProducesOriginals failed" << endl;
TestFailed = true;
return;
}
// Decoder checks:
if (!Test_DecodeAllOriginal(N, blockBytes, finalBytes, &message[0], decodedMessagePtr, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Test_DecodeAllOriginal failed" << endl;
TestFailed = true;
return;
}
if (!Test_DecodeAllRecovery(N, blockBytes, finalBytes, &message[0], decodedMessagePtr, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Test_DecodeAllRecovery failed" << endl;
TestFailed = true;
return;
}
if (!Test_DecodeRandomLosses(prng, seed, N, blockBytes, finalBytes, &message[0], decodedMessagePtr, messageBytes))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Test_DecodeAllRecovery failed" << endl;
TestFailed = true;
return;
}
wirehair_free(encoder);
}
static bool ReadmeExample()
{
// Size of packets to produce
static const int kPacketSize = 1400;
// Note: Does not need to be an even multiple of packet size or 16 etc
static const int kMessageBytes = 1000 * 1000 + 333;
vector<uint8_t> message(kMessageBytes);
// Fill message contents
memset(&message[0], 1, message.size());
// Create encoder
WirehairCodec encoder = wirehair_encoder_create(nullptr, &message[0], kMessageBytes, kPacketSize);
if (!encoder)
{
cout << "!!! Failed to create encoder" << endl;
return false;
}
// Create decoder
WirehairCodec decoder = wirehair_decoder_create(nullptr, kMessageBytes, kPacketSize);
if (!decoder)
{
// Free memory for encoder
wirehair_free(encoder);
cout << "!!! Failed to create decoder" << endl;
return false;
}
unsigned blockId = 0, needed = 0;
for (;;)
{
// Select which block to encode.
// Note: First N blocks are the original data, so it's possible to start
// sending data while wirehair_encoder_create() is getting started.
blockId++;
// Simulate 10% packetloss
if (blockId % 10 == 0) {
continue;
}
// Keep track of how many pieces were needed
++needed;
vector<uint8_t> block(kPacketSize);
// Encode a packet
uint32_t writeLen = 0;
WirehairResult encodeResult = wirehair_encode(
encoder, // Encoder object
blockId, // ID of block to generate
&block[0], // Output buffer
kPacketSize, // Output buffer size
&writeLen); // Returned block length
if (encodeResult != Wirehair_Success)
{
cout << "wirehair_encode failed: " << encodeResult << endl;
return false;
}
// Attempt decode
WirehairResult decodeResult = wirehair_decode(
decoder, // Decoder object
blockId, // ID of block that was encoded
&block[0], // Input block
writeLen); // Block length
// If decoder returns success:
if (decodeResult == Wirehair_Success) {
// Decoder has enough data to recover now
break;
}
if (decodeResult != Wirehair_NeedMore)
{
cout << "wirehair_decode failed: " << decodeResult << endl;
return false;
}
}
vector<uint8_t> decoded(kMessageBytes);
// Recover original data on decoder side
WirehairResult decodeResult = wirehair_recover(
decoder,
&decoded[0],
kMessageBytes);
if (decodeResult != Wirehair_Success)
{
cout << "wirehair_recover failed: " << decodeResult << endl;
return false;
}
// Free memory for encoder and decoder
wirehair_free(encoder);
wirehair_free(decoder);
return true;
}
static bool Benchmark(unsigned N, unsigned packetBytes, unsigned trials)
{
siamese::PCGRandom prng;
prng.Seed(N, packetBytes);
const unsigned kBlockBytes = packetBytes;
const unsigned kMessageBytes = N * kBlockBytes;
vector<uint8_t> message(kMessageBytes);
vector<uint8_t> block(kBlockBytes);
vector<uint8_t> decoded(kMessageBytes);
memset(&message[0], 6, kMessageBytes);
uint64_t encode_create_sum = 0;
uint64_t runs = 0;
uint64_t extra_sum = 0;
uint64_t encode_sum = 0;
uint64_t decode_sum = 0;
uint64_t packets = 0;
uint64_t recover_sum = 0;
for (unsigned trial = 0; trial < trials; ++trial)
{
++runs;
uint64_t t0 = siamese::GetTimeUsec();
WirehairCodec encoder = wirehair_encoder_create(nullptr, &message[0], kMessageBytes, kBlockBytes);
if (!encoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create encoder" << endl;
return false;
}
uint64_t t1 = siamese::GetTimeUsec();
WirehairCodec decoder = wirehair_decoder_create(nullptr, kMessageBytes, kBlockBytes);
if (!decoder)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Failed to create decoder" << endl;
return false;
}
encode_create_sum += t1 - t0;
unsigned blockId = 0;
unsigned needed = 0;
for (;;)
{
blockId++;
// Introduce about 30% loss to the data
if (prng.Next() % 100 < 30) {
continue;
}
++needed;
uint64_t t3 = siamese::GetTimeUsec();
uint32_t writeLen = 0;
WirehairResult encodeResult = wirehair_encode(
encoder,
blockId,
&block[0],
kBlockBytes,
&writeLen);
if (encodeResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_encode failed" << endl;
return false;
}
uint64_t t4 = siamese::GetTimeUsec();
WirehairResult decodeResult = wirehair_decode(decoder, blockId, &block[0], writeLen);
uint64_t t5 = siamese::GetTimeUsec();
encode_sum += t4 - t3;
decode_sum += t5 - t4;
++packets;
if (decodeResult != Wirehair_NeedMore)
{
if (decodeResult == Wirehair_Success) {
break;
}
SIAMESE_DEBUG_BREAK();
cout << "wirehair_decode failed for " << blockId << " and N = " << N << endl;
return false;
}
}
SIAMESE_DEBUG_ASSERT(needed >= N);
extra_sum += needed - N;
uint64_t t7 = siamese::GetTimeUsec();
WirehairResult recoverResult = wirehair_recover(decoder, &decoded[0], kMessageBytes);
uint64_t t8 = siamese::GetTimeUsec();
if (recoverResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "wirehair_recover failed" << endl;
return false;
}
recover_sum += t8 - t7;
wirehair_free(decoder);
wirehair_free(encoder);
}
cout << "For N = " << N << " packets of " << packetBytes << " bytes:" << endl;
uint64_t avg_encode_create_usec = encode_create_sum / runs;
float encode_create_MBPS = 0.f;
if (avg_encode_create_usec > 0) {
encode_create_MBPS = kMessageBytes / (float)avg_encode_create_usec;
}
cout << "+ Average wirehair_encoder_create() time: " << avg_encode_create_usec << " usec (" << encode_create_MBPS << " MBPS)" << endl;
// wirehair_decoder_create() is super fast. So is the decoder_becomes_encoder() most of the time
uint64_t avg_encode_usec = encode_sum / packets;
uint64_t avg_decode_usec = decode_sum / packets;
float encode_MBPS = 0.f, decode_MBPS = 0.f;
if (encode_sum > 0) {
encode_MBPS = (packets * kBlockBytes) / (float)encode_sum;
}
if (decode_sum > 0) {
decode_MBPS = (packets * kBlockBytes) / (float)decode_sum;
}
cout << "+ Average wirehair_encode() time: " << avg_encode_usec << " usec (" << encode_MBPS << " MBPS)" << endl;
cout << "+ Average wirehair_decode() time: " << avg_decode_usec << " usec (" << decode_MBPS << " MBPS)" << endl;
float avg_overhead_pieces = extra_sum / (float)runs;
cout << "+ Average overhead piece count beyond N = " << avg_overhead_pieces << endl;
uint64_t avg_recover_usec = recover_sum / runs;
float recover_MBPS = 0.f;
if (recover_sum > 0) {
recover_MBPS = (runs * kMessageBytes) / (float)recover_sum;
}
cout << "+ Average wirehair_recover() time: " << avg_recover_usec << " usec (" << recover_MBPS << " MBPS)" << endl;
return true;
}
static const unsigned kBenchmarkNList[] = {
12,
32,
102,
134,
169,
201,
294,
359,
413,
770,
1000
};
int main()
{
const WirehairResult initResult = wirehair_init();
if (initResult != Wirehair_Success)
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Wirehair initialization failed: " << initResult << endl;
return -1;
}
if (!ReadmeExample())
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Example usage failed" << endl;
return -2;
}
#ifdef BENCHMARK_SHORT_LIST
for (unsigned i = 0; i < sizeof(kBenchmarkNList) / sizeof(kBenchmarkNList[0]); ++i)
{
const unsigned N = kBenchmarkNList[i];
#else
for (unsigned N = 2; N < 64000; N *= 2)
{
#endif
const unsigned kPacketBytes = 1300;
const unsigned kBenchTrials = 2000;
if (!Benchmark(N, kPacketBytes, kBenchTrials))
{
SIAMESE_DEBUG_BREAK();
cout << "!!! Benchmark failed" << endl;
return -3;
}
}
cout << "Wirehair Unit Test" << endl;
uint64_t seed = siamese::GetTimeUsec();
cout << "Start seed = " << seed << endl;
for (unsigned N = kMinN; N <= kMaxN; ++N)
{
#ifdef ENABLE_OMP
#pragma omp parallel for
#endif
for (int blockBytes = 1; blockBytes <= kMaxBlockBytes; ++blockBytes) {
TestN(seed, N, blockBytes);
}
if (TestFailed) {
cout << "A test failed for N = " << N << endl;
return -4;
}
//cout << "Test passed for N = " << N << endl;
}
return 0;
}