Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
35 changes: 35 additions & 0 deletions bench/keccak_bench_cli.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const std = @import("std");
const eth = @import("eth");

pub fn main() !void {
var args = std.process.args();
_ = args.next(); // skip program name

const size_str = args.next() orelse "32";
const iters_str = args.next() orelse "1000000";
const backend_str = args.next() orelse "xkcp";

const size = try std.fmt.parseInt(usize, size_str, 10);
const iters = try std.fmt.parseInt(usize, iters_str, 10);

// Allocate input data
const allocator = std.heap.page_allocator;
const data = try allocator.alloc(u8, size);
defer allocator.free(data);
@memset(data, 0xAB);

if (std.mem.eql(u8, backend_str, "xkcp")) {
// XKCP backend (our new implementation)
for (0..iters) |_| {
const result = eth.keccak.hash(data);
std.mem.doNotOptimizeAway(&result);
}
} else if (std.mem.eql(u8, backend_str, "stdlib")) {
// Zig stdlib backend
for (0..iters) |_| {
var result: [32]u8 = undefined;
std.crypto.hash.sha3.Keccak256.hash(data, &result, .{});
std.mem.doNotOptimizeAway(&result);
}
}
Comment thread
koko1123 marked this conversation as resolved.
}
78 changes: 78 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
addXkcp(b, eth_module, target);

// Unit tests
const unit_tests = b.addTest(.{
Expand Down Expand Up @@ -48,7 +50,9 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = .ReleaseFast,
.link_libc = true,
});
addXkcp(b, bench_module, target);

const zbench_dep = b.dependency("zbench", .{});
const zbench_mod = zbench_dep.module("zbench");
Expand Down Expand Up @@ -87,4 +91,78 @@ pub fn build(b: *std.Build) void {
const run_keccak_compare = b.addRunArtifact(keccak_compare_exe);
const keccak_compare_step = b.step("bench-keccak", "Compare eth.zig Keccak vs stdlib (ReleaseFast)");
keccak_compare_step.dependOn(&run_keccak_compare.step);

// Keccak CLI benchmark (for hyperfine comparison)
const keccak_bench_exe = b.addExecutable(.{
.name = "keccak-bench-zig",
.root_module = b.createModule(.{
.root_source_file = b.path("bench/keccak_bench_cli.zig"),
.target = target,
.optimize = .ReleaseFast,
.imports = &.{
.{ .name = "eth", .module = bench_module },
},
}),
});
b.installArtifact(keccak_bench_exe);
}

