-
Notifications
You must be signed in to change notification settings - Fork 337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP ARM Neon/AVX2 SIMD implementation. #730
base: master
Are you sure you want to change the base?
Changes from 8 commits
8c268cd
d8eed56
9beedac
6b6ff88
edf90b8
21d5232
4a04a91
6ee867a
ded09f8
8b281a8
b98ab40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
require "benchmark/ips" | ||
require "json" | ||
require "date" | ||
require "oj" | ||
|
||
Oj.default_options = Oj.default_options.merge(mode: :compat) | ||
|
||
if ENV["ONLY"] | ||
RUN = ENV["ONLY"].split(/[,: ]/).map{|x| [x.to_sym, true] }.to_h | ||
RUN.default = false | ||
elsif ENV["EXCEPT"] | ||
RUN = ENV["EXCEPT"].split(/[,: ]/).map{|x| [x.to_sym, false] }.to_h | ||
RUN.default = true | ||
else | ||
RUN = Hash.new(true) | ||
end | ||
|
||
def implementations(ruby_obj) | ||
state = JSON::State.new(JSON.dump_default_options) | ||
{ | ||
json: ["json", proc { JSON.generate(ruby_obj) }], | ||
oj: ["oj", proc { Oj.dump(ruby_obj) }], | ||
} | ||
end | ||
|
||
def benchmark_encoding(benchmark_name, ruby_obj, check_expected: true, except: []) | ||
json_output = JSON.dump(ruby_obj) | ||
puts "== Encoding #{benchmark_name} (#{json_output.bytesize} bytes)" | ||
|
||
impls = implementations(ruby_obj).select { |name| RUN[name] } | ||
except.each { |i| impls.delete(i) } | ||
|
||
Benchmark.ips do |x| | ||
expected = ::JSON.dump(ruby_obj) if check_expected | ||
impls.values.each do |name, block| | ||
begin | ||
result = block.call | ||
if check_expected && expected != result | ||
puts "#{name} does not match expected output. Skipping" | ||
puts "Expected:" + '-' * 40 | ||
puts expected | ||
puts "Actual:" + '-' * 40 | ||
puts result | ||
puts '-' * 40 | ||
next | ||
end | ||
rescue => error | ||
puts "#{name} unsupported (#{error})" | ||
next | ||
end | ||
x.report(name, &block) | ||
end | ||
x.compare!(order: :baseline) | ||
end | ||
puts | ||
end | ||
|
||
benchmark_encoding "long string", (["this is a test of the emergency broadcast system."*5]*500) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#ifndef EXTCONF_H | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file gets generated by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it shouldn't be committed. |
||
#define EXTCONF_H | ||
#define JSON_GENERATOR 1 | ||
#define ENABLE_SIMD 1 | ||
#define HAVE_ARM_NEON_H 1 | ||
#define HAVE_TYPE_UINT8X16_T 1 | ||
#define HAVE_TYPE_UINT8X8_T 1 | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,5 +6,55 @@ | |
else | ||
append_cflags("-std=c99") | ||
$defs << "-DJSON_GENERATOR" | ||
|
||
if enable_config('use-simd', default=true) | ||
if RbConfig::CONFIG['host_cpu'] =~ /^(arm.*|aarch64.*)/ | ||
$defs.push("-DENABLE_SIMD") | ||
|
||
# Try to compile a small program using NEON instructions | ||
if have_header('arm_neon.h') | ||
have_type('uint8x16_t', headers=['arm_neon.h']) && try_compile(<<~'SRC') | ||
#include <arm_neon.h> | ||
int main() { | ||
uint8x16_t test = vdupq_n_u8(32); | ||
return 0; | ||
} | ||
SRC | ||
|
||
have_type('uint8x8_t', headers=['arm_neon.h']) && try_compile(<<~'SRC') | ||
#include <arm_neon.h> | ||
int main() { | ||
uint8x8_t test = vdup_n_u8(32); | ||
return 0; | ||
} | ||
SRC | ||
end | ||
elsif have_header('x86intrin.h') | ||
|
||
# This is currently hardcoded to false as using m256 seems significantly slower on my machine. | ||
# TODO make this configurable | ||
if false && have_type('__m256i', headers=['x86intrin.h']) && try_compile(<<~'SRC', opt='-mavx2') | ||
#include <x86intrin.h> | ||
int main() { | ||
__m256i test = _mm256_set1_epi8(32); | ||
return 0; | ||
} | ||
SRC | ||
$defs.push("-DENABLE_SIMD") | ||
append_cflags('-mavx2') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is likely not a safe assumption. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. What we should try to do is to use the various That's really the tricky part and blocker. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-target-function-attribute int sse3_func (void) __attribute__ ((__target__ ("sse3"))); Seem to be the syntax, and I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like both However, I'm working on samyron#1 to refactor to use runtime SIMD detection. I also specialized the SSE version a bit. See the link for a few benchmarks. |
||
elsif have_type('__m128i', headers=['x86intrin.h']) && try_compile(<<~'SRC', opt='-mavx2') | ||
#include <x86intrin.h> | ||
int main() { | ||
__m128i test = _mm_set1_epi8(32); | ||
return 0; | ||
} | ||
SRC | ||
$defs.push("-DENABLE_SIMD") | ||
append_cflags('-mavx2') | ||
end | ||
end | ||
end | ||
|
||
create_header | ||
create_makefile 'json/ext/generator' | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is probably a better way to accomplish this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, also I cleaned up the Rakefile the other day, so this will cause a merge conflict.