LoginSignup
4
6

More than 5 years have passed since last update.

「C# が C++ の速度を凌駕している」らしいので、私も試してみる

Last updated at Posted at 2016-10-26

何番煎じか。古い記事を引っ張り出してごめんなさい。
でも、わたし、気になります。

概要

気づいたら、C# が C++ の速度を凌駕している!
続) 気づいたら、C# が C++ の速度を凌駕している!
だそうです。
あちこちで検証されているようですが、半年遅れて私もやってみる。
「C# が C++ の速度を凌駕している」らしいので、C++側を高速化してみた

環境

Windows 10 Pro バージョン 1607(OSビルド 14393.321) 64bit版
.NET Framework 4.6.1
Visual Studio 2015 Update 3
CPU: Intel Core i7-6700T
Memory: 16GB (DDR4)
csc.exe バージョン 4.6.1586.0
cl.exe バージョン 19.00.24215 for x64

とりあえずそのまま

続) 気づいたら、C# が C++ の速度を凌駕している!
に掲載されているコードを変えずに実行してみる(10回ループだけいれる)。

10回試行。

最遅 最速 平均
C#(safe) 2449 2403 2418.5
C#(unsafe) 1652 1620 1637.4
C++ 1407 1359 1371.9

DDR4のせいなのかな。どうやっても肉薄しないんだよなぁ。

わたし、気になります

System.Diagnostics.Stopwatchには、

The Stopwatch measures elapsed time by counting timer ticks in the underlying timer mechanism.If the installed hardware and operating system support a high-resolution performance counter, then the Stopwatch class uses that counter to measure elapsed time.

高精度タイマーがあれば使うとあります。

WindowsAPIのプログラム経験がある方は、C++のコードを見てぴんとくると思います。
というわけで、GetTickCountをstd::chrono::high_resolution_clockに置き換えましょう。QueryPerformanceCounterも高精度タイマがあれば使いますから。

最遅 最速 平均
C++ 1375 1354 1364.7

上振れしない分だけさらに差が開きましたね。

おそらく追記します。

追記

もう少し画像処理

「C# が C++ の速度を凌駕している」らしいので、C++側を高速化してみた
のコメントにあるようにとんでも高速化ができてしまうので、もう少し画像処理ぽくしてみる。
近傍ピクセルが関係しないピクセル独立の処理は、画像処理にほとんどないです。
固定敷居値の2値化が、恐らく一番単純な処理かなと考えました。
コードは以下、できるだけ元から変わらないようにね。

C#

// Compile: csc /o /unsafe speedtest.cs
using System;
using System.Diagnostics;

class SpeedTest
{
    private const byte threshold = 127;
    private const byte Black = (byte)0x00U;
    private const byte White = (byte)0xFFU;

    static void test1(byte[] a, int w, int h, int stride)
    {
        for(int y = 0; y < h; y++) {
            int offset = y * stride;
            for(int x = 0; x < w; x++) {
                a[x+offset] = (threshold<a[x+offset])? White : Black;
            }
        }
    }

    static unsafe void test2(byte[] a, int w, int h, int stride)
    {
        fixed (byte* p0 = a) {
            for(int y = 0; y < h; y++) {
                byte* p = p0 + y * stride;
                for(int x = 0; x < w; x++) {
                    p[x] = (threshold<p[x])? White : Black;
                }
            }
        }
    }

    static void time(Action action, int count = 100)
    {
        var tw = new Stopwatch();
        tw.Start();
        for(int i = 0; i < count; i++)
            action();
        tw.Stop();
        Console.WriteLine(tw.ElapsedMilliseconds);
    }

    void fill(byte[] bytes)
    {
        Random random = new Random(DateTime.UtcNow.Millisecond);
        for(int i = 0; i<bytes.Length; ++i) {
            bytes[i] = (byte)random.Next();
        }
    }

    static void Main(string[] args)
    {
        int w = 4321;
        int h = 6789;
        int stride = (w + 3) & ~3;
        var a = new byte[stride * h];

        for(int i = 0; i<10; ++i) {
            time(() => test1(a, w, h, stride));
        }
        Console.WriteLine();
        for(int i = 0; i<10; ++i) {
            time(() => test2(a, w, h, stride));
        }
    }
}

C++

// Compile: cl /MD /Ox /EHsc speedtest.cpp
#include <stdio.h>
#include <windows.h>
#include <functional>
#include <chrono>
#include <random>
#include <cassert>
typedef unsigned char byte;

static void test(byte* a, int w, int h, int stride)
{
    static const byte threshold = 127;
    auto p0 = a;
    for(int y = 0; y < h; y++){
        auto p = p0 + y * stride;
        for(int x = 0; x < w; x++){
            p[x] = (threshold<p[x])? 0xFFU : 0x00U;
        }
    }
}

void time(std::function<void()> action, int count = 100)
{
    std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
    //auto start = GetTickCount();
    for(int i = 0; i < count; i++)
        action();

    std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
    std::chrono::milliseconds msec = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    printf("%lld\n", msec.count());
    //printf("%u\n", GetTickCount() - start);
}

void fill(int size, byte* bytes)
{
    std::random_device devrand;
    std::mt19937 random(devrand());
    for(int i=0; i<size; ++i){
        bytes[i] = static_cast<byte>(random());
    }
}

int main()
{
    int w = 4321;
    int stride = (w + 3) & ~3;
    int h = 6789;
    auto a = new byte[stride * h];
    for(int i=0; i<10; ++i){
        fill(w*stride, a);
        time([=]() { test(a, w, h, stride); });
    }
    delete[] a;

    return 0;
}

結果

最遅 最速 平均
C#(safe) 3767 3724 3746.8
C#(unsafe) 2821 2806 2815.1
C++ 1882 1857 1866.5

メモリアクセスがもっと遅ければ肉薄する気がしてきた。
あ、C#で乱数初期化忘れてる、もういいや。

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6