blob: 635fa574ba6fafe7299e1474621b099b86e3312b [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Macro-benchmark for ffi with boringssl.
// @dart=2.9
import 'dart:convert';
import 'dart:ffi';
import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:ffi/ffi.dart';
import 'digest.dart';
import 'types.dart';
//
// BoringSSL functions
//
Uint8List inventData(int length) {
final result = Uint8List(length);
for (int i = 0; i < length; i++) {
result[i] = i % 256;
}
return result;
}
Uint8List toUint8List(Pointer<Bytes> bytes, int length) {
final result = Uint8List(length);
final uint8bytes = bytes.asUint8Pointer();
for (int i = 0; i < length; i++) {
result[i] = uint8bytes[i];
}
return result;
}
void copyFromUint8ListToTarget(Uint8List source, Pointer<Data> target) {
final int length = source.length;
final uint8target = target.asUint8Pointer();
for (int i = 0; i < length; i++) {
uint8target[i] = source[i];
}
}
String hash(Pointer<Data> data, int length, Pointer<EVP_MD> hashAlgorithm) {
final context = EVP_MD_CTX_new();
EVP_DigestInit(context, hashAlgorithm);
EVP_DigestUpdate(context, data, length);
final int resultSize = EVP_MD_CTX_size(context);
final Pointer<Bytes> result = calloc<Uint8>(resultSize).cast();
EVP_DigestFinal(context, result, nullptr);
EVP_MD_CTX_free(context);
final String hash = base64Encode(toUint8List(result, resultSize));
calloc.free(result);
return hash;
}
//
// Benchmark fixtures.
//
// Number of repeats: 1 && Length in bytes: 10000000
// * CPU: Intel(R) Xeon(R) Gold 6154
// * Architecture: x64
// * 23000 - 52000000 us (without optimizations)
// * 23000 - 30000 us (with optimizations)
const int L = 1000; // Length of data in bytes.
final hashAlgorithm = EVP_sha512();
// Hash of generated data of `L` bytes with `hashAlgorithm`.
const String expectedHash =
'bNLtqb+cBZcSkCmwBUuB5DP2uLe0madetwXv10usGUFJg1sdGhTEi+aW5NWIRW1RKiLq56obV74rVurn014Iyw==';
/// This benchmark runs a digest algorithm on data residing in C memory.
///
/// This benchmark is intended as macro benchmark with a realistic workload.
class DigestCMemory extends BenchmarkBase {
DigestCMemory() : super('FfiBoringssl.DigestCMemory');
Pointer<Data> data; // Data in C memory that we want to digest.
@override
void setup() {
data = calloc<Uint8>(L).cast();
copyFromUint8ListToTarget(inventData(L), data);
hash(data, L, hashAlgorithm);
}
@override
void teardown() {
calloc.free(data);
}
@override
void run() {
final String result = hash(data, L, hashAlgorithm);
if (result != expectedHash) {
throw Exception('$name: Unexpected result: $result');
}
}
}
/// This benchmark runs a digest algorithm on data residing in Dart memory.
///
/// This benchmark is intended as macro benchmark with a realistic workload.
class DigestDartMemory extends BenchmarkBase {
DigestDartMemory() : super('FfiBoringssl.DigestDartMemory');
Uint8List data; // Data in C memory that we want to digest.
@override
void setup() {
data = inventData(L);
final Pointer<Data> dataInC = calloc<Uint8>(L).cast();
copyFromUint8ListToTarget(data, dataInC);
hash(dataInC, L, hashAlgorithm);
calloc.free(dataInC);
}
@override
void teardown() {}
@override
void run() {
final Pointer<Data> dataInC = calloc<Uint8>(L).cast();
copyFromUint8ListToTarget(data, dataInC);
final String result = hash(dataInC, L, hashAlgorithm);
calloc.free(dataInC);
if (result != expectedHash) {
throw Exception('$name: Unexpected result: $result');
}
}
}
//
// Main driver.
//
void main() {
final benchmarks = [
() => DigestCMemory(),
() => DigestDartMemory(),
];
for (final benchmark in benchmarks) {
benchmark().report();
}
}