Created
September 8, 2018 17:01
-
-
Save Egor-Skriptunoff/4b99da2e80b6b457ff051b586d613dbd to your computer and use it in GitHub Desktop.
SHA256 benchmark for LuaJIT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--~ How to run the benchmark: | |
--~ $ luajit sha256_for_LuaJIT.lua;luajit -O-narrow sha256_for_LuaJIT.lua | |
local table_concat, byte, char, string_rep, sub, floor, ceil, min, max = | |
table.concat, string.byte, string.char, string.rep, string.sub, math.floor, math.ceil, math.min, math.max | |
local b = require"bit" | |
local AND = b.band | |
local OR = b.bor | |
local XOR = b.bxor | |
local SHL = b.lshift | |
local SHR = b.rshift | |
local ROL = b.rol | |
local ROR = b.ror | |
local HEX = b.tohex | |
local INT32 = b.tobit | |
local ffi = require"ffi" | |
local int32arr8 = ffi.typeof"int32_t[8]" | |
local sha2_H = int32arr8() | |
local sha2_K = ffi.new"uint32_t[64]" | |
local W = ffi.new"int32_t[64]" | |
local function sha256_inner_loop(H, str, offs, size) | |
-- size is a multiple of 64 | |
for pos = offs, offs + size - 1, 64 do | |
-- Process one 64-byte block | |
for j = 0, 15 do | |
pos = pos + 4 | |
local a, b, c, d = byte(str, pos - 3, pos) | |
W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) | |
end | |
for j = 16, 63 do | |
local a, b = W[j-15], W[j-2] | |
W[j] = INT32( XOR(ROR(a, 7), ROL(a, 14), SHR(a, 3)) + XOR(ROL(b, 15), ROL(b, 13), SHR(b, 10)) + W[j-7] + W[j-16] ) | |
end | |
local a, b, c, d, e, f, g, h = H[0], H[1], H[2], H[3], H[4], H[5], H[6], H[7] | |
for j = 0, 63 do | |
local z = INT32( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + h + W[j] + sha2_K[j] ) | |
h, g, f, e = g, f, e, INT32( d + z ) | |
d, c, b, a = c, b, a, INT32( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) | |
end | |
H[0], H[1], H[2], H[3] = INT32(a + H[0]), INT32(b + H[1]), INT32(c + H[2]), INT32(d + H[3]) | |
H[4], H[5], H[6], H[7] = INT32(e + H[4]), INT32(f + H[5]), INT32(g + H[6]), INT32(h + H[7]) | |
end | |
end | |
do -- Calculating of magic numbers (sha2_K and sha2_H) | |
local idx, step, p = 0, {4, 1, 2, -2, 2}, 4 | |
repeat | |
p = p + step[p % 6] | |
local d = 1 | |
repeat | |
d = d + step[d % 6] | |
if d * d > p then | |
sha2_K[idx] = floor(p^(1/3) * 2^32) % 2^32 | |
if idx < 8 then | |
sha2_H[idx] = INT32( floor(p^(1/2) * 2^32) % 2^32 ) | |
end | |
idx = idx + 1 | |
break | |
end | |
until p % d == 0 | |
until idx > 63 | |
end | |
local function sha256(text) | |
local H, length, tail = int32arr8(sha2_H), 0, "" | |
local function partial(text_part) | |
if text_part then | |
if tail then | |
length = length + #text_part | |
local offs = 0 | |
if tail ~= "" and #tail + #text_part >= 64 then | |
offs = 64 - #tail | |
sha256_inner_loop(H, tail..sub(text_part, 1, offs), 0, 64) | |
tail = "" | |
end | |
local size = #text_part - offs | |
local size_tail = size % 64 | |
sha256_inner_loop(H, text_part, offs, size - size_tail) | |
tail = tail..sub(text_part, #text_part + 1 - size_tail) | |
return partial | |
else | |
error("Adding more chunks is not allowed after receiving the final result", 2) | |
end | |
else | |
if tail then | |
local final_blocks = {tail, "\128", string_rep("\0", (-9 - length) % 64 + 1)} | |
tail = nil | |
-- Assuming user data length is shorter than 2^53 bytes | |
-- Anyway, it looks very unrealistic that one would spend enough time to process a 2^53 bytes of data by using this Lua script :-) | |
-- 2^53 bytes = 2^56 bits, so "bit-counter" fits in 7 bytes | |
length = length * (8 / 256^7) -- convert "byte-counter" to "bit-counter" and move decimal point to the left | |
for j = 4, 10 do | |
length = length % 1 * 256 | |
final_blocks[j] = char(floor(length)) | |
end | |
final_blocks = table_concat(final_blocks) | |
sha256_inner_loop(H, final_blocks, 0, #final_blocks) | |
local hx = {} | |
for j = 0, 7 do | |
hx[j+1] = HEX(H[j]) | |
end | |
H = table_concat(hx) | |
end | |
return H | |
end | |
end | |
if text then | |
return partial(text)() | |
else | |
return partial | |
end | |
end | |
-------------------------------------------------------------------------------- | |
-- TEST | |
-------------------------------------------------------------------------------- | |
assert(sha256("") == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") | |
assert(sha256("abc") == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") | |
assert(sha256("123456") == "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92") | |
assert(sha256(("a"):rep(55)) == "9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318") | |
assert(sha256(("a"):rep(56)) == "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a") | |
assert(sha256("The quick brown fox jumps over the lazy dog") == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592") | |
do | |
local append = sha256() | |
append("The quick brown fox") | |
append(" jumps ") | |
append("over the lazy dog") | |
assert(append() == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592") | |
end | |
-------------------------------------------------------------------------------- | |
-- BENCHMARK | |
-------------------------------------------------------------------------------- | |
local function get_narrow_flag() | |
for _, o in ipairs{ jit.status() } do | |
if o == "narrow" then | |
return "ON" | |
end | |
end | |
return "OFF" | |
end | |
local function benchmark() | |
-- calculating SHA256 of 4 MBytes long string of letters "a" | |
local length = 4 * 2^20 -- 4 Mb | |
local part = ("a"):rep(2^12) -- 4 Kb | |
local N = length/#part | |
local clk_start = os.clock() | |
local append = sha256() | |
for j = 1, N do | |
append(part) | |
end | |
local result_sha256 = append() | |
print("CPU seconds:", os.clock() - clk_start) | |
end | |
print("arch: "..jit.arch) | |
print("bits: "..(ffi.abi"32bit" and "32" or ffi.abi"64bit" and "64" or "")) | |
print("narrow: "..get_narrow_flag()) | |
jit.off(); jit.flush() | |
io.write"JIT: OFF " | |
benchmark() | |
jit.on() | |
io.write"JIT: ON " | |
benchmark() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment