Python gerçekte ne kadar yavaş? (Diliniz ne kadar hızlı?)


149

Python / NumPy'de yazdığım bu kod var.

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)

Biri diğerinden daha uzun olan, belirli bir olasılık dağılımına sahip iki rasgele dizinin evrişim devrinin, birinci konumda 0, her iki konumda da 0 olduğu sayıdır.

Python'un hızlı bir şekilde kod yazması için korkunç bir dil olduğunu söyleyen bir arkadaşımla bahse girdim. Bilgisayarımda 9 saniye sürer. "Uygun bir dilde" yazılsa 100 kat daha hızlı yapılabileceğini söylüyor.

Buradaki zorluk, bu kodun gerçekten seçtiğiniz herhangi bir dilde 100 kat daha hızlı yapılıp yapılmadığını görmek. Kodunuzu test edeceğim ve en hızlı bir hafta sonra kazanacaksınız. Birisi 0.09'un altına düşerse o zaman otomatik olarak kazanır ve kaybederim.

durum

  • Python . Alistair Buxon tarafından 30 kat daha hızlı! En hızlı çözüm olmasa da aslında benim favorim.
  • Octave . @Thethos tarafından 100 kat daha hızlı.
  • Pas . @Dbaupp tarafından 500 kat daha hızlı.
  • C ++ . Guy Sirton tarafından 570 kez hızlandırılıyor.
  • C . @ 727 kat daha fazla hızlanıyor.
  • C ++ . @Stefan tarafından inanılmaz derecede hızlı.

En hızlı çözümler artık hassas bir şekilde zamana göre çok hızlı. Bu nedenle n'yi 10'a yükselttim ve en iyilerini karşılaştırmak için iters = 100000'i ayarladım. Bu önlem altında en hızlı olanlardır.

  • C . 7.5 by @ace.
  • C ++ . @Stefan tarafından 1s.

Benim Makine zamanlamaları benim makinede işletilecek. Bu, AMD FX-8350 Sekiz Çekirdekli İşlemciye standart bir ubuntu kurulumudur. Bu ayrıca kodunuzu çalıştırabilmem gerektiği anlamına geliyor.

Takip edilen mesajlar Bu yarışma x100 hız kazanmak için oldukça kolay olduğu için, hız gurusu uzmanlıklarını geliştirmek isteyenler için takipte bulundum. Python'un Gerçekten Ne Kadar Yavaş Olduğunu Görmek (Bölüm II)?

Yanıtlar:


61

C ++ bit büyüsü

Basit RNG ile 0.84ms, c ++ 11 std ile 1.67ms :: knuth

Hafif algoritmik modifikasyonlu 0.16ms (aşağıdaki düzenlemeye bakınız)

Piton uygulaması, donanımımda 7.97 saniyede çalışıyor. Bu, seçtiğiniz RNG'ye bağlı olarak 9488 - 4772 kat daha hızlı.

#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;
}

Ekstra kayıtlar için 64-bit olarak derleyin. Basit rasgele üreteci kullanırken, convolve () içindeki döngüler herhangi bir bellek erişimi olmadan çalışır, tüm değişkenler kayıtlarda saklanır.

Nasıl çalışır: saklamak yerine Sve Fhafıza içi diziler olarak, bir uint32_t içinde bitler olarak depolanır.
Çünkü S, en naz anlamlı bitler, bir set bitinin + 1'i ve bir setsiz bitin -1'i ifade ettiği durumlarda kullanılır.
F[-1, 0, 0, 1] dağılımını oluşturmak için en az 2 bit gerektirir. Bu, rasgele bitler üreterek ve en az 16 önemli (çağrılan r) ve 16 en önemli bit (çağrılan l) inceleyerek yapılır. Eğer l & ~rF'nin +1 ~l & rolduğunu varsayarsak F, -1 olduğunu varsayarsak . Aksi takdirde F0'dır. Bu aradığımız dağılımı oluşturur.

Şimdi S, posBitsF == 1 negBitsolan her konumda bir ayar biti ve F == -1 olan her yerde bir ayar biti var.

