pipeline?
最近の CPU で Bubble Sort をする場合、ランダムな配列より逆順に並んだ配列のソートの方が早いという話を聞いたので試したみた。
#include <iostream> #include <limits> #include <utility> #include <vector> #include <algorithm> #include <boost/random/mersenne_twister.hpp> #include <boost/random/uniform_int.hpp> #include <ctime> template<typename T> void bubble_sort(T begin, T end) { typedef typename std::iterator_traits<T>::difference_type size_type; size_type size = std::distance(begin, end); T a = begin; for (size_type i = 0; i < size - 1; ++i) { for (size_type j = 0; j < size - i - 1; j++) { if (a[j] > a[j + 1]) std::swap(a[j], a[j + 1]); } } } template<typename T> void measure(T begin, T end) { clock_t t1 = clock(); bubble_sort(begin, end); clock_t t2 = clock(); std::cout << static_cast<double>(t2 - t1) / CLOCKS_PER_SEC << std::endl; } #define ARRAY_SIZE 20000 int main() { std::vector<int> a(ARRAY_SIZE); std::vector<int> b(ARRAY_SIZE); boost::uniform_int<int> r(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()); boost::mt19937 mt(0); typedef std::vector<int>::iterator iterator; for (iterator it = a.begin(), end = a.end(); it != end; ++it) { *it = r(mt); } std::copy(a.begin(), a.end(), b.begin()); std::sort(b.begin(), b.end(), std::greater<int>()); measure(a.begin(), a.end()); measure(b.begin(), b.end()); return 0; }
Pentium 4 2.4GHz で実行。
% g++ -W -Wall -O3 bubble_sort.cpp -o bubble_sort % ./bubble_sort 1.35 0.61
おぉ、本当だ。逆順に整列した配列の方が交換回数が多いはずだが、そんなのを吹き飛ばすぐらい投機実行が効いているということか?
どうせなので Java でもやってみる。
import java.util.Arrays; import java.util.Random; public class BubbleSort { public static final void bubbleSort(int[] a) { int length = a.length; for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - i - 1; j++) { if (a[j] > a[j + 1]) { int tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp; } } } } public static final void measure(int[] a) { long start = System.currentTimeMillis(); bubbleSort(a); long end = System.currentTimeMillis(); System.out.println(end - start); } public static void main(String[] args) { final int ARRAY_SIZE = 20000; Random random = new Random(); int[] a = new int[ARRAY_SIZE]; int[] b = new int[ARRAY_SIZE]; for (int i = 0; i < ARRAY_SIZE; i++) { a[i] = random.nextInt(); } System.arraycopy(a, 0, b, 0, ARRAY_SIZE); Arrays.sort(b); b = reverse(b); measure(a); measure(b); } private static int[] reverse(int[] a) { int[] b = new int[a.length]; for (int i = 0; i < a.length; i++) { b[b.length - i - 1] = a[i]; } return b; } }
% java -version java version "1.5.0_06" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05) Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing) % javac -O BubbleSort.java % java -server BubbleSort 1451 439 % java -client BubbleSort 1959 1589
Server VM ならかなり差がでる。関係ないけど、Server VM の方は C++ と遜色ない速度が出ている。実験もせずに Java が遅いとかいっているやつは腹を(ry
スクリプト言語は大差はつかないと思うが、一応 Perl も。
use strict; use warnings; use Benchmark; sub bubble_sort { my ($a) = @_; for my $i ( 0 ... ( @$a - 2 ) ) { for my $j ( 0 ... ( @$a - $i - 2 ) ) { if ( $a->[$j] > $a->[ $j + 1 ] ) { my $tmp = $a->[$j]; $a->[$j] = $a->[ $j + 1 ]; $a->[ $j + 1 ] = $tmp; } } } } my $size = 5000; my @a; for ( 0 ... $size ) { push @a, rand; } my @b = reverse sort @a; timethese(1, { random => sub { bubble_sort(\@a) }, reverse_sorted => sub { bubble_sort(\@b) } });
% perl bubble_sort.pl Benchmark: timing 1 iterations of random, reverse_sorted... sorted random: 7 wallclock secs ( 6.70 usr + 0.00 sys = 6.70 CPU) @ 0.15/s (n=1) (warning: too few iterations for a reliable count) sorted reverse_sorted: 7 wallclock secs ( 6.71 usr + 0.00 sys = 6.71 CPU) @ 0.15/s (n=1) (warning: too few iterations for a reliable count)
やっぱ変わんないね。
凡ミスしてた。こっちが本来の結果。
% perl bubble_sort.pl Benchmark: timing 1 iterations of random, reverse_sorted... random: 16 wallclock secs (16.18 usr + 0.00 sys = 16.18 CPU) @ 0.06/s (n=1) (warning: too few iterations for a reliable count) reverse_sorted: 26 wallclock secs (25.25 usr + 0.00 sys = 25.25 CPU) @ 0.04/s (n=1) (warning: too few iterations for a reliable count)
スクリプト言語なら単純に交換回数が多いほうが不利。