Skip to content

Instantly share code, notes, and snippets.

@will
Last active June 1, 2016 22:24
Show Gist options
  • Save will/c348bf90a7cbd7e93fd0 to your computer and use it in GitHub Desktop.
Save will/c348bf90a7cbd7e93fd0 to your computer and use it in GitHub Desktop.
require "benchmark"
lib LibC
fun ntohl(str : UInt32) : Int32
end
NUM = 0x01020304
RES = 0x04030201
def ntohl(value_ptr)
LibC.ntohl((value_ptr.as(UInt32*)).value)
end
def intrin(value_ptr)
Intrinsics.bswap32((value_ptr.as(UInt32*)).value)
end
def handshift(value_ptr)
((((((((0_u32
) | value_ptr[0]) << 8
) | value_ptr[1]) << 8
) | value_ptr[2]) << 8
) | value_ptr[3])
end
def handswap(value_ptr)
value_ptr[0], value_ptr[1], value_ptr[2], value_ptr[3] = value_ptr[3], value_ptr[2], value_ptr[1], value_ptr[0]
(value_ptr.as(Int32*)).value
end
def memoryIo(value_ptr)
IO::ByteFormat::NetworkEndian.decode(Int32, MemoryIO.new(value_ptr.to_slice(4)))
end
def getptr
j = Pointer(Int32).malloc(1)
j.copy_from(pointerof(NUM), 1)
j.as(UInt8*)
end
puts "32bit"
puts "="*10
pp RES
p ntohl(getptr) == intrin(getptr) == handshift(getptr) == handswap(getptr) == memoryIo(getptr) == RES
puts
Benchmark.ips do |x|
x.report("ntohl") { ntohl(getptr) }
x.report("intrin") { intrin(getptr) }
x.report("handshift") { handshift(getptr) }
x.report("handswap") { handswap(getptr) }
x.report("memoryIo") { memoryIo(getptr) }
end
NUM64 = 0x0102030405060708
RES64 = 0x0807060504030201
def ntohl64(value_ptr)
(LibC.ntohl((value_ptr.as(UInt32*)).value).to_u64 << 32) | LibC.ntohl(((value_ptr + 4).as(UInt32*)).value)
end
def intrin64(value_ptr)
(Intrinsics.bswap32((value_ptr.as(UInt32*)).value).to_u64 << 32) | Intrinsics.bswap32(((value_ptr + 4).as(UInt32*)).value)
end
def handswap64(value_ptr)
value_ptr[0], value_ptr[1], value_ptr[2], value_ptr[3], value_ptr[4], value_ptr[5], value_ptr[6], value_ptr[7] = value_ptr[7], value_ptr[6], value_ptr[5], value_ptr[4], value_ptr[3], value_ptr[2], value_ptr[1], value_ptr[0]
(value_ptr.as(Int64*)).value
end
def handshift64(value_ptr)
((((((((((((((((0_u64
) | value_ptr[0]) << 8
) | value_ptr[1]) << 8
) | value_ptr[2]) << 8
) | value_ptr[3]) << 8
) | value_ptr[4]) << 8
) | value_ptr[5]) << 8
) | value_ptr[6]) << 8
) | value_ptr[7])
end
def memoryIo64(value_ptr)
IO::ByteFormat::NetworkEndian.decode(Int64, MemoryIO.new(value_ptr.to_slice(8)))
end
def getptr64
j = Pointer(Int64).malloc(1)
j.copy_from(pointerof(NUM64), 1)
j.as(UInt8*)
end
puts
puts "64 bit"
puts "="*10
pp RES64
p ntohl64(getptr64) == intrin64(getptr64) == handswap64(getptr64) == handshift64(getptr64) == memoryIo64(getptr64) == RES64
puts
Benchmark.ips do |x|
x.report("ntohl64") { ntohl64(getptr64) }
x.report("intrin64") { intrin64(getptr64) }
x.report("handshift64") { handshift64(getptr64) }
x.report("handswap64") { handswap64(getptr64) }
x.report("memoryIo64") { memoryIo(getptr) }
end
$ crystal run --release byte_swap.cr
32bit
==========
RES = 67305985
true
ntohl 42.64M (± 6.40%) 1.09× slower
intrin 43.25M (± 4.54%) 1.08× slower
handshift 46.57M (± 3.72%) fastest
handswap 42.42M (± 4.45%) 1.10× slower
memoryIo 9.8M (± 9.81%) 4.75× slower
64 bit
==========
RES64 = 578437695752307201
true
ntohl64 46.17M (±20.54%) 1.21× slower
intrin64 54.08M (± 9.65%) 1.03× slower
handshift64 53.88M (± 4.56%) 1.04× slower
handswap64 55.81M (± 5.68%) fastest
memoryIo64 10.83M (±21.91%) 5.15× slower
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment