파이썬은 실제로 얼마나 느립니까? (또는 언어가 얼마나 빠릅니까?)


149

Python / NumPy로 작성한이 코드가 있습니다

from __future__ import division
import numpy as np
import itertools

n = 6
iters = 1000
firstzero = 0
bothzero = 0
""" The next line iterates over arrays of length n+1 which contain only -1s and 1s """
for S in itertools.product([-1, 1], repeat=n+1):
    """For i from 0 to iters -1 """
    for i in xrange(iters):
        """ Choose a random array of length n.
            Prob 1/4 of being -1, prob 1/4 of being 1 and prob 1/2 of being 0. """
        F = np.random.choice(np.array([-1, 0, 0, 1], dtype=np.int8), size=n)
        """The next loop just makes sure that F is not all zeros."""
        while np.all(F == 0):
            F = np.random.choice(np.array([-1, 0, 0, 1], dtype=np.int8), size=n)
        """np.convolve(F, S, 'valid') computes two inner products between
        F and the two successive windows of S of length n."""
        FS = np.convolve(F, S, 'valid')
        if FS[0] == 0:
            firstzero += 1
        if np.all(FS == 0):
            bothzero += 1

print("firstzero: %i" % firstzero)
print("bothzero: %i" % bothzero)

그것은 두 개의 랜덤 배열의 컨볼 루션 수를 세고 있는데, 하나는 특정 확률 분포로 하나는 다른 것보다 길고 첫 번째 위치는 0이거나 두 위치는 0입니다.

나는 파이썬이 빠를 필요가있는 코드를 작성하는 끔찍한 언어라고 말하는 친구와 내기를했습니다. 내 컴퓨터에서 9 초가 걸립니다. 그는 "적절한 언어로"작성하면 100 배 더 빨라질 수 있다고 말합니다.

문제는 선택한 코드에서이 코드가 실제로 100 배 더 빠를 수 있는지 확인하는 것입니다. 나는 당신의 코드를 테스트 할 것이고 지금부터 가장 빠른 일주일이 이길 것입니다. 누군가가 0.09s 이하로 떨어지면 자동으로 이기고 잃습니다.

지위

  • 파이썬 . Alistair Buxon으로 30 배 속도 향상! 가장 빠른 해결책은 아니지만 실제로 가장 좋아합니다.
  • 옥타브 . @Thethos로 100 배 속도가 향상됩니다.
  • . @dbaupp에 의해 500 배가 빨라집니다.
  • C ++ . Guy Sirton의 570 배 속도.
  • C . @ace로 727 배가 빨라집니다.
  • C ++ . @Stefan에 의해 믿을 수 없을 정도로 빠릅니다.

가장 빠른 솔루션은 현재 너무 빠르기 때문에 시간이 너무 빠릅니다. 따라서 n을 10으로 늘리고 iters = 100000을 설정하여 최고의 것을 비교했습니다. 이 측정에서 가장 빠릅니다.

  • C . @ace에 의해 7.5s.
  • C ++ . @Stefan의 1입니다.

내 컴퓨터 타이밍이 내 컴퓨터에서 실행됩니다. 이것은 AMD FX-8350 8 코어 프로세서에 표준 우분투 설치입니다. 이것은 또한 코드를 실행할 수 있어야 함을 의미합니다.

후속 소식 게시이 경쟁은 x100 속도 향상을 얻기가 너무 쉬웠으므로 속도 전문가의 전문 지식을 행사하려는 사람들을 위해 후속 조치를 게시했습니다. 파이썬이 얼마나 느리게 진행 되는가 (II 부)를 참조하십시오 .

답변:


61

C ++ 비트 매직

간단한 RNG로 0.84ms, c ++ 11 std :: knuth로 1.67ms

약간의 알고리즘 수정으로 0.16ms (아래 편집 참조)

파이썬 구현은 내 장비에서 7.97 초 안에 실행됩니다. 따라서 선택한 RNG에 따라 9488 ~ 4772 배 더 빠릅니다.

#include <iostream>
#include <bitset>
#include <random>
#include <chrono>
#include <stdint.h>
#include <cassert>
#include <tuple>

#if 0
// C++11 random
std::random_device rd;
std::knuth_b gen(rd());

uint32_t genRandom()
{
    return gen();
}
#else
// bad, fast, random.

uint32_t genRandom()
{
    static uint32_t seed = std::random_device()();
    auto oldSeed = seed;
    seed = seed*1664525UL + 1013904223UL; // numerical recipes, 32 bit
    return oldSeed;
}
#endif

#ifdef _MSC_VER
uint32_t popcnt( uint32_t x ){ return _mm_popcnt_u32(x); }
#else
uint32_t popcnt( uint32_t x ){ return __builtin_popcount(x); }
#endif



std::pair<unsigned, unsigned> convolve()
{
    const uint32_t n = 6;
    const uint32_t iters = 1000;
    unsigned firstZero = 0;
    unsigned bothZero = 0;

    uint32_t S = (1 << (n+1));
    // generate all possible N+1 bit strings
    // 1 = +1
    // 0 = -1
    while ( S-- )
    {
        uint32_t s1 = S % ( 1 << n );
        uint32_t s2 = (S >> 1) % ( 1 << n );
        uint32_t fmask = (1 << n) -1; fmask |= fmask << 16;
        static_assert( n < 16, "packing of F fails when n > 16.");


        for( unsigned i = 0; i < iters; i++ )
        {
            // generate random bit mess
            uint32_t F;
            do {
                F = genRandom() & fmask;
            } while ( 0 == ((F % (1 << n)) ^ (F >> 16 )) );

            // Assume F is an array with interleaved elements such that F[0] || F[16] is one element
            // here MSB(F) & ~LSB(F) returns 1 for all elements that are positive
            // and  ~MSB(F) & LSB(F) returns 1 for all elements that are negative
            // this results in the distribution ( -1, 0, 0, 1 )
            // to ease calculations we generate r = LSB(F) and l = MSB(F)

            uint32_t r = F % ( 1 << n );
            // modulo is required because the behaviour of the leftmost bit is implementation defined
            uint32_t l = ( F >> 16 ) % ( 1 << n );

            uint32_t posBits = l & ~r;
            uint32_t negBits = ~l & r;
            assert( (posBits & negBits) == 0 );

            // calculate which bits in the expression S * F evaluate to +1
            unsigned firstPosBits = ((s1 & posBits) | (~s1 & negBits));
            // idem for -1
            unsigned firstNegBits = ((~s1 & posBits) | (s1 & negBits));

            if ( popcnt( firstPosBits ) == popcnt( firstNegBits ) )
            {
                firstZero++;

                unsigned secondPosBits = ((s2 & posBits) | (~s2 & negBits));
                unsigned secondNegBits = ((~s2 & posBits) | (s2 & negBits));

                if ( popcnt( secondPosBits ) == popcnt( secondNegBits ) )
                {
                    bothZero++;
                }
            }
        }
    }

    return std::make_pair(firstZero, bothZero);
}

int main()
{
    typedef std::chrono::high_resolution_clock clock;
    int rounds = 1000;
    std::vector< std::pair<unsigned, unsigned> > out(rounds);

    // do 100 rounds to get the cpu up to speed..
    for( int i = 0; i < 10000; i++ )
    {
        convolve();
    }


    auto start = clock::now();

    for( int i = 0; i < rounds; i++ )
    {
        out[i] = convolve();
    }

    auto end = clock::now();
    double seconds = std::chrono::duration_cast< std::chrono::microseconds >( end - start ).count() / 1000000.0;

#if 0
    for( auto pair : out )
        std::cout << pair.first << ", " << pair.second << std::endl;
#endif

    std::cout << seconds/rounds*1000 << " msec/round" << std::endl;

    return 0;
}

추가 레지스터를 위해 64 비트로 컴파일하십시오. 단순 랜덤 생성기를 사용할 때 convolve ()의 루프는 메모리 액세스없이 실행되며 모든 변수는 레지스터에 저장됩니다.

작동 원리 : 오히려 저장하지 SF메모리 배열로, 그것은가 uint32_t 비트로 저장됩니다.
에 대해 S, n최하위 비트가 사용되는데, 여기서 세트 비트는 +1을 나타내고 비 세트 비트는 -1을 나타낸다.
F[-1, 0, 0, 1]의 분포를 만들려면 최소 2 비트가 필요합니다. 이는 랜덤 비트를 생성 r하고 16 개의 최하위 비트 ()와 16 개의 최상위 비트 ()를 검사하여 수행됩니다 l. 경우 l & ~r우리는 경우 F가 1이라고 가정 ~l & r우리가 가정 F-1이다. 그렇지 않으면 F0입니다. 이것은 우리가 찾고있는 분포를 생성합니다.

이제 우리가 S, posBits모든 위치에 설정된 비트가 어디 F == 1과 negBits모든 위치 F의 == -1에 설정된 비트.

F * S조건에서 (*는 곱셈을 나타냄) +1로 평가됨을 증명할 수 있습니다 (S & posBits) | (~S & negBits). 또한 F * S-1 로 평가되는 모든 경우에 대해 유사한 논리를 생성 할 수 있습니다 . 마지막으로 sum(F * S)결과에 -1과 +1이 같은 경우에만 0으로 평가됩니다. +1 비트 수와 -1 비트 수를 간단히 비교하면 계산하기가 매우 쉽습니다.

이 구현은 32 비트 int를 사용하며 n허용 되는 최대 값 은 16입니다. 랜덤 생성 코드를 수정하여 구현을 31 비트로, uint32_t 대신 uint64_t를 사용하여 63 비트로 스케일링 할 수 있습니다.

편집하다

다음과 같은 볼록 함수 :

std::pair<unsigned, unsigned> convolve()
{
    const uint32_t n = 6;
    const uint32_t iters = 1000;
    unsigned firstZero = 0;
    unsigned bothZero = 0;
    uint32_t fmask = (1 << n) -1; fmask |= fmask << 16;
    static_assert( n < 16, "packing of F fails when n > 16.");


    for( unsigned i = 0; i < iters; i++ )
    {
        // generate random bit mess
        uint32_t F;
        do {
            F = genRandom() & fmask;
        } while ( 0 == ((F % (1 << n)) ^ (F >> 16 )) );

        // Assume F is an array with interleaved elements such that F[0] || F[16] is one element
        // here MSB(F) & ~LSB(F) returns 1 for all elements that are positive
        // and  ~MSB(F) & LSB(F) returns 1 for all elements that are negative
        // this results in the distribution ( -1, 0, 0, 1 )
        // to ease calculations we generate r = LSB(F) and l = MSB(F)

        uint32_t r = F % ( 1 << n );
        // modulo is required because the behaviour of the leftmost bit is implementation defined
        uint32_t l = ( F >> 16 ) % ( 1 << n );

        uint32_t posBits = l & ~r;
        uint32_t negBits = ~l & r;
        assert( (posBits & negBits) == 0 );

        uint32_t mask = posBits | negBits;
        uint32_t totalBits = popcnt( mask );
        // if the amount of -1 and +1's is uneven, sum(S*F) cannot possibly evaluate to 0
        if ( totalBits & 1 )
            continue;

        uint32_t adjF = posBits & ~negBits;
        uint32_t desiredBits = totalBits / 2;

        uint32_t S = (1 << (n+1));
        // generate all possible N+1 bit strings
        // 1 = +1
        // 0 = -1
        while ( S-- )
        {
            // calculate which bits in the expression S * F evaluate to +1
            auto firstBits = (S & mask) ^ adjF;
            auto secondBits = (S & ( mask << 1 ) ) ^ ( adjF << 1 );

            bool a = desiredBits == popcnt( firstBits );
            bool b = desiredBits == popcnt( secondBits );
            firstZero += a;
            bothZero += a & b;
        }
    }

    return std::make_pair(firstZero, bothZero);
}

런타임을 0.160-0.161ms로 줄입니다. 수동 루프 언롤 (위 그림에 표시되지 않음)은 0.150입니다. 사소한 n = 10, iter = 100000의 경우는 250ms에서 실행됩니다. 추가 코어를 활용하여 50ms 미만으로 얻을 수 있다고 확신하지만 너무 쉽습니다.

내부 루프 분기를 해제하고 F 및 S 루프를 교체하면됩니다. 필요하지 않은
경우 bothZero가능한 모든 S 어레이를 조금씩 반복하여 런타임을 0.02ms로 줄일 수 있습니다.


3
gcc 친화적 인 버전을 제공 할 수 있습니까? 현재 테스트 할 수 있는지 잘 모르겠습니다.

나는 이것에 대해 아무것도 모르지만 Google은 __builtin_popcount가 _mm_popcnt_u32 ()의 대체품 일 수 있다고 말합니다.

3
코드가 업데이트되었으며 #ifdef 스위치를 사용하여 올바른 popcnt 명령을 선택하십시오. -std=c++0x -mpopcnt -O232 비트 모드에서 실행하려면 1.01ms로 컴파일하고 사용 합니다 (64 비트 GCC 버전이 없습니다).
Stefan

출력물을 인쇄 할 수 있습니까? 실제로 현재 수행중인 작업인지 확실하지 않습니다 :)

7
당신은 분명히 마법사입니다. + 1
BurntPizza

76

Python2.7 + Numpy 1.8.1 : 10.242 초

포트란 90 + : 0.029의 0.003의 0.022의 0.010의

젠장, 당신은 내기를 잃었습니다! 병렬화도 떨어지지 않고 바로 Fortran 90+를 사용하십시오.

편집 배열을 바꾸는 Guy Sirton의 알고리즘을 사용했습니다 S(좋은 찾기 : D). 분명히이 -g -traceback플래그를 약 0.017 초로 낮추는 컴파일러 플래그가 활성화되었습니다. 현재 나는 이것을 다음과 같이 컴파일하고있다.

ifort -fast -o convolve convolve_random_arrays.f90

없는 사람들을 위해 ifort사용할 수 있습니다

gfortran -O3 -ffast-math -o convolve convolve_random_arrays.f90

편집 2 : 런타임 감소는 이전에 잘못된 일을하고 잘못된 답변을 얻었 기 때문입니다. 올바른 방법으로 수행하면 속도가 느려집니다. 나는 여전히 C ++이 내 것보다 빠르다는 것을 믿을 수 없으므로 아마도 이번 주에 시간을 보내서 속도를 높이기 위해이 쓰레기를 조정하려고 할 것입니다.

편집 3 : BSD의 RNG (Sampo Smolander가 제안한대로)를 기반으로 하나 사용하여 RNG 섹션을 간단히 변경하고으로 상수 나누기를 제거 함으로써 Guy Sirtonm1C ++ 응답 과 동일한 런타임을 줄였습니다 . 정적 배열을 사용하면 (Sharpie에서 제안한대로) 런타임이 C ++ 런타임 아래로 떨어집니다! 예, 포트란! :디

편집 4 분명히 정수가 한계를 초과하므로 gfortran을 사용하여 컴파일하고 (잘못된 값) 올바르게 실행되지 않습니다. 작동하도록 수정했지만, ifort 11+ 또는 gfortran 4.7+ (또는 iso_fortran_envF2008 int64종류 를 허용하는 다른 컴파일러 )가 필요합니다.

코드는 다음과 같습니다.

program convolve_random_arrays
   use iso_fortran_env
   implicit none
   integer(int64), parameter :: a1 = 1103515245
   integer(int64), parameter :: c1 = 12345
   integer(int64), parameter :: m1 = 2147483648
   real, parameter ::    mi = 4.656612873e-10 ! 1/m1
   integer, parameter :: n = 6
   integer :: p, pmax, iters, i, nil(0:1), seed
   !integer, allocatable ::  F(:), S(:), FS(:)
   integer :: F(n), S(n+1), FS(2)

   !n = 6
   !allocate(F(n), S(n+1), FS(2))
   iters = 1000
   nil = 0

   !call init_random_seed()

   S = -1
   pmax = 2**(n+1)
   do p=1,pmax
      do i=1,iters
         F = rand_int_array(n)
         if(all(F==0)) then
            do while(all(F==0))
               F = rand_int_array(n)
            enddo
         endif

         FS = convolve(F,S)

         if(FS(1) == 0) then
            nil(0) = nil(0) + 1
            if(FS(2) == 0) nil(1) = nil(1) + 1
         endif

      enddo
      call permute(S)
   enddo

   print *,"first zero:",nil(0)
   print *," both zero:",nil(1)

 contains
   pure function convolve(x, h) result(y)
!x is the signal array
!h is the noise/impulse array
      integer, dimension(:), intent(in) :: x, h
      integer, dimension(abs(size(x)-size(h))+1) :: y
      integer:: i, j, r
      y(1) = dot_product(x,h(1:n-1))
      y(2) = dot_product(x,h(2:n  ))
   end function convolve

   pure subroutine permute(x)
      integer, intent(inout) :: x(:)
      integer :: i

      do i=1,size(x)
         if(x(i)==-1) then
            x(i) = 1
            return
         endif
         x(i) = -1
      enddo
   end subroutine permute

   function rand_int_array(i) result(x)
     integer, intent(in) :: i
     integer :: x(i), j
     real :: y
     do j=1,i
        y = bsd_rng()
        if(y <= 0.25) then
           x(j) = -1
        else if (y >= 0.75) then
           x(j) = +1
        else
           x(j) = 0
        endif
     enddo
   end function rand_int_array

   function bsd_rng() result(x)
      real :: x
      integer(int64) :: b=3141592653
      b = mod(a1*b + c1, m1)
      x = real(b)*mi
   end function bsd_rng
end program convolve_random_arrays

나는 이제 문제는 당신이 느린 as-molasses 파이썬의 사용을 중단하고 fast-as-electrons-can-move-move Fortran;)을 사용할 것이라고 가정합니다.


1
어쨌든 case 문이 생성기 함수보다 빠르지 않습니까? 분기 예측 / 캐시 라인 / 등의 속도 향상을 기대하지 않는 한?
OrangeDog

17
같은 기계에서 속도를 비교해야합니다. OP 코드에 대해 어떤 런타임을 얻었습니까?
nbubis

3
C ++ 답변은 매우 가벼운 난수 생성기를 구현합니다. 귀하의 답변은 컴파일러와 함께 제공되는 기본값을 사용했는데 느려질 수 있습니까?
Sampo Smolander

3
또한 C ++ 예제는 정적으로 할당 된 배열을 사용하는 것으로 보입니다. 컴파일 타임에 설정된 고정 길이 배열을 사용 해보고 시간이 부족한지 확인하십시오.
Sharpie

1
@KyleKanos @Lembik 문제는 포트란의 정수 할당이 int64 사양을 암시 적으로 사용하지 않기 때문에 변환하기 전에 숫자가 int32라는 것입니다. 코드는 integer(int64) :: b = 3141592653_int64모든 int64입니다. 이것은 포트란 표준의 일부이며 프로그래머가 형식 선언 프로그래밍 언어로 예상합니다. (이 대체 할 수 있습니다 과정의 기본 설정을 확인할 수)
제로 번째

69

파이썬 2.7-0.882s 0.283s

(OP의 원본 : 6.404s)

편집 : F 값을 사전 계산하여 Steven Rumbalski의 최적화. 이 최적화로 cpython은 pypy의 0.365를 능가합니다.

import itertools
import operator
import random

n=6
iters = 1000
firstzero = 0
bothzero = 0

choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))

for S in itertools.product([-1,1], repeat = n+1):
    for i in xrange(iters):
        F = random.choice(choicesF)
        if not sum(map(operator.mul, F, S[:-1])):
            firstzero += 1
            if not sum(map(operator.mul, F, S[1:])):
                bothzero += 1

print "firstzero", firstzero
print "bothzero", bothzero

OP의 원래 코드는 이러한 작은 배열을 사용하므로이 순수한 파이썬 구현에서 알 수 있듯이 Numpy를 사용하면 아무런 이점이 없습니다. 그러나 내 코드보다 세 배 더 빠른 이 numpy 구현 도 참조하십시오 .

또한 첫 번째 결과가 0이 아닌 경우 나머지 컨벌루션을 건너 뛰어 최적화합니다.


11
pypy를 사용하면 약 0.5 초 안에 실행됩니다.
Alistair Buxton

2
n = 10으로 설정하면 훨씬 더 확실한 속도를 얻을 수 있습니다. 나는 cpython 대 pypy에 대해 19 대 4.6을 얻습니다.

3
또 다른 최적화는 F4032 개만 있기 때문에 가능성을 미리 계산하는 것입니다. choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))루프 외부를 정의하십시오 . 그런 다음 innerloop에서 define을 정의하십시오 F = random.choice(choicesF). 나는 그러한 접근 방식으로 3 배의 속도를 얻습니다.
Steven Rumbalski

3
Cython에서 이것을 컴파일하는 것은 어떻습니까? 그런 다음 몇 가지 재치있는 정적 유형을 추가 하시겠습니까?
Thane Brimhall

2
함수에 모든 것을 넣고 마지막에 호출하십시오. 이것은 이름을 현지화하여 @riffraff가 제안한 최적화를 작동시킵니다. 또한 range(iters)루프 외부 에서 생성을 이동하십시오 . 전체적으로, 나는 당신의 아주 좋은 대답보다 약 7 %의 속도를 얻습니다.
WolframH

44

녹 : 0.011s

오리지널 파이썬 : 8.3

원래 파이썬을 그대로 번역했습니다.

extern crate rand;

use rand::Rng;

static N: uint = 6;
static ITERS: uint = 1000;

fn convolve<T: Num>(into: &mut [T], a: &[T], b: &[T]) {
    // we want `a` to be the longest array
    if a.len() < b.len() {
        convolve(into, b, a);
        return
    }

    assert_eq!(into.len(), a.len() - b.len() + 1);

    for (n,place) in into.mut_iter().enumerate() {
        for (x, y) in a.slice_from(n).iter().zip(b.iter()) {
            *place = *place + *x * *y
        }
    }
}

fn main() {
    let mut first_zero = 0;
    let mut both_zero = 0;
    let mut rng = rand::XorShiftRng::new().unwrap();

    for s in PlusMinus::new() {
        for _ in range(0, ITERS) {
            let mut f = [0, .. N];
            while f.iter().all(|x| *x == 0) {
                for p in f.mut_iter() {
                    match rng.gen::<u32>() % 4 {
                        0 => *p = -1,
                        1 | 2 => *p = 0,
                        _ => *p = 1
                    }
                }
            }

            let mut fs = [0, .. 2];
            convolve(fs, s, f);

            if fs[0] == 0 { first_zero += 1 }
            if fs.iter().all(|&x| x == 0) { both_zero += 1 }
        }
    }

    println!("{}\n{}", first_zero, both_zero);
}



/// An iterator over [+-]1 arrays of the appropriate length
struct PlusMinus {
    done: bool,
    current: [i32, .. N + 1]
}
impl PlusMinus {
    fn new() -> PlusMinus {
        PlusMinus { done: false, current: [-1, .. N + 1] }
    }
}

impl Iterator<[i32, .. N + 1]> for PlusMinus {
    fn next(&mut self) -> Option<[i32, .. N+1]> {
        if self.done {
            return None
        }

        let ret = self.current;

        // a binary "adder", that just adds one to a bit vector (where
        // -1 is the zero, and 1 is the one).
        for (i, place) in self.current.mut_iter().enumerate() {
            *place = -*place;
            if *place == 1 {
                break
            } else if i == N {
                // we've wrapped, so we want to stop after this one
                self.done = true
            }
        }

        Some(ret)
    }
}
  • 로 컴파일 --opt-level=3
  • 내 녹 컴파일러는 최근 밤입니다 : ( rustc 0.11-pre-nightly (eea4909 2014-04-24 23:41:15 -0700)정확히 말하면)

야간 버전의 녹을 사용하여 컴파일해야합니다. 그러나 코드가 잘못되었다고 생각합니다. 출력은 firstzero 27215 bothzero 12086에 가까운 값이어야합니다. 대신 27367 6481

@Lembik, 으악, 컨볼 루션에서 내 as와 bs를 혼합했습니다. 고정 (런타임을 눈에 띄게 변경하지 않음).
huon

4
녹의 속도를 매우 잘 보여줍니다.

39

C ++ (VS 2012) -0.0260.015

Python 2.7.6 / Numpy 1.8.1-12 초

속도 향상 ~ x800.

복잡한 배열이 매우 크면 간격이 훨씬 작아집니다.

#include <vector>
#include <iostream>
#include <ctime>

using namespace std;

static unsigned int seed = 35;

int my_random()
{
   seed = seed*1664525UL + 1013904223UL; // numerical recipes, 32 bit

   switch((seed>>30) & 3)
   {
   case 0: return 0;
   case 1: return -1;
   case 2: return 1;
   case 3: return 0;
   }
   return 0;
}

bool allzero(const vector<int>& T)
{
   for(auto x : T)
   {
      if(x!=0)
      {
         return false;
      }
   }
   return true;
}

void convolve(vector<int>& out, const vector<int>& v1, const vector<int>& v2)
{
   for(size_t i = 0; i<out.size(); ++i)
   {
      int result = 0;
      for(size_t j = 0; j<v2.size(); ++j)
      {
         result += v1[i+j]*v2[j];
      }
      out[i] = result;
   }
}

void advance(vector<int>& v)
{
   for(auto &x : v)
   {
      if(x==-1)
      {
         x = 1;
         return;
      }
      x = -1;
   }
}

void convolve_random_arrays(void)
{
   const size_t n = 6;
   const int two_to_n_plus_one = 128;
   const int iters = 1000;
   int bothzero = 0;
   int firstzero = 0;

   vector<int> S(n+1);
   vector<int> F(n);
   vector<int> FS(2);

   time_t current_time;
   time(&current_time);
   seed = current_time;

   for(auto &x : S)
   {
      x = -1;
   }
   for(int i=0; i<two_to_n_plus_one; ++i)
   {
      for(int j=0; j<iters; ++j)
      {
         do
         {
            for(auto &x : F)
            {
               x = my_random();
            }
         } while(allzero(F));
         convolve(FS, S, F);
         if(FS[0] == 0)
         {
            firstzero++;
            if(FS[1] == 0)
            {
               bothzero++;
            }
         }
      }
      advance(S);
   }
   cout << firstzero << endl; // This output can slow things down
   cout << bothzero << endl; // comment out for timing the algorithm
}

몇 가지 참고 사항 :

  • 임의의 함수가 루프에서 호출되어 매우 가벼운 선형 합동 생성기를 사용했습니다 (그러나 MSB를 관대하게 보았습니다).
  • 이것은 실제로 최적화 된 솔루션의 출발점 일뿐입니다.
  • 그렇게 오래 걸리지 않았다 ...
  • 나는 S[0]"최소 유효 숫자"숫자가되는 S의 모든 값을 반복합니다 .

자체 포함 된 예제에 대해이 기본 기능을 추가하십시오.

int main(int argc, char** argv)
{
  for(int i=0; i<1000; ++i) // run 1000 times for stop-watch
  {
      convolve_random_arrays();
  }
}

1
과연. OP 코드에서 배열의 크기가 작다는 것은 numpy를 사용하는 것이 실제로 파이썬보다 훨씬 느리다는 것을 의미합니다.
Alistair Buxton

2
이제 x800은 내가 말하는 것입니다!

아주 좋아요! advance함수로 인해 코드 속도가 향상 되었으므로 이제 코드가 사용자 코드보다 빠릅니다 : P (그러나 매우 좋은 경쟁입니다!)
Kyle Kanos

1
매트가 말한 것처럼 @lembik 예. C ++ 11 supprt와 메인 함수가 필요합니다. 이 프로그램을 실행하는 데 도움이 더 필요한 경우 알려주십시오.
Guy Sirton

2
방금 이것을 테스트했고 std :: vector. 대신 일반 배열을 사용하여 20 %를 더
줄일 수 있습니다

21

내 컴퓨터에서 0.015 초가 걸리고 OP의 원래 코드는 ~ 7.7 초입니다. 무작위 배열을 생성하고 동일한 루프에서 전개하여 최적화하려고 시도했지만 큰 차이는 없습니다.

첫 번째 배열은 정수를 가져 와서 이진수로 쓰고 1에서 -1로, 0에서 1로 변경합니다. 나머지는 매우 간단해야합니다.

편집 : 대신 필요없이 nint로서 int, 지금 우리가 nA는 매크로 정의 상수, 그래서 우리가 사용할 수있는 int arr[n];대신에 malloc.

Edit2 : 내장 rand()기능 대신 xorshift PRNG를 구현합니다. 또한 무작위 배열을 생성 할 때 많은 조건문이 제거됩니다.

컴파일 지침 :

gcc -O3 -march=native -fwhole-program -fstrict-aliasing -ftree-vectorize -Wall ./test.c -o ./test

암호:

#include <stdio.h>
#include <time.h>

#define n (6)
#define iters (1000)
unsigned int x,y=34353,z=57768,w=1564; //PRNG seeds

/* xorshift PRNG
 * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
 * Used under CC-By-SA */
int myRand() {
    unsigned int t;
    t = x ^ (x << 11);
    x = y; y = z; z = w;
    return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}

int main() {
    int firstzero=0, bothzero=0;
    int arr[n+1];
    unsigned int i, j;
    x=(int)time(NULL);

    for(i=0; i< 1<<(n+1) ; i++) {
        unsigned int tmp=i;
        for(j=0; j<n+1; j++) {
            arr[j]=(tmp&1)*(-2)+1;
            tmp>>=1;
        }
        for(j=0; j<iters; j++) {
            int randArr[n];
            unsigned int k, flag=0;
            int first=0, second=0;
            do {
                for(k=0; k<n; k++) {
                    randArr[k]=(1-(myRand()&3))%2;
                    flag+=(randArr[k]&1);
                    first+=arr[k]*randArr[k];
                    second+=arr[k+1]*randArr[k];
                }
            } while(!flag);
            firstzero+=(!first);
            bothzero+=(!first&&!second);
        }
    }
    printf("firstzero %d\nbothzero %d\n", firstzero, bothzero);
    return 0;
}

1
나는 이것을 테스트했다. 매우 빠르며 (시도 n = 10) 올바른 모양의 출력을 제공합니다. 감사합니다.

임의의 벡터가 모두 0이면 마지막 요소 만 재생성되므로이 구현은 원본을 따르지 않습니다. 원래 전체 벡터가 될 것입니다. 루프 do{}while(!flag)나 그 효과에 무언가 를 동봉해야합니다 . 런타임이 많이 변경 될 것으로 기대하지는 않습니다 (더 빨라질 수 있습니다).
Guy Sirton

Sirton 공지 사항 @Guy 전과 것을 continue;문 나는 할당 -1k, 그래서 k0에서 반복됩니다 다시.
ace_HongKongIndependence

1
@ 에이스 아! 네가 옳아. 나는 너무 빨리 스캐닝하고 있었고 :-) 가 -=아닌 것 같았 습니다 =-. while 루프가 더 읽기 쉽습니다.
Guy Sirton

17

제이

나는 컴파일 된 언어를 능가 할 것으로 기대하지 않으며 뭔가 0.09 초 미만을 얻는 데 기적의 기계가 필요하다고 말하지만 어쨌든이 J를 제출하고 싶습니다. 매우 매끄 럽기 때문입니다.

NB. constants
num =: 6
iters =: 1000

NB. convolve
NB. take the multiplication table                */
NB. then sum along the NE-SW diagonals           +//.
NB. and keep the longest ones                    #~ [: (= >./) #/.
NB. operate on rows of higher dimensional lists  " 1
conv =: (+//. #~ [: (= >./) #/.) @: (*/) " 1

NB. main program
S  =: > , { (num+1) # < _1 1                NB. all {-1,1}^(num+1)
F  =: (3&= - 0&=) (iters , num) ?@$ 4       NB. iters random arrays of length num
FS =: ,/ S conv/ F                          NB. make a convolution table
FB =: +/ ({. , *./)"1 ] 0 = FS              NB. first and both zero
('first zero ',:'both zero ') ,. ":"0 FB    NB. output results

이것은 지난 10 년 동안 랩톱에서 약 0.5 초가 걸리며, 그 답은 Python보다 약 20 배 빠릅니다. conv우리는 그것을 게으르게 (전체 컨볼 루션을 계산하고) 전체적으로 작성하기 때문에 대부분의 시간을 소비 합니다.

SF에 대해 알고 있으므로이 프로그램에 대해 특정 최적화를 수행하여 작업 속도를 높일 수 있습니다. 내가 생각해 낸 가장 좋은 것은 conv =: ((num, num+1) { +//.)@:(*/)"1— 대각선 합계에서 컨볼 루션의 가장 긴 요소에 해당하는 두 숫자를 선택하는 것입니다.


6
J : 항상 제출할 가치가있는 사람 :)
Vitaly Dyatlov

17

펄-9.3 배 빠름 ... 830 % 개선

고대 넷북에서 OP의 코드를 실행하는 데 53 초가 걸립니다. Alistair Buxton의 버전은 약 6.5 초가 소요되며 다음 Perl 버전은 약 5.7 초가 소요됩니다.

use v5.10;
use strict;
use warnings;

use Algorithm::Combinatorics qw( variations_with_repetition );
use List::Util qw( any sum );
use List::MoreUtils qw( pairwise );

my $n         = 6;
my $iters     = 1000;
my $firstzero = 0;
my $bothzero  = 0;

my $variations = variations_with_repetition([-1, 1], $n+1);
while (my $S = $variations->next)
{
  for my $i (1 .. $iters)
  {
    my @F;
    until (@F and any { $_ } @F)
    {
      @F = map +((-1,0,0,1)[rand 4]), 1..$n;
    }

    # The pairwise function doesn't accept array slices,
    # so need to copy into a temp array @S0
    my @S0 = @$S[0..$n-1];

    unless (sum pairwise { $a * $b } @F, @S0)
    {
      $firstzero++;
      my @S1 = @$S[1..$n];  # copy again :-(
      $bothzero++ unless sum pairwise { $a * $b } @F, @S1;
    }
  }
}

say "firstzero ", $firstzero;
say "bothzero ", $bothzero;

12

Python 2.7-Mkl 바인딩을 사용한 Numpy 1.8.1-0.086s

(OP의 원본 : 6.404s) (Buxton의 순수한 파이썬 : 0.270s)

import numpy as np
import itertools

n=6
iters = 1000

#Pack all of the Ses into a single array
S = np.array( list(itertools.product([-1,1], repeat=n+1)) )

# Create a whole array of test arrays, oversample a bit to ensure we 
# have at least (iters) of them
F = np.random.rand(int(iters*1.1),n)
F = ( F < 0.25 )*-1 + ( F > 0.75 )*1
goodrows = (np.abs(F).sum(1)!=0)
assert goodrows.sum() > iters, "Got very unlucky"
# get 1000 cases that aren't all zero
F = F[goodrows][:iters]

# Do the convolution explicitly for the two 
# slots, but on all of the Ses and Fes at the 
# same time
firstzeros = (F[:,None,:]*S[None,:,:-1]).sum(-1)==0
secondzeros = (F[:,None,:]*S[None,:,1:]).sum(-1)==0

firstzero_count = firstzeros.sum()
bothzero_count = (firstzeros * secondzeros).sum()
print "firstzero", firstzero_count
print "bothzero", bothzero_count

Buxton이 지적했듯이 OP의 원래 코드는 작은 배열을 사용하므로 Numpy를 사용하면 이점이 없습니다. 이 구현은 모든 F 및 S 사례를 어레이 지향 방식으로 한 번에 수행하여 numpy를 활용합니다. 이것은 파이썬에 대한 mkl 바인딩과 결합되어 매우 빠른 구현으로 이어집니다.

라이브러리를로드하고 인터프리터를 시작하는 데 0.076 초가 걸리므로 실제 계산은 C ++ 솔루션과 유사하게 ~ 0.01 초가 걸립니다.


mkl 바인딩은 무엇이며 우분투에서 어떻게 구할 수 있습니까?

실행이 python -c "import numpy; numpy.show_config()"NumPy와 버전이 등 BLAS / 아틀라스 / MKL에 대해 컴파일하는 경우 표시됩니다 ATLAS는 NumPy와 자유 가속 수학 패키지 에 링크 할 수 있습니다 , 인텔 MKL은 당신이 일반적으로 비용을 지불 할 필요 (당신이 학술 아니라면) 및 / scipy을 NumPy와 연결할 수 있습니다 .
alemi

쉬운 방법으로 아나콘다 파이썬 배포판을 사용하고 가속 패키지를 사용하십시오 . 또는 고려 된 배포판을 사용하십시오 .
alemi

Windows를 사용하는 경우 여기 에서 numpy를 다운로드 하십시오 . MKL에 링크 된 사전 컴파일 된 numpy 설치 프로그램.
가짜 이름

9

MATLAB 0.024 초

컴퓨터 1

  • 원본 코드 : ~ 3.3 초
  • Alistar Buxton의 코드 : ~ 0.51 초
  • Alistar Buxton의 새로운 코드 : ~ 0.25s
  • Matlab 코드 : ~ 0.024 초 (Matlab이 이미 실행 중)

컴퓨터 2

  • 원본 코드 : ~ 6.66s
  • Alistar Buxton의 코드 : ~ 0.64 초
  • Alistar Buxton의 새로운 코드 :?
  • Matlab : ~ 0.07 초 (Matlab이 이미 실행 중)
  • 옥타브 : ~ 0.07 초

나는 너무 느린 Matlab을 시도하기로 결정했습니다. 방법을 알고 있다면 Matlab에서 대부분의 루프를 제거하여 매우 빠릅니다. 그러나 메모리 요구 사항은 루프 솔루션보다 높지만 배열이 매우 크지 않은 경우에는 문제가되지 않습니다 ...

function call_convolve_random_arrays
tic
convolve_random_arrays
toc
end

function convolve_random_arrays

n = 6;
iters = 1000;
firstzero = 0;
bothzero = 0;

rnd = [-1, 0, 0, 1];

S = -1 *ones(1, n + 1);

IDX1 = 1:n;
IDX2 = IDX1 + 1;

for i = 1:2^(n + 1)
    F = rnd(randi(4, [iters, n]));
    sel = ~any(F,2);
    while any(sel)
        F(sel, :) = rnd(randi(4, [sum(sel), n]));
        sel = ~any(F,2);
    end

    sum1 = F * S(IDX1)';
    sel = sum1 == 0;
    firstzero = firstzero + sum(sel);

    sum2 = F(sel, :) * S(IDX2)';
    sel = sum2 == 0;
    bothzero = bothzero + sum(sel);

    S = permute(S); 
end

fprintf('firstzero %i \nbothzero %i \n', firstzero, bothzero);

end

function x = permute(x)

for i=1:length(x)
    if(x(i)==-1)
        x(i) = 1;
            return
    end
        x(i) = -1;
end

end

여기 내가하는 일이 있습니다.

  • Kyle Kanos 함수를 사용하여 S를 순회
  • 한 번에 모든 n * 반복자 난수 계산
  • 1 ~ 4를 [-1 0 1]에 매핑
  • 행렬 곱셈 사용 (요소 별 합 (F * S (1 : 5))은 F * S (1 : 5)의 행렬 곱셈과 같습니다. '
  • 둘 다의 경우 : 첫 번째 조건을 가득 채우는 멤버 만 계산

나는 당신이 matlab을 가지고 있지 않다고 가정한다. 나는 그것이 어떻게 비교되는지보고 싶어서 너무 나쁘다 ...

(처음 실행할 때 기능이 느려질 수 있습니다.)


잘하면 옥타브가 가능합니다 ...?

시도해 볼 수는 있지만 옥타브로 작업 한 적이 없습니다.
mathause

좋아, call_convolve_random_arrays.m이라는 파일에 코드를 넣고 옥타브에서 호출하면 옥타브에서와 같이 실행할 수 있습니다.
mathause

실제로 무언가를하려면 더 많은 코드가 필요합니까? "octave call_convolve_random_arrays.m"을 수행하면 아무것도 출력되지 않습니다. 참조 bpaste.net/show/JPtLOCeI3aP3wc3F3aGf

죄송합니다. 옥타브를 열고 실행하십시오. firstzero, zero 및 execution time을 표시해야합니다.
mathause

7

줄리아 : 0.30s

Op 's Python : 21.36 초 (Core2 duo)

71 배 빠른 속도

function countconv()                                                                                                                                                           
    n = 6                                                                                                                                                                      
    iters = 1000                                                                                                                                                               
    firstzero = 0                                                                                                                                                              
    bothzero = 0                                                                                                                                                               
    cprod= Iterators.product(fill([-1,1], n+1)...)                                                                                                                             
    F=Array(Float64,n);                                                                                                                                                        
    P=[-1. 0. 0. 1.]                                                                                                                                                                                                                                                                                                             

    for S in cprod                                                                                                                                                             
        Sm=[S...]                                                                                                                                                              
        for i = 1:iters                                                                                                                                                        
            F=P[rand(1:4,n)]                                                                                                                                                  
            while all(F==0)                                                                                                                                                   
                F=P[rand(1:4,n)]                                                                                                                                              
            end                                                                                                                                                               
            if  dot(reverse!(F),Sm[1:end-1]) == 0                                                                                                                           
                firstzero += 1                                                                                                                                                 
                if dot(F,Sm[2:end]) == 0                                                                                                                              
                    bothzero += 1                                                                                                                                              
                end                                                                                                                                                            
            end                                                                                                                                                                
        end                                                                                                                                                                    
    end
    return firstzero,bothzero
end

Arman의 Julia 답변을 수정했습니다. 먼저 전역 변수가 Julia의 유추와 JIT를 어렵게 만들기 때문에 함수에 래핑했습니다. 전역 변수는 언제든지 유형을 변경할 수 있으며 모든 작업을 확인해야합니다 . 그런 다음 익명 함수와 배열 이해를 제거했습니다. 실제로 필요하지는 않지만 여전히 느립니다. 줄리아는 지금 더 낮은 수준의 추상화로 더 빠릅니다.

더 빨리 만들 수있는 더 많은 방법이 있지만 괜찮은 일을합니다.


REPL에서 시간을 측정하거나 명령 줄에서 전체 파일을 실행하고 있습니까?
Aditya

둘 다 REPL에서.
user20768

6

좋아, Java를 여기에 표현해야한다고 생각하기 때문에 이것을 게시하고 있습니다. 나는 다른 언어로 끔찍하고 문제를 정확하게 이해하지 못한다고 고백 하므로이 코드를 수정하는 데 도움이 필요합니다. 나는 대부분의 코드 에이스 C 예제를 훔친 다음 다른 코드 조각을 빌렸다. 나는 그것이 가짜 파가 아닌 희망 ...

내가 지적하고 싶은 한 가지는 런타임에 최적화 된 언어가 최고 속도에 도달하기 위해 여러 번 / 여러 번 실행되어야한다는 것입니다. 빠른 달리기와 관련된 대부분의 것들이 여러 번 실행되기 때문에 완전히 최적화 된 속도 (또는 적어도 평균 속도)를 취하는 것이 정당하다고 생각합니다.

코드는 여전히 수정해야하지만 어쨌든 내가 몇 번이나 얻을 수 있는지 보았습니다.

Ubuntu에서 Intel (R) Xeon (R) CPU E3-1270 V2 @ 3.50GHz를 1000 번 실행 한 결과는 다음과 같습니다.

서버 : / tmp # time java8 -cp. 시험 장치

firstzero 40000

둘 다 20000

첫 실행 시간 : 41ms 마지막 실행 시간 : 4ms

실제 0m5.014s 사용자 0m4.664s 시스템 0m0.268s

내 엉터리 코드는 다음과 같습니다.

public class Tester 
{
    public static void main( String[] args )
    {
        long firstRunTime = 0;
        long lastRunTime = 0;
        String testResults = null;
        for( int i=0 ; i<1000 ; i++ )
        {
            long timer = System.currentTimeMillis();
            testResults = new Tester().runtest();
            lastRunTime = System.currentTimeMillis() - timer;
            if( i ==0 )
            {
                firstRunTime = lastRunTime;
            }
        }
        System.err.println( testResults );
        System.err.println( "first run time: " + firstRunTime + " ms" );
        System.err.println( "last run time: " + lastRunTime + " ms" );
    }

    private int x,y=34353,z=57768,w=1564; 

    public String runtest()
    {
        int n = 6;
        int iters = 1000;
        //#define iters (1000)
        //PRNG seeds

        /* xorshift PRNG
         * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
         * Used under CC-By-SA */

            int firstzero=0, bothzero=0;
            int[] arr = new int[n+1];
            int i=0, j=0;
            x=(int)(System.currentTimeMillis()/1000l);

            for(i=0; i< 1<<(n+1) ; i++) {
                int tmp=i;
                for(j=0; j<n+1; j++) {
                    arr[j]=(tmp&1)*(-2)+1;
                    tmp>>=1;
                }
                for(j=0; j<iters; j++) {
                    int[] randArr = new int[n];
                    int k=0;
                    long flag = 0;
                    int first=0, second=0;
                    do {
                        for(k=0; k<n; k++) {
                            randArr[k]=(1-(myRand()&3))%2;
                            flag+=(randArr[k]&1);
                            first+=arr[k]*randArr[k];
                            second+=arr[k+1]*randArr[k];
                        }
                    } while(allzero(randArr));
                    if( first == 0 )
                    {
                        firstzero+=1;
                        if( second == 0 )
                        {
                            bothzero++;
                        }
                    }
                }
            }
         return ( "firstzero " + firstzero + "\nbothzero " + bothzero + "\n" );
    }

    private boolean allzero(int[] arr)
    {
       for(int x : arr)
       {
          if(x!=0)
          {
             return false;
          }
       }
       return true;
    }

    public int myRand() 
    {
        long t;
        t = x ^ (x << 11);
        x = y; y = z; z = w;
        return (int)( w ^ (w >> 19) ^ t ^ (t >> 8));
    }
}

그리고 파이썬을 업그레이드하고 python-numpy를 설치 한 후 파이썬 코드를 실행하려고 시도했지만 다음과 같이 나타납니다.

server:/tmp# python tester.py
Traceback (most recent call last):
  File "peepee.py", line 15, in <module>
    F = np.random.choice(np.array([-1,0,0,1], dtype=np.int8), size = n)
AttributeError: 'module' object has no attribute 'choice'

의견 : 벤치마킹 (시스템에서 나노 버전 사용) 에 절대 사용 currentTimeMillis하지 않고 1k 실행으로 JIT를 참여시키기에 충분하지 않을 수 있습니다 (클라이언트의 경우 1.5k, 서버의 경우 10k가 기본값이지만 myRand를 호출하는 경우가 많음) JITed는 콜 스택을 컴파일하는 일부 함수를 컴파일해야합니다. 여기서 약한 PNRG는 부정하지만 C ++ 솔루션과 다른 것들도 부정 행위이므로 불공평하지 않습니다.
Voo

윈도우에서는 currentTimeMillis를 피해야하지만, 아주 미세한 입도 측정을위한 리눅스의 경우 나노 시간이 필요하지 않으며 나노 시간을 얻기위한 호출은 밀리 초보다 훨씬 비쌉니다. 그래서 나는 당신이 그것을 사용해서는 안된다는 것에 매우 동의하지 않습니다.
Chris Seline

특정 OS 및 JVM 구현을위한 Java 코드를 작성하고 있습니까? 실제로 HotSpot dev 트리를 확인하고 Linux가 단조롭지 않고 정확성을 보장하지 않는 milliSeconds를 사용 하기 때문에 실제로 어떤 OS를 사용하고 있는지 잘 모르겠습니다 gettimeofday(&time, NULL)(일부 플랫폼 / 커널에서 정확히 동일 함) currentTimeMillis Windows 구현과 같은 문제-너무 좋거나 아예 없습니다). 반면 nanoTime은 clock_gettime(CLOCK_MONOTONIC, &tp)Linux에서 벤치마킹 할 때 사용하는 것이 옳은 것도 분명합니다.
Voo

Linux 배포판이나 커널에서 Java를 코딩 한 이후로 문제가 발생하지 않았습니다.
Chris Seline

6

아래 Golang 코드에서 내 컴퓨터의 Golang 버전 45X의 Python :

package main

import (
"fmt"
"time"
)

const (
n     = 6
iters = 1000
)

var (
x, y, z, w = 34353, 34353, 57768, 1564 //PRNG seeds
)

/* xorshift PRNG
 * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
 * Used under CC-By-SA */
func myRand() int {
var t uint
t = uint(x ^ (x << 11))
x, y, z = y, z, w
w = int(uint(w^w>>19) ^ t ^ (t >> 8))
return w
}

func main() {
var firstzero, bothzero int
var arr [n + 1]int
var i, j int
x = int(time.Now().Unix())

for i = 0; i < 1<<(n+1); i = i + 1 {
    tmp := i
    for j = 0; j < n+1; j = j + 1 {
        arr[j] = (tmp&1)*(-2) + 1
        tmp >>= 1
    }
    for j = 0; j < iters; j = j + 1 {
        var randArr [n]int
        var flag uint
        var k, first, second int
        for {
            for k = 0; k < n; k = k + 1 {
                randArr[k] = (1 - (myRand() & 3)) % 2
                flag += uint(randArr[k] & 1)
                first += arr[k] * randArr[k]
                second += arr[k+1] * randArr[k]
            }
            if flag != 0 {
                break
            }
        }
        if first == 0 {
            firstzero += 1
            if second == 0 {
                bothzero += 1
            }
        }
    }
}
println("firstzero", firstzero, "bothzero", bothzero)
}

그리고 아래에서 복사 된 아래의 파이썬 코드 :

import itertools
import operator
import random

n=6
iters = 1000
firstzero = 0
bothzero = 0

choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))

for S in itertools.product([-1,1], repeat = n+1):
    for i in xrange(iters):
        F = random.choice(choicesF)
        if not sum(map(operator.mul, F, S[:-1])):
            firstzero += 1
            if not sum(map(operator.mul, F, S[1:])):
                bothzero += 1

print "firstzero", firstzero
print "bothzero", bothzero

그리고 아래 시간 :

$time python test.py
firstzero 27349
bothzero 12125

real    0m0.477s
user    0m0.461s
sys 0m0.014s

$time ./hf
firstzero 27253 bothzero 12142

real    0m0.011s
user    0m0.008s
sys 0m0.002s

1
사용에 대해 생각 "github.com/yanatan16/itertools"했습니까? 또한 이것이 여러 고 루틴에서 잘 작동한다고 말할 수 있습니까?
ymg

5

C # 0.135 초

Alistair Buxton의 일반 파이썬 기반 C # : 0.278s
Parallelized C # : 0.135s
Python from the question : 5.907s
Alistair의 일반 파이썬 : 0.853s

실제로이 구현이 올바른지 확실하지 않습니다. 결과를 아래에서 보면 출력이 다릅니다.

더 최적의 알고리즘이 있습니다. 방금 파이썬과 매우 유사한 알고리즘을 사용하기로 결정했습니다.

단일 스레드 C

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConvolvingArrays
{
    static class Program
    {
        static void Main(string[] args)
        {
            int n=6;
            int iters = 1000;
            int firstzero = 0;
            int bothzero = 0;

            int[] arraySeed = new int[] {-1, 1};
            int[] randomSource = new int[] {-1, 0, 0, 1};
            Random rand = new Random();

            foreach (var S in Enumerable.Repeat(arraySeed, n+1).CartesianProduct())
            {
                for (int i = 0; i < iters; i++)
                {
                    var F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    while (!F.Any(f => f != 0))
                    {
                        F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    }
                    if (Enumerable.Zip(F, S.Take(n), (f, s) => f * s).Sum() == 0)
                    {
                        firstzero++;
                        if (Enumerable.Zip(F, S.Skip(1), (f, s) => f * s).Sum() == 0)
                        {
                            bothzero++;
                        }
                    }
                }
            }

            Console.WriteLine("firstzero {0}", firstzero);
            Console.WriteLine("bothzero {0}", bothzero);
        }

        // itertools.product?
        // http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
        static IEnumerable<IEnumerable<T>> CartesianProduct<T>
            (this IEnumerable<IEnumerable<T>> sequences)
        {
            IEnumerable<IEnumerable<T>> emptyProduct =
              new[] { Enumerable.Empty<T>() };
            return sequences.Aggregate(
              emptyProduct,
              (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item }));
        }
    }
}

병렬 C # :

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConvolvingArrays
{
    static class Program
    {
        static void Main(string[] args)
        {
            int n=6;
            int iters = 1000;
            int firstzero = 0;
            int bothzero = 0;

            int[] arraySeed = new int[] {-1, 1};
            int[] randomSource = new int[] {-1, 0, 0, 1};

            ConcurrentBag<int[]> results = new ConcurrentBag<int[]>();

            // The next line iterates over arrays of length n+1 which contain only -1s and 1s
            Parallel.ForEach(Enumerable.Repeat(arraySeed, n + 1).CartesianProduct(), (S) =>
            {
                int fz = 0;
                int bz = 0;
                ThreadSafeRandom rand = new ThreadSafeRandom();
                for (int i = 0; i < iters; i++)
                {
                    var F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    while (!F.Any(f => f != 0))
                    {
                        F = Enumerable.Range(0, n).Select(_ => randomSource[rand.Next(randomSource.Length)]);
                    }
                    if (Enumerable.Zip(F, S.Take(n), (f, s) => f * s).Sum() == 0)
                    {
                        fz++;
                        if (Enumerable.Zip(F, S.Skip(1), (f, s) => f * s).Sum() == 0)
                        {
                            bz++;
                        }
                    }
                }

                results.Add(new int[] { fz, bz });
            });

            foreach (int[] res in results)
            {
                firstzero += res[0];
                bothzero += res[1];
            }

            Console.WriteLine("firstzero {0}", firstzero);
            Console.WriteLine("bothzero {0}", bothzero);
        }

        // itertools.product?
        // http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
        static IEnumerable<IEnumerable<T>> CartesianProduct<T>
            (this IEnumerable<IEnumerable<T>> sequences)
        {
            IEnumerable<IEnumerable<T>> emptyProduct =
              new[] { Enumerable.Empty<T>() };
            return sequences.Aggregate(
              emptyProduct,
              (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item }));
        }
    }

    // http://stackoverflow.com/a/11109361/1030702
    public class ThreadSafeRandom
    {
        private static readonly Random _global = new Random();
        [ThreadStatic]
        private static Random _local;

        public ThreadSafeRandom()
        {
            if (_local == null)
            {
                int seed;
                lock (_global)
                {
                    seed = _global.Next();
                }
                _local = new Random(seed);
            }
        }
        public int Next()
        {
            return _local.Next();
        }
        public int Next(int maxValue)
        {
            return _local.Next(maxValue);
        }
    }
}

테스트 출력 :

Windows (.NET)

C #은 Windows에서 훨씬 빠릅니다. 아마도 .NET이 모노보다 빠르기 때문일 것입니다.

사용자 및 시스템 타이밍이 작동하지 않는 것 같습니다 ( git bash타이밍에 사용됨 ).

$ time /c/Python27/python.exe numpypython.py
firstzero 27413
bothzero 12073

real    0m5.907s
user    0m0.000s
sys     0m0.000s
$ time /c/Python27/python.exe plainpython.py
firstzero 26983
bothzero 12033

real    0m0.853s
user    0m0.000s
sys     0m0.000s
$ time ConvolvingArrays.exe
firstzero 28526
bothzero 6453

real    0m0.278s
user    0m0.000s
sys     0m0.000s
$ time ConvolvingArraysParallel.exe
firstzero 28857
bothzero 6485

real    0m0.135s
user    0m0.000s
sys     0m0.000s

리눅스 (모노)

bob@phoebe:~/convolvingarrays$ time python program.py
firstzero 27059
bothzero 12131

real    0m11.932s
user    0m11.912s
sys     0m0.012s
bob@phoebe:~/convolvingarrays$ mcs -optimize+ -debug- program.cs
bob@phoebe:~/convolvingarrays$ time mono program.exe
firstzero 28982
bothzero 6512

real    0m1.360s
user    0m1.532s
sys     0m0.872s
bob@phoebe:~/convolvingarrays$ mcs -optimize+ -debug- parallelprogram.cs
bob@phoebe:~/convolvingarrays$ time mono parallelprogram.exe
firstzero 28857
bothzero 6496

real    0m0.851s
user    0m2.708s
sys     0m3.028s

1
나는 당신이 말한대로 코드가 정확하다고 생각하지 않습니다. 출력이 맞지 않습니다.

@Lembik Yea. 누군가가 어디에서 문제가 있는지 말해 줄 수 있다면 고맙겠습니다 .- 알아낼 수 없습니다 ( 해야 할 일에 대한 최소한의 이해 만으로는 도움이되지 않습니다).
Bob


나는 그것이 다른 파이썬 솔루션과 동일해야합니다 말할 수 @Lembik 난 그냥 지금까지, 그것의 모든 이상 갔어요 ... 지금 난 정말 혼란.
Bob

4

Haskell : 코어 당 ~ 2000x 속도 향상

'ghc -O3 -funbox-strict-fields -threaded -fllvm'으로 컴파일하고 '+ RTS -Nk'로 실행하십시오. 여기서 k는 머신의 코어 수입니다.

import Control.Parallel.Strategies
import Data.Bits
import Data.List
import Data.Word
import System.Random

n = 6 :: Int
iters = 1000 :: Int

data G = G !Word !Word !Word !Word deriving (Eq, Show)

gen :: G -> (Word, G)
gen (G x y z w) = let t  = x `xor` (x `shiftL` 11)
                      w' = w `xor` (w `shiftR` 19) `xor` t `xor` (t `shiftR` 8)
                  in (w', G y z w w')  

mask :: Word -> Word
mask = (.&.) $ (2 ^ n) - 1

gen_nonzero :: G -> (Word, G)
gen_nonzero g = let (x, g') = gen g 
                    a = mask x
                in if a == 0 then gen_nonzero g' else (a, g')


data F = F {zeros  :: !Word, 
            posneg :: !Word} deriving (Eq, Show)

gen_f :: G -> (F, G)       
gen_f g = let (a, g')  = gen_nonzero g
              (b, g'') = gen g'
          in  (F a $ mask b, g'')

inner :: Word -> F -> Int
inner s (F zs pn) = let s' = complement $ s `xor` pn
                        ones = s' .&. zs
                        negs = (complement s') .&. zs
                    in popCount ones - popCount negs

specialised_convolve :: Word -> F -> (Int, Int)
specialised_convolve s f@(F zs pn) = (inner s f', inner s f) 
    where f' = F (zs `shiftL` 1) (pn `shiftL` 1)

ss :: [Word]
ss = [0..2 ^ (n + 1) - 1]

main_loop :: [G] -> (Int, Int)
main_loop gs = foldl1' (\(fz, bz) (fz', bz') -> (fz + fz', bz + bz')) . parMap rdeepseq helper $ zip ss gs
    where helper (s, g) = go 0 (0, 0) g
                where go k u@(fz, bz) g = if k == iters 
                                              then u 
                                              else let (f, g') = gen_f g
                                                       v = case specialised_convolve s f
                                                               of (0, 0) -> (fz + 1, bz + 1)
                                                                  (0, _) -> (fz + 1, bz)
                                                                  _      -> (fz, bz)
                                                   in go (k + 1) v g'

seed :: IO G                                        
seed = do std_g <- newStdGen
          let [x, y, z, w] = map fromIntegral $ take 4 (randoms std_g :: [Int])
          return $ G x y z w

main :: IO ()
main = (sequence $ map (const seed) ss) >>= print . main_loop

2
따라서 4 개의 코어로 9000 개가 넘습니다 ! 옳은 방법은 없습니다.
Cees Timmerman

암달의 법칙에 따르면 병렬 처리 속도는 병렬 처리 장치의 수와 선형이 아닙니다. 대신 그들은 단지 반환 dimishing 제공
xaedes을

@xaedes 적은 수의 코어에 대해 속도 향상이 본질적으로 선형 인 것 같습니다
user1502040

3

루비

루비 (2.1.0) 0.277s
루비 (2.1.1) 0.281s
파이썬 (Alistair Buxton) 0.330s
파이썬 (alemi) 0.097s

n = 6
iters = 1000
first_zero = 0
both_zero = 0

choices = [-1, 0, 0, 1].repeated_permutation(n).select{|v| [0] != v.uniq}

def convolve(v1, v2)
  [0, 1].map do |i|
    r = 0
    6.times do |j|
      r += v1[i+j] * v2[j]
    end
    r
  end
end

[-1, 1].repeated_permutation(n+1) do |s|
  iters.times do
    f = choices.sample
    fs = convolve s, f
    if 0 == fs[0]
      first_zero += 1
      if 0 == fs[1]
        both_zero += 1
      end
    end
  end
end

puts 'firstzero %i' % first_zero
puts 'bothzero %i' % both_zero

3

PHP 없이 스레드가 완료되지 않았습니다

6.6 배 빠름

PHP의 v5.5.9 - 1.223 0.646 초;

vs

Python v2.7.6-8.072 초

<?php

$n = 6;
$iters = 1000;
$firstzero = 0;
$bothzero = 0;

$x=time();
$y=34353;
$z=57768;
$w=1564; //PRNG seeds

function myRand() {
    global $x;
    global $y;
    global $z;
    global $w;
    $t = $x ^ ($x << 11);
    $x = $y; $y = $z; $z = $w;
    return $w = $w ^ ($w >> 19) ^ $t ^ ($t >> 8);
}

function array_cartesian() {
    $_ = func_get_args();
    if (count($_) == 0)
        return array();
    $a = array_shift($_);
    if (count($_) == 0)
        $c = array(array());
    else
        $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;
}

function rand_array($a, $n)
{
    $r = array();
    for($i = 0; $i < $n; $i++)
        $r[] = $a[myRand()%count($a)];
    return $r;
}

function convolve($a, $b)
{
    // slows down
    /*if(count($a) < count($b))
        return convolve($b,$a);*/
    $result = array();
    $w = count($a) - count($b) + 1;
    for($i = 0; $i < $w; $i++){
        $r = 0;
        for($k = 0; $k < count($b); $k++)
            $r += $b[$k] * $a[$i + $k];
        $result[] = $r;
    }
    return $result;
}

$cross = call_user_func_array('array_cartesian',array_fill(0,$n+1,array(-1,1)));

foreach($cross as $S)
    for($i = 0; $i < $iters; $i++){
        while(true)
        {
            $F = rand_array(array(-1,0,0,1), $n);
            if(in_array(-1, $F) || in_array(1, $F))
                break;
        }
        $FS = convolve($S, $F);
        if(0==$FS[0]) $firstzero += 1;
        if(0==$FS[0] && 0==$FS[1]) $bothzero += 1;
    }

echo "firstzero $firstzero\n";
echo "bothzero $bothzero\n";
  • 사용자 정의 임의 생성기 (C 답변에서 도난당한)를 사용했으며 PHP는 짜증나고 숫자는 일치하지 않습니다.
  • convolve 조금 더 빠른 기능을 단순화
  • 0으로 만 배열을 확인하는 것도 매우 최적화되어 있습니다 ( $F$FS확인 참조 ).

출력 :

$ time python num.py 
firstzero 27050
bothzero 11990

real    0m8.072s
user    0m8.037s
sys 0m0.024s
$ time php num.php
firstzero 27407
bothzero 12216

real    0m1.223s
user    0m1.210s
sys 0m0.012s

편집하다. 두 번째 버전의 스크립트는 다음 용도로만 작동합니다 0.646 sec.

<?php

$n = 6;
$iters = 1000;
$firstzero = 0;
$bothzero = 0;

$x=time();
$y=34353;
$z=57768;
$w=1564; //PRNG seeds

function myRand() {
    global $x;
    global $y;
    global $z;
    global $w;
    $t = $x ^ ($x << 11);
    $x = $y; $y = $z; $z = $w;
    return $w = $w ^ ($w >> 19) ^ $t ^ ($t >> 8);
}

function array_cartesian() {
    $_ = func_get_args();
    if (count($_) == 0)
        return array();
    $a = array_shift($_);
    if (count($_) == 0)
        $c = array(array());
    else
        $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;
}

function convolve($a, $b)
{
    // slows down
    /*if(count($a) < count($b))
        return convolve($b,$a);*/
    $result = array();
    $w = count($a) - count($b) + 1;
    for($i = 0; $i < $w; $i++){
        $r = 0;
        for($k = 0; $k < count($b); $k++)
            $r += $b[$k] * $a[$i + $k];
        $result[] = $r;
    }
    return $result;
}

$cross = call_user_func_array('array_cartesian',array_fill(0,$n+1,array(-1,1)));

$choices = call_user_func_array('array_cartesian',array_fill(0,$n,array(-1,0,0,1)));

foreach($cross as $S)
    for($i = 0; $i < $iters; $i++){
        while(true)
        {
            $F = $choices[myRand()%count($choices)];
            if(in_array(-1, $F) || in_array(1, $F))
                break;
        }
        $FS = convolve($S, $F);
        if(0==$FS[0]){
            $firstzero += 1;
            if(0==$FS[1])
                $bothzero += 1;
        }
    }

echo "firstzero $firstzero\n";
echo "bothzero $bothzero\n";

3

F # 솔루션

CLR Core i7 4 (8) @ 3.4Ghz에서 x86으로 컴파일시 런타임은 0.030 초입니다.

코드가 올바른지 전혀 모른다.

  • 기능 최적화 (인라인 폴드)-> 0.026s
  • 콘솔 프로젝트를 통한 빌드-> 0.022s
  • 순열 배열 생성을위한 더 나은 알고리즘 추가-> 0.018s
  • Windows 용 모노-> 0.089
  • Alistair의 Python 스크립트 실행-> 0.259s
let inline ffoldi n f state =
    let mutable state = state
    for i = 0 to n - 1 do
        state <- f state i
    state

let product values n =
    let p = Array.length values
    Array.init (pown p n) (fun i ->
        (Array.zeroCreate n, i)
        |> ffoldi n (fun (result, i') j ->
            result.[j] <- values.[i' % p]
            result, i' / p
        )
        |> fst
    )

let convolute signals filter =
    let m = Array.length signals
    let n = Array.length filter
    let len = max m n - min m n + 1

    Array.init len (fun offset ->
        ffoldi n (fun acc i ->
            acc + filter.[i] * signals.[m - 1 - offset - i]
        ) 0
    )

let n = 6
let iters = 1000

let next =
    let arrays =
        product [|-1; 0; 0; 1|] n
        |> Array.filter (Array.forall ((=) 0) >> not)
    let rnd = System.Random()
    fun () -> arrays.[rnd.Next arrays.Length]

let signals = product [|-1; 1|] (n + 1)

let firstzero, bothzero =
    ffoldi signals.Length (fun (firstzero, bothzero) i ->
        let s = signals.[i]
        ffoldi iters (fun (first, both) _ ->
            let f = next()
            match convolute s f with
            | [|0; 0|] -> first + 1, both + 1
            | [|0; _|] -> first + 1, both
            | _ -> first, both
        ) (firstzero, bothzero)
    ) (0, 0)

printfn "firstzero %i" firstzero
printfn "bothzero %i" bothzero

2

Q, 0.296 세그

n:6; iter:1000  /parametrization (constants)
c:n#0           /auxiliar constant (sequence 0 0.. 0 (n))
A:B:();         /A and B accumulates results of inner product (firstresult, secondresult)

/S=sequence with all arrays of length n+1 with values -1 and 1
S:+(2**m)#/:{,/x#/:-1 1}'m:|n(2*)\1 

f:{do[iter; F:c; while[F~c; F:n?-1 0 0 1]; A,:+/F*-1_x; B,:+/F*1_x];} /hard work
f'S               /map(S,f)
N:~A; +/'(N;N&~B) / ~A is not A (or A=0) ->bitmap.  +/ is sum (population over a bitmap)
                  / +/'(N;N&~B) = count firstResult=0, count firstResult=0 and secondResult=0

Q는 컬렉션 지향 언어입니다 (kx.com)

관용적 Q를 탐구하기 위해 코드를 다시 작성했지만 다른 영리한 최적화는 없습니다.

스크립팅 언어는 실행 시간이 아닌 프로그래머 시간을 최적화합니다

  • Q는이 문제에 가장 적합한 도구는 아닙니다.

첫 번째 코딩 시도 = 승자는 아니지만 합리적인 시간 (약 30 배 속도 향상)

  • 통역사들 사이에서 매우 경쟁력
  • 중지하고 다른 문제를 선택하십시오

노트.-

  • 프로그램은 기본 시드 (repetable execs)를 사용합니다. 랜덤 생성기 사용을 위해 다른 시드를 선택하려면 \S seed
  • 결과는 2 int의 스 퀴언 스로 제공되므로 두 번째 값의 최종 i 접미사가 있습니다. 27421 12133i-> (27241, 12133)
  • 인터프리터 시작 시간을 계산하지 않습니다. \t sentence 그 문장이 소비 한 시간을 측정

매우 흥미로운 감사합니다.

1

줄리아 : 12.149 6.929s

속도 에 대한 그들의 주장 에도 불구하고 , 초기 JIT 컴파일 시간 은 우리를 뒤로 젖 힙니다 !

다음 Julia 코드는 프로그래밍 경험을 더 빠른 언어로 쉽게 옮길 수 있음을 보여주는 데모로 원래 Python 코드를 직접 번역 한 것입니다 (최적화되지 않음).

require("Iterators")

n = 6
iters = 1000
firstzero = 0
bothzero = 0

for S in Iterators.product(fill([-1,1], n+1)...)
    for i = 1:iters
        F = [[-1 0 0 1][rand(1:4)] for _ = 1:n]
        while all((x) -> round(x,8) == 0, F)
            F = [[-1 0 0 1][rand(1:4)] for _ = 1:n]
        end
        FS = conv(F, [S...])
        if round(FS[1],8) == 0
            firstzero += 1
        end
        if all((x) -> round(x,8) == 0, FS)
            bothzero += 1
        end
    end
end

println("firstzero ", firstzero)
println("bothzero ", bothzero)

편집하다

실행 n = 8시간은 32.935 초입니다. 이 알고리즘의 복잡도가 있음을 고려 O(2^n)하고 4 * (12.149 - C) = (32.935 - C), 여기서 C으로 JIT 컴파일 시간을 나타내는 상수이다. 에 대한 해결 C우리가 찾을 C = 5.2203동안 그 실제 실행 시간을 제안하는 것은, n = 66.929 s입니다.


줄리아가 저절로 들어가는 지 확인하기 위해 n을 8로 늘리는 것은 어떻습니까?

이것은 여기에서 많은 성능 팁을 무시합니다 : julia.readthedocs.org/en/latest/manual/performance-tips . 훨씬 더 나은 다른 Julia 항목도 참조하십시오. 제출은 감사합니다 :-)
StefanKarpinski

0

녹, 6.6ms, 1950 배속

Alistair Buxton 코드 를 Rust 로 직접 변환 한 것입니다 . 레이온 (두려움없는 동시성) 으로 여러 코어를 사용하는 것을 고려 했지만 성능이 향상되지 않았습니다. 아마도 이미 빠르기 때문입니다.

extern crate itertools;
extern crate rand;
extern crate time;

use itertools::Itertools;
use rand::{prelude::*, prng::XorShiftRng};
use std::iter;
use time::precise_time_ns;

fn main() {
    let start = precise_time_ns();

    let n = 6;
    let iters = 1000;
    let mut first_zero = 0;
    let mut both_zero = 0;
    let choices_f: Vec<Vec<i8>> = iter::repeat([-1, 0, 0, 1].iter().cloned())
        .take(n)
        .multi_cartesian_product()
        .filter(|i| i.iter().any(|&x| x != 0))
        .collect();
    // xorshift RNG is faster than default algorithm designed for security
    // rather than performance.
    let mut rng = XorShiftRng::from_entropy(); 
    for s in iter::repeat(&[-1, 1]).take(n + 1).multi_cartesian_product() {
        for _ in 0..iters {
            let f = rng.choose(&choices_f).unwrap();
            if f.iter()
                .zip(&s[..s.len() - 1])
                .map(|(a, &b)| a * b)
                .sum::<i8>() == 0
            {
                first_zero += 1;
                if f.iter().zip(&s[1..]).map(|(a, &b)| a * b).sum::<i8>() == 0 {
                    both_zero += 1;
                }
            }
        }
    }
    println!("first_zero = {}\nboth_zero = {}", first_zero, both_zero);

    println!("runtime {} ns", precise_time_ns() - start);
}

외부 의존성을 사용하는 Cargo.toml :

[package]
name = "how_slow_is_python"
version = "0.1.0"

[dependencies]
itertools = "0.7.8"
rand = "0.5.3"
time = "0.1.40"

속도 비교 :

$ time python2 py.py
firstzero: 27478
bothzero: 12246
12.80user 0.02system 0:12.90elapsed 99%CPU (0avgtext+0avgdata 23328maxresident)k
0inputs+0outputs (0major+3544minor)pagefaults 0swaps
$ time target/release/how_slow_is_python
first_zero = 27359
both_zero = 12162
runtime 6625608 ns
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 2784maxresident)k
0inputs+0outputs (0major+189minor)pagefaults 0swaps

6625608 ns는 약 6.6ms입니다. 이것은 1950 배 속도 향상을 의미합니다. 여기에는 가능한 많은 최적화가 있지만 성능보다는 가독성을 추구했습니다. 가능한 최적화 중 하나는 항상 n요소를 가지므로 선택을 저장하기 위해 벡터 대신 배열을 사용하는 것 입니다. Xorshift는 기본 HC-128 CSPRNG보다 빠르지 만 PRNG 알고리즘을 사용하는 것보다 속도가 느리기 때문에 XorShift 이외의 RNG를 사용할 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.