/// Add XKCP keccak C sources to a module with CPU-appropriate backend selection.
fn addXkcp(b: *std.Build, module: *std.Build.Module, target: std.Build.ResolvedTarget) void {
const c_flags = &.{"-O3"};

// Common include paths
module.addIncludePath(b.path("src/crypto/xkcp/common"));
module.addIncludePath(b.path("src/crypto/xkcp/high"));

// Select backend based on target CPU
const arch = target.result.cpu.arch;
if (arch == .aarch64) {
// XKCP's ARMv8A NEON assembly uses GAS syntax incompatible with clang's
// integrated assembler. Use the optimized generic 64-bit C backend instead,
// which LLVM will optimize with NEON auto-vectorization.
module.addIncludePath(b.path("src/crypto/xkcp/plain64"));
module.addCSourceFile(.{
.file = b.path("src/crypto/xkcp/plain64/KeccakP-1600-opt64.c"),
.flags = c_flags,
});
} else if (arch == .x86_64) {
const features = target.result.cpu.features;
const avx512f = @intFromEnum(std.Target.x86.Feature.avx512f);
const avx2 = @intFromEnum(std.Target.x86.Feature.avx2);

if (features.isEnabled(avx512f)) {
// AVX512 SnP header must come before plain64 to shadow KeccakP-1600-SnP.h
module.addIncludePath(b.path("src/crypto/xkcp/avx512"));
module.addIncludePath(b.path("src/crypto/xkcp/plain64"));
module.addAssemblyFile(b.path("src/crypto/xkcp/avx512/KeccakP-1600-AVX512.s"));
} else if (features.isEnabled(avx2)) {
// AVX2 SnP header must come before plain64 to shadow KeccakP-1600-SnP.h
module.addIncludePath(b.path("src/crypto/xkcp/avx2"));
module.addIncludePath(b.path("src/crypto/xkcp/plain64"));
module.addAssemblyFile(b.path("src/crypto/xkcp/avx2/KeccakP-1600-AVX2.s"));
Comment thread
koko1123 marked this conversation as resolved.
} else {
module.addCSourceFile(.{
.file = b.path("src/crypto/xkcp/plain64/KeccakP-1600-opt64.c"),
.flags = c_flags,
});
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} else {
// Generic 64-bit fallback
module.addIncludePath(b.path("src/crypto/xkcp/plain64"));
module.addCSourceFile(.{
.file = b.path("src/crypto/xkcp/plain64/KeccakP-1600-opt64.c"),
.flags = c_flags,
});
}

// High-level API (sponge + hash)
module.addCSourceFile(.{
.file = b.path("src/crypto/xkcp/high/KeccakSponge.c"),
.flags = c_flags,
});
module.addCSourceFile(.{
.file = b.path("src/crypto/xkcp/high/KeccakHash.c"),
.flags = c_flags,
});
}
50 changes: 50 additions & 0 deletions src/crypto/xkcp/armv8a/KeccakP-1600-SnP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
The eXtended Keccak Code Package (XKCP)
https://github.com/XKCP/XKCP

Implementation by Andre Moraes

---

Please refer to SnP-documentation.h for more details.
*/

#ifndef _KeccakP_1600_SnP_h_
#define _KeccakP_1600_SnP_h_

#include <stdint.h>
#include "align.h"
#include "SnP-common.h"

typedef struct {
ALIGN(64) uint64_t A[25];
} KeccakP1600_align512plain64_state;

typedef KeccakP1600_align512plain64_state KeccakP1600_state;

#define KeccakP1600_GetImplementation() "64-bit optimized ARMv8a assembler implementation"
#define KeccakP1600_GetFeatures() (SnP_Feature_Main)
#define KeccakP1600_stateAlignment 64

#define KeccakP1600_StaticInitialize()
void KeccakP1600_Initialize(KeccakP1600_align512plain64_state *state);
void KeccakP1600_AddByte(KeccakP1600_align512plain64_state *state, unsigned char data, unsigned int offset);
void KeccakP1600_AddBytes(KeccakP1600_align512plain64_state *state, const unsigned char *data, unsigned int offset, unsigned int length);
void KeccakP1600_OverwriteBytes(KeccakP1600_align512plain64_state *state, const unsigned char *data, unsigned int offset, unsigned int length);
void KeccakP1600_OverwriteWithZeroes(KeccakP1600_align512plain64_state *state, unsigned int byteCount);
void KeccakP1600_Permute_Nrounds(KeccakP1600_align512plain64_state *state, unsigned int nrounds);
void KeccakP1600_Permute_12rounds(KeccakP1600_align512plain64_state *state);
void KeccakP1600_Permute_24rounds(KeccakP1600_align512plain64_state *state);
void KeccakP1600_ExtractBytes(const KeccakP1600_align512plain64_state *state, unsigned char *data, unsigned int offset, unsigned int length);
void KeccakP1600_ExtractAndAddBytes(const KeccakP1600_align512plain64_state *state, const unsigned char *input, unsigned char *output, unsigned int offset, unsigned int length);

#define KeccakF1600_FastLoop_Absorb(...) 0
#define KeccakP1600_12rounds_FastLoop_Absorb(...) 0
#define KeccakP1600_ODDuplexingFastInOut(...) 0
#define KeccakP1600_12rounds_ODDuplexingFastInOut(...) 0
#define KeccakP1600_ODDuplexingFastOut(...) 0
#define KeccakP1600_12rounds_ODDuplexingFastOut(...) 0
#define KeccakP1600_ODDuplexingFastIn(...) 0
#define KeccakP1600_12rounds_ODDuplexingFastIn(...) 0

#endif
Loading