コードを飛ばす——高性能Julia学習ノート(三)
7829 ワード
前の2編のコードを飛ばす——高性能Julia学習ノート(一) コードを飛ばす——高性能Julia学習ノート(二)、高性能のJuliaコードを書く方法を紹介しました.この2編は私の最近のプロジェクトと結びつけて、簡単なテストでいろいろな言語をmonte carloアルゴリズムでpiの効率を計算します.
まず、本文は厳格な意味での性能テストとは言えず、言語の聖戦を挑発したくない.個人の能力は限られており、実現した異なる言語バージョンのコードも必ずしも最も効率的ではなく、基本的にはnaive実現である.
モンテカルロアルゴリズムに詳しくない場合は、次の2つの資料を参照してください.私は時間を無駄にしません. https://zh.wikipedia.org/wiki... http://www.ruanyifeng.com/blo...
マシンは2015年のMacPro:
JSバージョン
まず、本文は厳格な意味での性能テストとは言えず、言語の聖戦を挑発したくない.個人の能力は限られており、実現した異なる言語バージョンのコードも必ずしも最も効率的ではなく、基本的にはnaive実現である.
モンテカルロアルゴリズムに詳しくない場合は、次の2つの資料を参照してください.私は時間を無駄にしません.
マシンは2015年のMacPro:
Processor: 2.5GHz Intel Core i7
Memory: 16GB 1600 MHZ DDR3
Os: macOS High Sierra Version 10.13.4
JSバージョン function pi(n) {
let inCircle = 0;
for (let i = 0; i <= n; i++) {
x = Math.random();
y = Math.random();
if (x * x + y * y < 1.0) {
inCircle += 1;
}
}
return (4.0 * inCircle) / n;
}
const N = 100000000;
console.log(pi(N));
結果:➜ me.magicly.performance git:(master) ✗ node --version
v10.11.0
➜ me.magicly.performance git:(master) ✗ time node mc.js
3.14174988
node mc.js 10.92s user 0.99s system 167% cpu 7.091 total
Goバージョン package main
import (
"math/rand"
)
func PI(samples int) (result float64) {
inCircle := 0
r := rand.New(rand.NewSource(42))
for i := 0; i < samples; i++ {
x := r.Float64()
y := r.Float64()
if (x*x + y*y) < 1 {
inCircle++
}
}
return float64(inCircle) / float64(samples) * 4.0
}
func main() {
samples := 100000000
PI(samples)
}
結果:➜ me.magicly.performance git:(master) ✗ go version
go version go1.11 darwin/amd64
➜ me.magicly.performance git:(master) ✗ time go run monte_carlo.go
go run monte_carlo.go 2.17s user 0.10s system 101% cpu 2.231 total
Cバージョン #include
#include
#include
#include
#define SEED 42
int main(int argc, char **argv)
{
int niter = 100000000;
double x, y;
int i, count = 0;
double z;
double pi;
srand(SEED);
count = 0;
for (i = 0; i < niter; i++)
{
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
z = x * x + y * y;
if (z <= 1)
count++;
}
pi = (double)count / niter * 4;
printf("# of trials= %d , estimate of pi is %g
", niter, pi);
}
結果:➜ me.magicly.performance git:(master) ✗ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
➜ me.magicly.performance git:(master) ✗ gcc -O2 -o mc-pi-c mc-pi.c
➜ me.magicly.performance git:(master) ✗ time ./mc-pi-c
# of trials= 100000000 , estimate of pi is 3.14155
./mc-pi-c 1.22s user 0.00s system 99% cpu 1.226 total
C++バージョン #include
#include //defines rand(), srand(), RAND_MAX
#include //defines math functions
using namespace std;
int main()
{
const int SEED = 42;
int interval, i;
double x, y, z, pi;
int inCircle = 0;
srand(SEED);
const int N = 100000000;
for (i = 0; i < N; i++)
{
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
z = x * x + y * y;
if (z < 1)
{
inCircle++;
}
}
pi = double(4 * inCircle) / N;
cout << "
Final Estimation of Pi = " << pi << endl;
return 0;
}
結果:➜ me.magicly.performance git:(master) ✗ c++ --version
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
➜ me.magicly.performance git:(master) ✗ c++ -O2 -o mc-pi-cpp mc-pi.cpp
➜ me.magicly.performance git:(master) ✗ time ./mc-pi-cpp
Final Estimation of Pi = 3.14155
./mc-pi-cpp 1.23s user 0.01s system 99% cpu 1.239 total
Juliaバージョン function pi(N::Int)
inCircle = 0
for i = 1:N
x = rand() * 2 - 1
y = rand() * 2 - 1
r2 = x*x + y*y
if r2 < 1.0
inCircle += 1
end
end
return inCircle / N * 4.0
end
N = 100_000_000
println(pi(N))
結果:➜ me.magicly.performance git:(master) ✗ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.0.1 (2018-09-29)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> versioninfo()
Julia Version 1.0.1
Commit 0d713926f8 (2018-09-29 19:05 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin14.5.0)
CPU: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
➜ me.magicly.performance git:(master) ✗ time julia mc.jl
3.14179496
julia mc.jl 0.85s user 0.17s system 144% cpu 0.705 total
またRust開発環境のアップグレードに問題があり、うまくいかなかったが、これまでの経験からC++とは差が少ないと思う.
githubでは、より多くの言語が含まれている比較を見つけました.興味があるのはhttps://gist.github.com/jmoir...を参考にすることができます.LuaJITはRustとあまり差がないほど速く、Julia公式サイトのbenchmarkと比較してhttps://julialang.org/benchma...に一致しています.
さらに、2つのGoの同時バージョンが実現されました.package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
type Job struct {
n int
}
var threads = runtime.NumCPU()
var rands = make([]*rand.Rand, 0, threads)
func init() {
fmt.Printf("cpus: %d
", threads)
runtime.GOMAXPROCS(threads)
for i := 0; i < threads; i++ {
rands = append(rands, rand.New(rand.NewSource(time.Now().UnixNano())))
}
}
func MultiPI2(samples int) float64 {
t1 := time.Now()
threadSamples := samples / threads
jobs := make(chan Job, 100)
results := make(chan int, 100)
for w := 0; w < threads; w++ {
go worker2(w, jobs, results, threadSamples)
}
go func() {
for i := 0; i < threads; i++ {
jobs
結果:➜ me.magicly.performance git:(master) ✗ time go run monte_carlo.1.go
cpus: 8
PI: 100000000 times, value: 3.141778, cost: 2.098006252s
MultiPI: 100000000 times, value: 3.141721, cost: 513.008435ms
MultiPI2: 100000000 times, value: 3.141272, cost: 485.336029ms
go run monte_carlo.1.go 9.41s user 0.18s system 285% cpu 3.357 total
効率が4倍に向上したことがわかります.なぜCPUが8個あるのに4倍しかアップしていないのでしょうか?実は私のmacproは4コアで、8はハイパースレッドから出た仮想コアで、cpu密集計算では効率を高めることはできません.この記事を参照してください:物理CPU、CPUコア数、論理CPU、ハイパースレッド.
次は、Juliaでパラレルを利用して効率をさらに高める方法を見てみましょう.
知識星に参加して面白い技術の話題を共有することを歓迎します.
function pi(n) {
let inCircle = 0;
for (let i = 0; i <= n; i++) {
x = Math.random();
y = Math.random();
if (x * x + y * y < 1.0) {
inCircle += 1;
}
}
return (4.0 * inCircle) / n;
}
const N = 100000000;
console.log(pi(N));
➜ me.magicly.performance git:(master) ✗ node --version
v10.11.0
➜ me.magicly.performance git:(master) ✗ time node mc.js
3.14174988
node mc.js 10.92s user 0.99s system 167% cpu 7.091 total
package main
import (
"math/rand"
)
func PI(samples int) (result float64) {
inCircle := 0
r := rand.New(rand.NewSource(42))
for i := 0; i < samples; i++ {
x := r.Float64()
y := r.Float64()
if (x*x + y*y) < 1 {
inCircle++
}
}
return float64(inCircle) / float64(samples) * 4.0
}
func main() {
samples := 100000000
PI(samples)
}
結果:
➜ me.magicly.performance git:(master) ✗ go version
go version go1.11 darwin/amd64
➜ me.magicly.performance git:(master) ✗ time go run monte_carlo.go
go run monte_carlo.go 2.17s user 0.10s system 101% cpu 2.231 total
Cバージョン #include
#include
#include
#include
#define SEED 42
int main(int argc, char **argv)
{
int niter = 100000000;
double x, y;
int i, count = 0;
double z;
double pi;
srand(SEED);
count = 0;
for (i = 0; i < niter; i++)
{
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
z = x * x + y * y;
if (z <= 1)
count++;
}
pi = (double)count / niter * 4;
printf("# of trials= %d , estimate of pi is %g
", niter, pi);
}
結果:➜ me.magicly.performance git:(master) ✗ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
➜ me.magicly.performance git:(master) ✗ gcc -O2 -o mc-pi-c mc-pi.c
➜ me.magicly.performance git:(master) ✗ time ./mc-pi-c
# of trials= 100000000 , estimate of pi is 3.14155
./mc-pi-c 1.22s user 0.00s system 99% cpu 1.226 total
C++バージョン #include
#include //defines rand(), srand(), RAND_MAX
#include //defines math functions
using namespace std;
int main()
{
const int SEED = 42;
int interval, i;
double x, y, z, pi;
int inCircle = 0;
srand(SEED);
const int N = 100000000;
for (i = 0; i < N; i++)
{
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
z = x * x + y * y;
if (z < 1)
{
inCircle++;
}
}
pi = double(4 * inCircle) / N;
cout << "
Final Estimation of Pi = " << pi << endl;
return 0;
}
結果:➜ me.magicly.performance git:(master) ✗ c++ --version
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
➜ me.magicly.performance git:(master) ✗ c++ -O2 -o mc-pi-cpp mc-pi.cpp
➜ me.magicly.performance git:(master) ✗ time ./mc-pi-cpp
Final Estimation of Pi = 3.14155
./mc-pi-cpp 1.23s user 0.01s system 99% cpu 1.239 total
Juliaバージョン function pi(N::Int)
inCircle = 0
for i = 1:N
x = rand() * 2 - 1
y = rand() * 2 - 1
r2 = x*x + y*y
if r2 < 1.0
inCircle += 1
end
end
return inCircle / N * 4.0
end
N = 100_000_000
println(pi(N))
結果:➜ me.magicly.performance git:(master) ✗ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.0.1 (2018-09-29)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> versioninfo()
Julia Version 1.0.1
Commit 0d713926f8 (2018-09-29 19:05 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin14.5.0)
CPU: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
➜ me.magicly.performance git:(master) ✗ time julia mc.jl
3.14179496
julia mc.jl 0.85s user 0.17s system 144% cpu 0.705 total
またRust開発環境のアップグレードに問題があり、うまくいかなかったが、これまでの経験からC++とは差が少ないと思う.
githubでは、より多くの言語が含まれている比較を見つけました.興味があるのはhttps://gist.github.com/jmoir...を参考にすることができます.LuaJITはRustとあまり差がないほど速く、Julia公式サイトのbenchmarkと比較してhttps://julialang.org/benchma...に一致しています.
さらに、2つのGoの同時バージョンが実現されました.package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
type Job struct {
n int
}
var threads = runtime.NumCPU()
var rands = make([]*rand.Rand, 0, threads)
func init() {
fmt.Printf("cpus: %d
", threads)
runtime.GOMAXPROCS(threads)
for i := 0; i < threads; i++ {
rands = append(rands, rand.New(rand.NewSource(time.Now().UnixNano())))
}
}
func MultiPI2(samples int) float64 {
t1 := time.Now()
threadSamples := samples / threads
jobs := make(chan Job, 100)
results := make(chan int, 100)
for w := 0; w < threads; w++ {
go worker2(w, jobs, results, threadSamples)
}
go func() {
for i := 0; i < threads; i++ {
jobs
結果:➜ me.magicly.performance git:(master) ✗ time go run monte_carlo.1.go
cpus: 8
PI: 100000000 times, value: 3.141778, cost: 2.098006252s
MultiPI: 100000000 times, value: 3.141721, cost: 513.008435ms
MultiPI2: 100000000 times, value: 3.141272, cost: 485.336029ms
go run monte_carlo.1.go 9.41s user 0.18s system 285% cpu 3.357 total
効率が4倍に向上したことがわかります.なぜCPUが8個あるのに4倍しかアップしていないのでしょうか?実は私のmacproは4コアで、8はハイパースレッドから出た仮想コアで、cpu密集計算では効率を高めることはできません.この記事を参照してください:物理CPU、CPUコア数、論理CPU、ハイパースレッド.
次は、Juliaでパラレルを利用して効率をさらに高める方法を見てみましょう.
知識星に参加して面白い技術の話題を共有することを歓迎します.
#include
#include
#include
#include
#define SEED 42
int main(int argc, char **argv)
{
int niter = 100000000;
double x, y;
int i, count = 0;
double z;
double pi;
srand(SEED);
count = 0;
for (i = 0; i < niter; i++)
{
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
z = x * x + y * y;
if (z <= 1)
count++;
}
pi = (double)count / niter * 4;
printf("# of trials= %d , estimate of pi is %g
", niter, pi);
}
➜ me.magicly.performance git:(master) ✗ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
➜ me.magicly.performance git:(master) ✗ gcc -O2 -o mc-pi-c mc-pi.c
➜ me.magicly.performance git:(master) ✗ time ./mc-pi-c
# of trials= 100000000 , estimate of pi is 3.14155
./mc-pi-c 1.22s user 0.00s system 99% cpu 1.226 total
#include
#include //defines rand(), srand(), RAND_MAX
#include //defines math functions
using namespace std;
int main()
{
const int SEED = 42;
int interval, i;
double x, y, z, pi;
int inCircle = 0;
srand(SEED);
const int N = 100000000;
for (i = 0; i < N; i++)
{
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
z = x * x + y * y;
if (z < 1)
{
inCircle++;
}
}
pi = double(4 * inCircle) / N;
cout << "
Final Estimation of Pi = " << pi << endl;
return 0;
}
結果:
➜ me.magicly.performance git:(master) ✗ c++ --version
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
➜ me.magicly.performance git:(master) ✗ c++ -O2 -o mc-pi-cpp mc-pi.cpp
➜ me.magicly.performance git:(master) ✗ time ./mc-pi-cpp
Final Estimation of Pi = 3.14155
./mc-pi-cpp 1.23s user 0.01s system 99% cpu 1.239 total
Juliaバージョン function pi(N::Int)
inCircle = 0
for i = 1:N
x = rand() * 2 - 1
y = rand() * 2 - 1
r2 = x*x + y*y
if r2 < 1.0
inCircle += 1
end
end
return inCircle / N * 4.0
end
N = 100_000_000
println(pi(N))
結果:➜ me.magicly.performance git:(master) ✗ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.0.1 (2018-09-29)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> versioninfo()
Julia Version 1.0.1
Commit 0d713926f8 (2018-09-29 19:05 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin14.5.0)
CPU: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
➜ me.magicly.performance git:(master) ✗ time julia mc.jl
3.14179496
julia mc.jl 0.85s user 0.17s system 144% cpu 0.705 total
またRust開発環境のアップグレードに問題があり、うまくいかなかったが、これまでの経験からC++とは差が少ないと思う.
githubでは、より多くの言語が含まれている比較を見つけました.興味があるのはhttps://gist.github.com/jmoir...を参考にすることができます.LuaJITはRustとあまり差がないほど速く、Julia公式サイトのbenchmarkと比較してhttps://julialang.org/benchma...に一致しています.
さらに、2つのGoの同時バージョンが実現されました.package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
type Job struct {
n int
}
var threads = runtime.NumCPU()
var rands = make([]*rand.Rand, 0, threads)
func init() {
fmt.Printf("cpus: %d
", threads)
runtime.GOMAXPROCS(threads)
for i := 0; i < threads; i++ {
rands = append(rands, rand.New(rand.NewSource(time.Now().UnixNano())))
}
}
func MultiPI2(samples int) float64 {
t1 := time.Now()
threadSamples := samples / threads
jobs := make(chan Job, 100)
results := make(chan int, 100)
for w := 0; w < threads; w++ {
go worker2(w, jobs, results, threadSamples)
}
go func() {
for i := 0; i < threads; i++ {
jobs
結果:➜ me.magicly.performance git:(master) ✗ time go run monte_carlo.1.go
cpus: 8
PI: 100000000 times, value: 3.141778, cost: 2.098006252s
MultiPI: 100000000 times, value: 3.141721, cost: 513.008435ms
MultiPI2: 100000000 times, value: 3.141272, cost: 485.336029ms
go run monte_carlo.1.go 9.41s user 0.18s system 285% cpu 3.357 total
効率が4倍に向上したことがわかります.なぜCPUが8個あるのに4倍しかアップしていないのでしょうか?実は私のmacproは4コアで、8はハイパースレッドから出た仮想コアで、cpu密集計算では効率を高めることはできません.この記事を参照してください:物理CPU、CPUコア数、論理CPU、ハイパースレッド.
次は、Juliaでパラレルを利用して効率をさらに高める方法を見てみましょう.
知識星に参加して面白い技術の話題を共有することを歓迎します.
function pi(N::Int)
inCircle = 0
for i = 1:N
x = rand() * 2 - 1
y = rand() * 2 - 1
r2 = x*x + y*y
if r2 < 1.0
inCircle += 1
end
end
return inCircle / N * 4.0
end
N = 100_000_000
println(pi(N))
➜ me.magicly.performance git:(master) ✗ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.0.1 (2018-09-29)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> versioninfo()
Julia Version 1.0.1
Commit 0d713926f8 (2018-09-29 19:05 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin14.5.0)
CPU: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
➜ me.magicly.performance git:(master) ✗ time julia mc.jl
3.14179496
julia mc.jl 0.85s user 0.17s system 144% cpu 0.705 total
package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
type Job struct {
n int
}
var threads = runtime.NumCPU()
var rands = make([]*rand.Rand, 0, threads)
func init() {
fmt.Printf("cpus: %d
", threads)
runtime.GOMAXPROCS(threads)
for i := 0; i < threads; i++ {
rands = append(rands, rand.New(rand.NewSource(time.Now().UnixNano())))
}
}
func MultiPI2(samples int) float64 {
t1 := time.Now()
threadSamples := samples / threads
jobs := make(chan Job, 100)
results := make(chan int, 100)
for w := 0; w < threads; w++ {
go worker2(w, jobs, results, threadSamples)
}
go func() {
for i := 0; i < threads; i++ {
jobs
➜ me.magicly.performance git:(master) ✗ time go run monte_carlo.1.go
cpus: 8
PI: 100000000 times, value: 3.141778, cost: 2.098006252s
MultiPI: 100000000 times, value: 3.141721, cost: 513.008435ms
MultiPI2: 100000000 times, value: 3.141272, cost: 485.336029ms
go run monte_carlo.1.go 9.41s user 0.18s system 285% cpu 3.357 total