F * S(Burada * çarpımı belirtir) koşul altında +1 olarak değerlendirildiğini ispatlayabiliriz (S & posBits) | (~S & negBits). Ayrıca F * S-1 olarak değerlendirilen tüm durumlar için benzer bir mantık üretebiliriz . Ve son olarak, sonuçta sum(F * S)sadece eşit miktarda -1 ve + 1 olması durumunda 0 olarak değerlendirildiğini biliyoruz . Bu sadece +1 bit ve -1 bit sayısını karşılaştırarak hesaplamak çok kolaydır.

Bu uygulama 32 bit girdi kullanır ve nkabul edilen maksimum sayı 16'dır. Uygulamayı rastgele üretme kodunu değiştirerek 31 bite, uint32_t yerine uint64_t kullanarak 63 bite ölçeklemek mümkündür.

Düzenle

Aşağıdaki convolve işlevi:

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);
}

çalışma zamanını 0.160-0.161ms olarak keser. Manuel döngü unroll (yukarıda resimde değil) 0.150 yapar. Daha az önemsiz n = 10, iter = 100000 örneği 250ms'nin altında. Eminim ek çekirdeklerden faydalanarak 50ms'nin altına alabilirim ama bu çok kolay.

Bu, iç döngü dalını serbest bırakarak ve F ve S döngüsünü değiştirerek yapılır. Gerekli
değilse bothZero, tüm olası S dizilerinde seyrek döngü yaparak çalışma süresini 0.02ms'ye düşürebilirim.


3
Bir gcc dostu sürümü ve aynı zamanda komut satırınızın ne olacağını söyleyebilir misiniz? Şu anda test edebileceğimden emin değilim.

Bununla ilgili hiçbir şey bilmiyorum ama google bana __builtin_popcount’ın _mm_popcnt_u32 () yerine kullanabileceğini söylüyor.

3
Kod güncellendi, doğru popcnt komutunu seçmek için #ifdef anahtarını kullanın. -std=c++0x -mpopcnt -O232 bit modunda çalıştırmak için derler ve 1.01ms alır (elimde 64 bit GCC sürümüne sahip değilim).
Stefan

Çıktıyı yazdırır mısın? Aslında şu anda bir şey yapıp yapmadığından emin değilim :)

7
Açıkça bir büyücüsün. + 1
BurntPizza

76

Python2.7 + Numpy 1.8.1: 10.242 s

Fortran 90+: 0.029 s 0.003 s 0.022 s 0.010 s

Kahretsin, bahisini kaybettin! Burada da bir paralellik damlası değil, sadece düz Fortran 90+.

EDIT Diziye izin vermek için Guy Sirton'ın algoritmasını kullandım S(iyi buluyorum: D). Görünüşe göre -g -tracebackbu kodu yaklaşık 0.017 s'ye yavaşlatan aktif bayrak derleyicileri vardı . Şu anda, bunu derliyorum

ifort -fast -o convolve convolve_random_arrays.f90

Sahip olmayanlar ifortiçin kullanabilirsiniz

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

EDIT 2 : Çalışma süresindeki düşüş, daha önce yanlış bir şey yaptığım ve yanlış bir cevap aldığım içindi. Doğru şekilde yapmak, görünüşe göre daha yavaş. Hala C ++ 'ın benimkinden daha hızlı olduğuna inanamıyorum, bu yüzden muhtemelen bu hafta bunu hızlandırmak için bu işi mahvetmeye çalışmak için biraz zaman harcayacağım.

DÜZENLEME 3 : basitçe birini kullanarak RNG bölümünü değiştirerek BSD'lerin RNG dayalı (Sampo Smolander önerdiği gibi) tarafından sürekli bölünmeyi ortadan kaldırarak m1, ben aynı öncesinde zaman kesmek Guy Sirton tarafından C ++ cevap . Statik dizilerin kullanılması (Sharpie tarafından önerildiği şekilde), çalışma zamanını C ++ çalışma zamanının altına düşürür! Yay Fortran! : D

EDIT 4 Görünüşe göre bu (derli toplu) derleme ve doğru (yanlış değerler) çalıştırmak değil çünkü tamsayılar limitlerini aştı. Çalışmasını sağlamak için düzeltmeler yaptım, ancak bunun için ya ifort 11+ ya da gfortran 4.7+ (ya da izin veren başka bir derleyici iso_fortran_envve F2008 int64türüne sahip) olması gerekiyor.

İşte kod:

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

Sanırım şu anki soru, pekmez kadar yavaş Python kullanmayı bırakıp elektronlar kadar hızlı hareket edebilen Fortran;) kullanmayacağınızdır.


1
Case ifadesi yine de bir jeneratör fonksiyonundan daha hızlı olmaz mıydı? Bir tür şube tahmini / önbellek satırı / etc hızlandırması beklemiyorsanız?
OrangeDog

17
Aynı makinede hız karşılaştırılmalıdır. OP'nin kodu için hangi çalışma zamanını aldınız?
nbubis

3
C ++ cevabı kendi hafif hafif rasgele sayı üretecini uygular. Cevabınız, derleyici ile gelen varsayılanı kullandı, hangisi daha yavaş olabilir?
Sampo Smolander

3
Ayrıca, C ++ örneği statik olarak ayrılmış dizileri kullanıyor gibi görünüyor. Derleme zamanında ayarlanan sabit uzunlukta dizileri kullanmayı deneyin ve herhangi bir zamanda tıraş edip etmediğini görün.
Sharpie,

1
@KyleKanos @Lembik problem şu ki, fortran'daki tamsayı ataması dolaylı olarak int64 belirtimini kullanmıyor, bu nedenle herhangi bir dönüştürme yapılmadan önce sayıların int32 olması. Kod şöyle olmalıdır: integer(int64) :: b = 3141592653_int64tüm int64 için. Bu, fortran standardının bir parçasıdır ve programcı tarafından tip beyan edilen bir programlama dilinde beklenir. (Bu geçersiz kılabilir elbette o varsayılan ayarları dikkat edin)
sıfırıncı

69

Python 2.7 - 0.882'ler 0.283'ler

(OP orijinali: 6.404s)

Düzenleme: F değerleri önceden hesaplayarak Steven Rumbalski'nin optimizasyonu. Bu optimizasyon ile cpython, pypy'nin 0.365'lerini atıyor.

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'nin orijinal kodu bu kadar küçük dizileri kullanıyor, bu saf python uygulamasının gösterdiği gibi Numpy'yi kullanmanın bir faydası yok. Ama aynı zamanda kodumdan üç kat daha hızlı olan bu numpy uygulamasına bakın .

İlk sonuç sıfır değilse, evrişimin geri kalanını atlayarak da optimize ederim.


11
Pypy ile bu yaklaşık 0.5 saniye içinde çalışır.
Alistair Buxton

2
N = 10 olarak ayarladıysanız çok daha inandırıcı bir hız kazanırsınız.

3
Diğer bir optimizasyon, olasılıkları önceden hesaplamak olacaktır Fçünkü bunlardan sadece 4032 tanesi vardır. choicesF = filter(any, itertools.product([-1, 0, 0, 1], repeat=n))Döngülerin dışını tanımlayın . Sonra iç döngüde tanımlayın F = random.choice(choicesF). Böyle bir yaklaşımla 3 kat hız kazanıyorum.
Steven Rumbalski,

3
Bunu Cython'da derlemeye ne dersiniz? Öyleyse birkaç temkinli statik tip mi eklediniz?
Thane Brimhall

2
Her şeyi bir fonksiyona koyun ve sonunda arayın. Bu, isimleri yerelleştirir ve bu da @riffraff'in önerdiği optimizasyonu sağlar. Ayrıca, range(iters)döngü dışına oluşturma işlemini taşıyın . Tamamen çok güzel cevabınız üzerinden% 7'lik bir hız kazanıyorum.
WolframH

44

Pas: 0.011s

Orijinal Python: 8.3

Orijinal Python'un düz bir çevirisi.

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)
    }
}
  • İle derlendi --opt-level=3
  • Pas derleyicim son zamanlarda her gece : ( rustc 0.11-pre-nightly (eea4909 2014-04-24 23:41:15 -0700)kesin olarak)

Pas gece sürümü kullanarak derlemek için aldım. Ancak kodun yanlış olduğunu düşünüyorum. Çıkış Bunun yerine 27367 6481 verir 27215 bothzero 12086. firstzero yakın bir şey olmalı

@Lembik, hoppala, benim var as ve bkonvolüsyon karıştığını; ın sabit (çalışma zamanını belirgin şekilde değiştirmez).
huon

4
Pasın hızının çok güzel bir göstergesi.

39

C ++ (VS 2012) - 0.026 ile 0.015

Python 2.7.6 / Numpy 1.8.1 - 12'ler

Hızlanma ~ x800.

Kıvrımlı diziler çok büyük olsaydı, boşluk çok daha küçük olurdu ...

#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
}

Birkaç not:

  • Döngüde rastgele işlev çağrılır, bu yüzden çok hafif bir doğrusal eşlenik jeneratör (ama MSB'lere cömertçe baktım) için gittim.
  • Bu gerçekten optimize edilmiş bir çözüm için başlangıç ​​noktasıdır.
  • Bu kadar uzun sürmedi mi ...
  • S'nin tüm değerlerini S[0]"en az anlamlı" basamak olarak alarak yineliyorum.

Bağımsız bir örnek için bu ana işlevi ekleyin:

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

1
Aslında. OP kodundaki dizilerin minik boyutu, numpy kullanmanın aslında düz pythondan daha yavaş bir büyüklük sırası olduğu anlamına gelir.
Alistair Buxton

2
Şimdi x800 bahsettiğim şey bu!

Çok hoş! advanceİşleviniz nedeniyle
kodumun hızını artırdım

1
@lembik, Mat'ın dediği gibi evet. Sen C ++ 11 supprt ve bir ana işlevi gerekir. Bunu çalıştırmak için daha fazla yardıma ihtiyacınız olursa bana haber verin ...
Guy Sirton

2
Bunu yeni test ettim ve std :: vector yerine düz dizileri kullanarak% 20 daha fazla tıraş edebilirim.
PlasmaHH

21

C

OP'nin orijinal kodu ~ 7,7 s alarak makinemde 0,015 saniye sürer. Rasgele diziyi oluşturarak ve aynı döngüde kıvrılarak optimize etmeye çalıştım, ancak çok fazla bir fark yaratmıyor gibi görünüyor.

İlk dizi bir tamsayı alarak üretilir, ikilik olarak yazılır ve 1 ile -1 arasında ve 0 ile 1 arasında tümü ile değiştirilir. Gerisi çok basit olmalıdır.

Düzenleme: yerine sahip nbir şekilde int, şimdi elimizde nbir makro tanımlanmış sabit, bu yüzden kullanabilirsiniz olarak int arr[n];yerine malloc.

Edit2: Yerleşik yerine rand()fonksiyonu, bu artık bir xorshift PRNG uygular. Ayrıca, rastgele diziyi oluştururken çok sayıda koşullu ifade kaldırılır.

Talimatları derleyin:

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

Kod:

#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
Bunu test ettim. Çok hızlı (n = 10 deneyin) ve doğru görünen çıktı verir. Teşekkür ederim.

Bu uygulama orijinali takip etmez, çünkü rastgele vektörün tümü sıfır ise, yalnızca son eleman yeniden oluşturulur. Orijinalde bütün vektör olacaktır. Bu çevrimi do{}while(!flag)veya bu etkiye bir şey eklemeniz gerekir . Çalışma zamanını çok değiştireceğini sanmıyorum (daha hızlı hale getirebilir).
Guy Sirton

Sirton Bildirimi @Guy önce continue;açıklamaya ben atanan -1için k, bu yüzden k0 ile döngü tekrar.
ace_HongKongIndependence

1
@ ace ah! haklısın. Çok hızlı bir şekilde tarıyordum ve bunun :-)' -=den ziyade olduğu gibi görünüyordu =-. Döngü daha okunaklı olurdu.
Guy Sirton

17

J

Herhangi bir derlenmiş dili dövmeyi beklemiyorum ve bir şey bana bununla 0,09 saniyeden daha az bir zaman alabilmem için mucizevi bir makine alacağını söylüyor, ama yine de bu J'yi göndermek istiyorum, çünkü oldukça kaygan.

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

Bu, önceki on yıldaki bir dizüstü bilgisayarda yaklaşık 0,5 sn sürüyor, yanıttaki Python kadar 20x kadar hızlı sürüyor. Çoğu zaman harcanır convçünkü tembelce yazıyoruz (tüm evrişimi hesaplıyoruz) ve genel olarak.

Hakkında bir şey biliyoruz Sve Fbu program için özel optimizasyonlar yaparak işleri hızlandırabiliriz. Karşılaştığım en conv =: ((num, num+1) { +//.)@:(*/)"1iyisi - özellikle köşegen toplamlardan, evrişimin en uzun elementlerine karşılık gelen iki sayıyı seçmek - bu süreyi yaklaşık yarıya indirir.


6
J her zaman göndermeye değer, dostum :)
Vitaly Dyatlov

17

Perl - 9.3X daha hızlı ...% 830 iyileştirme

Eski netbook'umda OP kodunun çalışması 53 saniye sürüyor; Alistair Buxton'un sürümü yaklaşık 6.5 saniye sürüyor ve aşağıdaki Perl sürümü yaklaşık 5.7 saniye sürüyor.

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 bağlanma ile birlikte 1.8.1 numara - 0.086s

(OP orijinali: 6.404s) (Buxton'ın saf pitonu: 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'ın işaret ettiği gibi, OP'nin orijinal kodu bu kadar küçük dizileri kullanıyor, Numpy kullanmanın bir faydası yok. Bu uygulama, tüm F ve S vakalarını bir dizi yönelimli bir şekilde bir kerede yaparak uyuşukluktan yararlanır. Bu python için mkl bağları ile birlikte çok hızlı bir uygulama yol açar.

Ayrıca sadece kitaplıkları yüklemek ve tercümanı başlatmak için 0,076 saniye gerekir, böylece gerçek hesaplama C ++ çözümüne benzer şekilde ~ 0,01 saniye sürer.


Mkl bağları nelerdir ve ubuntuda nasıl bulabilirim?

Koşu python -c "import numpy; numpy.show_config()"Numpy sürümünüz vb blas / atlas / MKL karşı derlenmiş olup olmadığını gösterecektir ATLAS numpy ücretsiz hızlandırılmış matematik paketidir karşı bağlantılı olabilir , Intel MKL genellikle ödemek zorunda (bir akademik olmadıkça) ve numpy / scipy ile bağlantılı olabilir .
alemi

Kolay bir şekilde, anakonda piton dağılımını kullanın ve hızlandırıcı paketi kullanın . Veya entlught dağılımını kullanın .
alemi

Eğer penceredeyseniz, buradan numpy'yi indirin . MKL'ye bağlı önceden derlenmiş numpy kurucuları.
Sahte İsim

9

MATLAB 0.024'ler

Bilgisayar 1

  • Orijinal Kod: ~ 3.3 s
  • Alistar Buxton Kodu: ~ 0.51 s
  • Alistar Buxton'un yeni Kodu: ~ 0.25 s
  • Matlab Kodu: ~ 0.024 s (Matlab zaten çalışıyor)

Bilgisayar 2

  • Orijinal Kod: ~ 6.66 s
  • Alistar Buxton Kodu: ~ 0.64 s
  • Alistar Buxton'un yeni Kodu:
  • Matlab: ~ 0.07 s (Matlab zaten çalışıyor)
  • Oktav: ~ 0.07 s

Oh çok yavaş Matlab'ı denemeye karar verdim. Nasıl olduğunu biliyorsanız, döngülerin çoğundan kurtulabilirsiniz (Matlab'da), bu onu oldukça hızlı kılar. Ancak, bellek gereksinimleri döngüsel çözümlerden daha yüksektir, ancak çok büyük dizileriniz yoksa bu bir sorun olmayacak ...

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

İşte yaptığım şey:

  • S aracılığıyla izin vermek için Kyle Kanos işlevini kullanın
  • tüm n * iters rasgele sayıları bir kerede hesaplamak
  • harita 1 - 4 - [-1 0 0 1]
  • Matrix çarpımını kullanın (elementwise sum (F * S (1: 5)), F * S (1: 5) 'matris çarpımına eşittir
  • hemzero için: yalnızca ilk koşulu yerine getiren üyeleri hesapla

Sanırım matlabınız yok, ki bu onun nasıl karşılaştırıldığını görmek isterdim ...

(İlk çalıştırdığınızda işlev daha yavaş olabilir.)


Bunun için çalışmasını sağlayabilirseniz oktavım var ...?

Bir deneyebilirim - Yine de oktav ile hiç çalışmadım.
mathause

Tamam, kodu call_convolve_random_arrays.m adlı bir dosyaya koyarsam ve sonra oktavdan çağırırsam oktavdaki gibi çalıştırabilirim.
mathause

Bir şey yapması için daha fazla koda ihtiyacı var mı? "Octave call_convolve_random_arrays.m" yaptığımda hiçbir şey çıkmıyor. Bkz. Bpaste.net/show/JPtLOCeI3aP3wc3F3aGf

üzgünüm, oktav açmayı ve çalıştırmayı dene. Sıfır, hem sıfır hem de yürütme zamanı göstermelidir.
mathause

7

Julia: 0,30 s

Op'un Python'u: 21,36 s (Core2 ikilisi)

71x hızlanma

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'ın Julia cevabında bazı değişiklikler yaptım: Öncelikle, global değişkenler Julia'nın tip çıkarımını ve JIT'i zorlaştırdığı için bir fonksiyona dahil ettim: Global değişken herhangi bir zamanda tipini değiştirebilir ve her işlemin kontrol edilmesi gerekir. . Sonra anonim işlevlerden ve dizi kavramalarından kurtuldum. Gerçekten gerekli değiller ve hala oldukça yavaşlar. Julia şu an daha düşük seviyeli soyutlamalarla daha hızlı.

Bunu hızlandırmanın daha çok yolu var, ama bu iyi bir iş yapıyor.


REPL'de süreyi mi ölçüyorsunuz veya tüm dosyayı komut satırından mı çalıştırıyorsunuz?
Aditya

her ikisi de REPL'den.
user20768

6

Tamam, bunu gönderiyorum çünkü Java'nın burada temsil edilmesi gerektiğini düşünüyorum. Diğer dillerden çok kötüyüm ve sorunu tam olarak anlamadığımı itiraf ediyorum, bu yüzden bu kodu düzeltmek için biraz yardıma ihtiyacım olacak. Ace kodunun C örneğinin çoğunu çaldım ve sonra diğerlerinden bazı snippet'leri ödünç aldım. Umarım bu sahte bir pas değildir ...

İşaret etmek istediğim şeylerden biri, çalışma zamanında en iyi duruma gelen dillerin tam hıza çıkabilmek için birkaç kez çalıştırılması gerekmesidir. Bence en iyi hıza ulaşmanın haklı olduğunu düşünüyorum (veya en azından ortalama hız) çünkü hızlı koşmakla ilgilendiğiniz çoğu şey birkaç kez çalıştırılacak.

Kod hala düzeltilmeli ama ne zaman alacağımı görmek için yine de koştum.

İşte 1000 kez çalıştıran Ubuntu'da Intel (R) Xeon (R) İşlemci E3-1270 V2 @ 3.50GHz sonuçları:

sunucu: / tmp # zaman java8 -cp. test cihazı

firstzero 40000

hemzero 20000

ilk çalışma zamanı: 41 ms son çalışma zamanı: 4 ms

gerçek 0m5.014s kullanıcı 0m4.664s sys 0m0.268s

İşte benim berbat kodum:

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'u yükselttikten ve python-numpy kurduktan sonra python kodunu çalıştırmayı denedim ama şunu anlıyorum:

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'

Yorumlar: AslacurrentTimeMillis kıyaslama için kullanmayın (Sistemdeki nano sürümünü kullanın) ve 1k çalıştırma JIT'in dahil edilmesine yetmeyebilir (istemci için 1.5k ve sunucu için 10k varsayılanlar olacaktır, ancak myRand'ı yeterince sık çağırırsınız) Bazı fonksiyonlara neden olması gereken JITed, burada çalışabilecek olan çağrı kodunun derlenmesini sağlar.) Son fakat en azından zayıf PNRG hile yapıyor, ama C ++ çözümü ve diğerleri de öyle, sanırım bu haksızlık değil.
Voo

Pencerelerde currentTimeMillis'ten kaçınmanız gerekir, ancak çok ince ayrıntı ölçümleri için linux için nano zamana ihtiyacınız yoktur ve nano zaman alma çağrısı millis'ten çok daha pahalıdır. Bu yüzden ASLA kullanmamalısınız.
Chris Seline

Yani belirli bir işletim sistemi ve JVM uygulaması için Java kodu mu yazıyorsunuz? Aslında hangi işletim sistemini kullandığınızdan emin değilim , çünkü sadece HotSpot dev gettimeofday(&time, NULL)ağacımı kontrol ettim ve Linux monoton olmayan ve herhangi bir doğruluk garantisi vermeyen milliSeconds için kullandı (aynı platformlarda / çekirdeklerde aynıdır) currentTimeMillis Windows uygulaması gibi sorunlar - bu da ne iyi ne de ne olursa olsun. Diğer yandan, nanoTime, clock_gettime(CLOCK_MONOTONIC, &tp)Linux'ta kıyaslama yaparken kullanmanın doğru olanıdır .
Voo

Herhangi bir Linux dağıtımında veya çekirdeğinde java kodladığım için bu benim için hiçbir zaman sorun yaratmadı.
Chris Seline

6

Golang sürüm 45X python makinemde aşağıda Golang kodları:

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)
}

ve aşağıdaki python kodları yukarıdan kopyalanmıştır:

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

ve aşağıdaki zaman:

$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
kullanmayı düşündün "github.com/yanatan16/itertools"mü? Ayrıca bunun birden fazla gobinde iyi çalışacağını söyler miydin?
ymg

5

C # 0.135s

Alistair Buxton'ın düz pythonuna dayanan C # : 0.278s
Paralelleştirilmiş C #: 0.135s sorudan
Python: 5.907s
Alistair'in düz python: 0.853s

Aslında bu uygulamanın doğru olduğundan emin değilim - alt kısımdaki sonuçlara bakarsanız çıktısı farklıdır.

Kesinlikle daha uygun algoritmalar var. Python'a çok benzer bir algoritma kullanmaya karar verdim.

Tek dişli 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 }));
        }
    }
}

Paralel 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);
        }
    }
}

Test çıkışı:

Windows (.NET)

C #, Windows'ta çok daha hızlı. Muhtemelen .NET'in monodan daha hızlı olması nedeniyle.

Kullanıcı ve sys zamanlaması çalışmıyor gibi görünüyor ( git bashzamanlama için kullanılır ).

$ 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

Linux (mono)

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
Söylediğin gibi kodun doğru olduğunu sanmıyorum. Çıkışlar doğru değil.

@Lembik Yea. Birisi bana nerede yanlış olduğunu söylese bile, çok sevinirim - anlayamıyorum (ne yapması gerektiği konusunda asgari bir anlayışa sahip olmak).
Bob

Bunun .NET ile nasıl yapıldığını görmek ilginç olurdu. Yerel blogs.msdn.com/b/dotnet/archive/2014/04/02/…
Rick Minerich

@Lembik Daha yeni tüm Python çözümleriyle aynı olması gerektiğini söyleyebildiğim kadarıyla ... hepsini karıştırdım ... şimdi gerçekten kafam karıştı.
Bob

4

Haskell: Çekirdek başına ~ 2000x hızlanma

'Ghc -O3 -funbox-strict-fields -threaded -fllvm' ile derleyin ve '+ RTS -Nk' ile çalıştırın, burada k makinenizdeki çekirdek sayısıdır.

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
Yani 4 çekirdekli 9000'in üzerinde mi? Doğru olması mümkün değil.
Cees Timmerman

Amdahl yasası , paralelleştirme hızının, paralel işlem birimlerinin sayısına doğrusal olmadığını belirtir. bunun yerine yalnızca azalan getiri sağlarlar
xaedes

@xaedes Hızlanma, düşük çekirdek sayıları için esasen doğrusal görünüyor
user1502040

3

Yakut

Yakut (2.1.0) 0.277'ler
Yakut (2.1.1) 0.281'ler
Python (Alistair Buxton) 0.330'ler
Python (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

iş parçacığı PHP olmadan tam olmaz

6.6x daha hızlı

PHP v5.5.9 - 1.223 0.646 sn;

vs

Python v2.7.6 - 8.072 saniye

<?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";
  • Özel rastgele bir jeneratör kullanılmış (C cevabından çalınmış), PHP bir emiyor ve sayıları eşleşmiyor
  • convolve fonksiyon biraz daha hızlı olacak şekilde basitleştirildi
  • Yalnızca sıfırlarla dizi denetleme de çok optimize edilmiştir (bkz. $FVe $FSdenetimler).

Çıktılar:

$ 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

Düzenle. Komut dosyasının ikinci sürümü sadece bunun için çalışır 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 # çözümü

CLR Core i7 4 (8) @ 3.4 Ghz'da x86'ya derlendiğinde çalışma zamanı 0.030s'dir

Kodun doğru olup olmadığı hakkında hiçbir fikrim yok.

  • İşlevsel optimizasyon (satır içi katlama) -> 0.026s
  • Konsol Projesi Üzerinden Bina -> 0.022s
  • Permütasyon dizilerinin oluşturulması için daha iyi bir algoritma eklendi -> 0.018s
  • Windows için Mono -> 0,089
  • Alistair'in Python senaryosunu çalıştırıyor -> 0.259
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 seg

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, koleksiyona yönelik bir dildir (kx.com)

Kod deyimsel Q'yu patlatmak için yeniden yazıldı, ancak başka akıllıca optimizasyonlar olmadı

Komut dosyası dilleri, yürütme süresini değil programlayıcı süresini optimize eder

  • Q bu sorun için en iyi araç değil

İlk kodlama denemesi = kazanan değil, ancak makul bir süre (yaklaşık 30x hızlanma)

  • tercümanlar arasında oldukça rekabetçi
  • dur ve başka bir problem seç

NOTES.-

  • program varsayılan tohum kullanır (tekrarlanabilir çalıştırmalar) Rasgele jeneratör kullanımı için başka bir tohum seçmek için \S seed
  • Sonuç iki inçlik bir kare olarak verilmiştir, bu nedenle ikinci değerde son bir i-son eki vardır 27421 12133i -> (27241, 12133)
  • Tercüman başlangıcını sayma zamanı. \t sentence bu cümle tarafından tüketilen mesures süresi

Çok ilginç teşekkürler.

1

Julia: 12.149 6.929 s

Hızlandırma taleplerine rağmen , ilk JIT derleme zamanı bizi geri tuttu !

Aşağıdaki Julia kodunun, programlama deneyiminizi daha hızlı bir dile kolayca aktarabileceğinizin bir kanıtı olarak orijinal Python kodunun doğrudan bir çevirisi (optimizasyon yapılmadığını);

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)

Düzenle

Koşma n = 832.935 sn sürer. Bu algoritma karmaşıklığı göz önüne alındığında O(2^n), daha sonra 4 * (12.149 - C) = (32.935 - C), burada Ctam zamanında derleme süresini temsil eden bir sabittir. CBunu çözmek için C = 5.2203, gerçek yürütme süresinin n = 66,929 s olduğunu düşündürüyoruz .


Julia'nın kendi başına gelip gelmediğini görmek için n'den 8'e yükselmeye ne dersiniz?

Buradaki performans ipuçlarının çoğunu yok sayar: julia.readthedocs.org/en/latest/manual/performance-tips . Ayrıca önemli ölçüde daha iyi olan diğer Julia girişlerine de bakın. Teslim :-) olsa takdir edilir
StefanKarpinski

0

Pas, 6.6 ms, 1950x hızlanma

Alistair Buxton'ın kodunun Rust'a doğrudan çevrilmesi . Rayon ile birden fazla çekirdek kullanmayı düşünmüştüm (korkusuz eşzamanlılık!), Ancak bu performansı daha da arttırmadı, çünkü zaten çok hızlıydı.

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);
}

Ve Cargo.toml, harici bağımlılıkları kullandığım gibi:

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

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

Hız karşılaştırması:

$ 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, yaklaşık 6.6 ms'dir. Bu, 1950 kat hızlanma anlamına gelir. Burada olası birçok optimizasyon var, ancak performanstan ziyade okunabilirlik için gidiyordum. Olası bir optimizasyon, seçimleri saklamak için vektörler yerine dizileri kullanmak olacaktır, çünkü her zaman nöğeleri olacaktır . Xorshift, varsayılan HC-128 CSPRNG'den daha hızlı olduğu için, PRNG algoritmalarının en safından daha yavaş olduğu gibi, XorShift dışında RNG kullanmak da mümkündür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.