| // 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. |
| |
| 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 = nullptr; // 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 = Uint8List(0); // 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(); |
| } |
| } |