Skip to content

Instantly share code, notes, and snippets.

@Egor-Skriptunoff
Created September 8, 2018 17:01
Show Gist options
  • Save Egor-Skriptunoff/4b99da2e80b6b457ff051b586d613dbd to your computer and use it in GitHub Desktop.
Save Egor-Skriptunoff/4b99da2e80b6b457ff051b586d613dbd to your computer and use it in GitHub Desktop.
SHA256 benchmark for LuaJIT
--~ 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