Python ช้าแค่ไหนจริง ๆ (ตอนที่ II)


52

นี่คือการติดตามถึงPython ช้าเพียงใด? (หรือวิธีการที่รวดเร็วเป็นภาษาของคุณ?)

มันกลับกลายเป็นว่ามันง่ายเกินไปที่จะรับ x100 speedup สำหรับคำถามสุดท้ายของฉัน สำหรับผู้ที่ชอบความท้าทาย แต่ต้องการบางสิ่งที่ยากกว่าที่พวกเขาสามารถใช้ทักษะระดับต่ำของพวกเขานี่คือส่วนที่ II ความท้าทายคือการได้รับการเร่งความเร็ว x100 สำหรับรหัสหลามต่อไปนี้ซึ่งผ่านการทดสอบในคอมพิวเตอร์ของฉัน

เพื่อให้ยากขึ้นฉันใช้ pypy ครั้งนี้ เวลาปัจจุบันสำหรับฉันคือ1 นาที 7 วินาทีโดยใช้ pypy 2.2.1

กฎระเบียบ

  1. คนแรกที่ส่งรหัสที่ฉันสามารถเรียกใช้ได้ถูกต้องและเร็วกว่า x100 เท่าบนเครื่องของฉันจะได้รับรางวัล 50 คะแนน
  2. ฉันจะให้รางวัลแก่รหัสที่เร็วที่สุดหลังจากผ่านไปหนึ่งสัปดาห์
import itertools 
import operator 
import random

n = 8 
m  = 8 
iters = 1000  

# creates an array of 0s with length m
# [0, 0, 0, 0, 0, 0, 0, 0]
leadingzerocounts = [0]*m

# itertools.product creates an array of all possible combinations of the 
# args passed to it.
#
# Ex:
#   itertools.product("ABCD", "xy") --> Ax Ay Bx By Cx Cy Dx Dy
#   itertools.product("AB", repeat=5) --> [
#    ('A', 'A', 'A', 'A', 'A'),
#    ('A', 'A', 'A', 'A', 'B'),
#    ('A', 'A', 'A', 'B', 'A'),
#    ('A', 'A', 'A', 'B', 'B'),
#    etc.
#   ]
for S in itertools.product([-1,1], repeat = n+m-1):
    for i in xrange(iters):
        F = [random.choice([-1,0,0,1]) for j in xrange(n)]

        # if the array is made up of only zeros keep recreating it until
        # there is at least one nonzero value.
        while not any(F):
            F = [random.choice([-1,0,0,1]) for j in xrange(n)]

        j = 0
        while (j < m and sum(map(operator.mul, F, S[j:j+n])) == 0):
            leadingzerocounts[j] +=1
            j += 1
print leadingzerocounts

ผลลัพธ์ควรคล้ายกับ

[6335185, 2526840, 1041967, 439735, 193391, 87083, 40635, 19694]

คุณต้องใช้เมล็ดพันธุ์แบบสุ่มในรหัสของคุณและตัวสร้างตัวเลขสุ่มใด ๆ ที่ดีพอที่จะให้คำตอบใกล้กับข้างต้นจะได้รับการยอมรับ

My Machineเวลาของฉันจะทำงานบนเครื่องของฉัน นี่คือการติดตั้ง Ubuntu มาตรฐานบนโปรเซสเซอร์ AMD FX-8350 Eight-Core นี่ก็หมายความว่าฉันต้องสามารถเรียกใช้รหัสของคุณได้

คำอธิบายของรหัส

รหัสนี้วนซ้ำในทุกอาร์เรย์ S ที่มีความยาว n + m-1 ที่ประกอบขึ้นเป็น -1s และ 1s สำหรับแต่ละอาร์เรย์ S นั้นจะสุ่มตัวอย่างอาร์เรย์สุ่มที่ไม่เป็นศูนย์จำนวน 1,000 ค่า F ของความยาว n ประกอบด้วย -1,0 หรือ 1 โดยมีความน่าจะเป็น 1/4, 1/2, / 14 ของการรับค่าแต่ละค่า จากนั้นคำนวณผลิตภัณฑ์ภายในระหว่าง F และแต่ละหน้าต่างของ S ความยาว n จนกระทั่งพบผลิตภัณฑ์ภายในที่ไม่เป็นศูนย์ มันเพิ่ม 1 ถึงleadingzerocountsทุกตำแหน่งที่พบว่าผลิตภัณฑ์ภายในเป็นศูนย์

สถานะ

  • Perl ชะลอตัว 2.7 เท่าโดย @tobyink (เทียบกับ pypy ไม่ใช่ cpython)

  • เจ . เพิ่มความเร็วได้ 39 ครั้งโดย @Eelvex

  • . ความเร็วเพิ่มขึ้น 59 เท่าโดย @ace
  • จูเลีย เร็วขึ้น 197 เท่าไม่รวมเวลาเริ่มต้นโดย @ อีกหนึ่งนาที 8.5 เท่าความเร็วรวมถึงเวลาเริ่มต้น (เร็วขึ้นโดยใช้โปรเซสเซอร์ 4 ตัวในกรณีนี้มากกว่า 8)
  • Fortran 438 ครั้งเร็วขึ้นโดย @ semi-extrinsic
  • Rpython เพิ่มความเร็ว 258 ครั้งโดย @primo
  • C ++ ความเร็วเพิ่มขึ้น 508 เท่าโดย @ilmale

(ฉันหยุดกำหนดเวลาการปรับปรุงใหม่เพราะมันเร็วเกินไปและเล็กเกินไป)


มันชี้ให้เห็นว่าการกำหนดเวลาต่ำกว่าหนึ่งวินาทีนั้นไม่น่าเชื่อถือและบางภาษามีค่าใช้จ่ายในการเริ่มต้น อาร์กิวเมนต์คือถ้าคุณจะรวมว่าคุณควรรวมเวลารวบรวมของ C / C ++ ฯลฯ นี่คือการกำหนดเวลาสำหรับรหัสที่เร็วที่สุดที่มีจำนวนการทำซ้ำเพิ่มขึ้นเป็น 100,000

  • จูเลีย 42 วินาทีโดย @ อีกนาที
  • C ++ 14 วินาทีโดย @GuySirton
  • Fortran 14 วินาทีโดย @ semi-extrinsic
  • C ++ 12 วินาทีโดย @ilmale
  • Rpython 18 วินาทีโดย @primo
  • C ++ 5s โดย @Stefan

ผู้ชนะคือ .. Stefan!

โพสต์ความท้าทายติดตาม คุณไปได้สูงแค่ไหน? (A + การเข้ารหัสขั้นตอนวิธีการท้าทาย) อันนี้ยากกว่า


3
คำอธิบายว่าควรใช้โค้ดใดเพื่อให้ได้ผลดีดังนั้นเราจึงสามารถเขียนใหม่ได้และไม่เพียงแค่ปรับแก้มัน
Einacio

6
" คนแรกที่ส่งรหัสที่ฉันสามารถเรียกใช้ได้ถูกต้องและเร็วกว่า x100 เท่าบนเครื่องของฉันชนะทันทีและการแข่งขันปิดลง " จุดประสงค์ของการปิดการแข่งขันคืออะไร ทำไมไม่ใช้กำหนดวันที่เหมือนกับคนอื่น ๆ ส่วนใหญ่เราจึงสามารถเห็นมันลดลงในภาษาอื่น ๆ ?
grovesNL

5
@Einacio นั่นเป็นความคิดที่ดี ฉันเปลี่ยนกฎซึ่งฉันหวังว่าจะไม่มีใครสนใจ

1
@ Lembik ฉันได้ปรับปรุงรุ่น Fortran ของฉันทำให้ 2x เร็วขึ้นอีกครั้งบนเครื่อง คุณสามารถจับเวลาอีกครั้งได้ไหม :)
กึ่ง extrinsic

1
เสร็จแล้ว @ semi-extrinsic

คำตอบ:


12

มายากล C ++ บิต

~ 16ms มัลติเธรด, 56ms เธรดเดียว ความเร็วขึ้น ~ 4000

(ความเร็วขึ้นอยู่กับรหัสแบบมัลติเธรดใน i7-2820QM ของฉันและ 1 นาที 9 วินาทีที่กล่าวถึงในคำถามเนื่องจากระบบของ OP มีประสิทธิภาพการทำงานเธรดเดียวที่แย่กว่า CPU ของฉัน แต่ประสิทธิภาพที่ดีขึ้นของเธรดหลายเธรดฉันคาดหวังว่าหมายเลขนี้จะถูกต้อง)

ส่วนที่มีหลายเธรดมีประสิทธิภาพค่อนข้างมากเนื่องจากการวางไข่ของเธรด ฉันอาจจะทำได้ดีกว่าโดยใช้ประโยชน์จากห้องสมุดงานของฉันที่กำหนดเอง แต่ที่หนึ่งมีข้อบกพร่องอยู่ภายใต้ระบบยูนิกซ์ .. สำหรับคำอธิบายและรหัสเหมือนกันเกือบจะไม่มีเกลียวอ้างถึงhttps://codegolf.stackexchange.com/a/26485/20965

แก้ไข

ฉันให้แต่ละเธรดเป็น RNG ของตัวเองและลดความยาวของบิตเป็น 32 ซึ่งลดเวลารันไทม์ลงเพียงไม่กี่มิลลิวินาที

#include <iostream>
#include <bitset>
#include <random>
#include <chrono>
#include <stdint.h>
#include <cassert>
#include <array>
#include <tuple>
#include <memory>
#include <thread>
#include <future>
#include <string.h>


#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



void convolve()
{
    static const unsigned threadCount = 32;
    static const unsigned n = 8;
    static const unsigned m = 8;
    static const unsigned totalIters = 1000;
    static_assert( n <= 16, "packing of F fails when n > 16.");
    static uint32_t fmask = (1 << n) -1; fmask |= fmask << 16;

    std::array< uint32_t, m * threadCount > out;
    std::vector< std::future<void> > threads;

    for( int threadId = 0; threadId < threadCount; threadId++)
    {
        threads.emplace_back( std::async( [&, threadId]
        {
            std::random_device rd;
            std::knuth_b gen(rd());
            uint32_t nextRandomNumber = gen();

            const unsigned iters = totalIters / threadCount;

            std::array< uint32_t, m > leadingZeros;
            for( auto& x : leadingZeros )
                x = 0;

            for( unsigned i = 0; i < iters; i++ )
            {
                // generate random bit mess
                uint32_t F;
                do {
                    // this funky looking construction shortens the dependancy chain of F
                    F = nextRandomNumber & fmask;
                    nextRandomNumber = gen();
                } 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 + m -1));
                // generate all possible N+1 bit strings
                // 1 = +1
                // 0 = -1
                while ( S-- )
                {
                    for( int shift = 0; shift < m; shift++ )
                    {
                        uint32_t s = (S >> shift) % ( 1 << n );
                        auto firstBits = (s & mask) ^ adjF;

                        if ( desiredBits == popcnt( firstBits ) )
                        {
                            leadingZeros[shift] = leadingZeros[shift] + 1;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }

            memcpy( out.data() + (threadId * m), leadingZeros.data(), sizeof( leadingZeros[0] ) * m );
        } ));

    };

    std::array< uint32_t, m > leadingZeros;
    for( auto& x : leadingZeros )
        x = 0;

    for( auto& thread : threads )
    {
        thread.wait();
    }

    for( int i = 0; i < (threadCount * m); i++ )
    {
        leadingZeros[i % m] += out[i];
    }


    for( auto x : leadingZeros )
        std::cout << x << ", ";

    std::cout << std::endl;
}

int main()
{
    typedef std::chrono::high_resolution_clock clock;
    int rounds = 100;

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


    auto start = clock::now();

    for( int i = 0; i < rounds; i++ )
        convolve();

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

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

    return 0;
}

ตัวอย่างผลลัพธ์:

   6317312, 2515072, 1034368, 434048, 190144, 85200, 39804, 19168,
   6226944, 2481408, 1031168, 438080, 192896, 86816, 40484, 19490,
   6321152, 2524672, 1045376, 442880, 195680, 88464, 41656, 20212,
   6330624, 2517504, 1031104, 430208, 187696, 83976, 38976, 18708,
   6304768, 2510336, 1030720, 433056, 190880, 86824, 40940, 19840,
   6272512, 2494720, 1028160, 432352, 189168, 84752, 39540, 19052,
   6233600, 2507520, 1046912, 447008, 198224, 89984, 42092, 20292,

ผลลัพธ์ไม่ถูกต้องฉันคิดว่าอาจมีข้อบกพร่องหรือไม่ เปรียบเทียบกับสิ่งที่อยู่ในคำถาม โดยเฉพาะคอลัมน์สุดท้ายควรเป็นตัวเลขที่ใกล้เคียงกับปี 19180

@ Lembik ฉันสามารถเห็นสิ่งที่คุณหมายถึง ฉันคิดว่าบางครั้งการสุ่มเอาท์พุทจะสุ่มไม่พอ ด้วยตัวสร้างแบบสุ่ม C ++ 11 มันทำงานได้ดี ฉันจะแก้ไขรหัสในวันนี้
สเตฟาน

ฉันได้รับ Stefan.cpp: 104: 101: ข้อผิดพลาด: 'memcpy' ไม่ได้ถูกประกาศในขอบเขตนี้ memcpy (out.data () + (threadId * m), LeadingZeros.data (), sizeof (LeadingZeros [0]) * m );

ฉันคิดว่าฉันต้องรวมสตริง ลองอีกครั้ง.
Stefan

คุณรวบรวมสิ่งนี้ด้วย g ++ -O3 -std = c ++ 0x -pthread -Wl, - ไม่จำเป็นต้องใช้ Stefan.cpp -o Stefan

16

C ++ x150 x450 x530

แทนที่จะใช้อาร์เรย์ฉันใช้บิต (และเวทมนต์มืด)
ขอบคุณ @ace สำหรับฟังก์ชั่นการสุ่มที่เร็วขึ้น

มันทำงานอย่างไร: บิตที่ 15 แรกของจำนวนเต็มsแทนอาร์เรย์S[15]; ศูนย์แสดงถึง -1, คนที่เป็นตัวแทนของ +1 อาเรย์Fสร้างในลักษณะที่คล้ายกัน แต่มีสองบิตสำหรับแต่ละสัญลักษณ์

  • 00 หมายถึง -1
  • 01 และ 10 หมายถึง 0
  • 11 หมายถึง 1

สาเหตุSและFมีการแสดงที่แตกต่างกันผมต้องแทรกด้วยตัวเองที่จะเทียบเคียงกับSF

  • 0 (-1) กลายเป็น 00 (-1 ในการแทนค่าF)
  • 1 (+1) กลายเป็น 11 (+1 ในรูปตัวแทนF)

ตอนนี้เราสามารถใช้ Carnot เพื่อคำนวณผลิตภัณฑ์ด้านใน โปรดจำไว้ว่าหนึ่งตัวแปรสามารถรับค่า 00 หรือ 11 เท่านั้น

0. 00 = 11 (-1 * -1 = +1)
0 01 = 10 (-1 * 0 = 0)
0 10 = 01 (-1 * 0 = 0)
0 11 = 00 (-1 * +1 = -1)
1 00 = 00 (+1 * -1 = -1)
1 10 = 10 (+1 * 0 = 0)
1 01 = 01 (+1 * 0 = 0)
1 11 = 11 (+1 * +1 = +1)

ดูเหมือนว่าไม่ใช่แฮคเกอร์ :)

สรุปสิ่งที่เป็นเพียงเกมของการเปลี่ยนแปลงและหน้ากากไม่มีอะไรซับซ้อนจริงๆ

#include <array>
#include <ctime>

// From standford bithacks
// http://graphics.stanford.edu/~seander/bithacks.html
inline int32_t interleaveBit(int32_t x)
{
   static const uint32_t B[] = { 0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF };
   x = (x | ( x << 8)) & B[3];
   x = (x | ( x << 4)) & B[2];
   x = (x | ( x << 2)) & B[1];
   x = (x | ( x << 1)) & B[0];
   return x | (x << 1);
}

inline int32_t sumOnes(int32_t v)
{
   static int b[] = { 1, 0, 0, 1};
   int s = 0;
   for( int i = 0; i < 8; ++i)
   {
      const int a = 3&(v>>(i*2));
      s += b[a];
   }
   return s;
}

inline int32_t sumArray(int32_t v)
{
   static int b[] = { -1, 0, 0, 1};
   int s = 0;
   for( int i = 0; i < 8; ++i)
   {
      const int a = 3&(v>>(i*2));
      s += b[a];
   }
   return s;
}

uint32_t x, y = 24252, z=57768, w=1564; //PRNG seeds

int32_t myRand()
{
   uint32_t t;
   t = x ^ (x<<1);
   x = y;
   y = z;
   z = w;
   w = w ^ ( w >> 19) ^ t ^ (t >> 8);
   return w;
}

int main()
{
   std::array<int, 8> leadingZero{0};
   x = static_cast<int32_t>(time(nullptr)); // seed PRNG
   const int maxS = 1 << 15;
   for(int s = 0; s < maxS; ++s)
   {
      const int32_t x = interleaveBit(s);
      for(int i = 0; i < 1000; ++i)
      {
         int32_t random;
         do
         {
            random = 0xFFFF & myRand();
         }while(sumOnes(random) == 0);
         int j = 7;
         while( j >= 0 )
         {
            const int32_t h = (x >> (j*2));
            const int32_t l = 0xFFFF & (~(random ^ h)); // inner product
            if(sumArray(l) == 0)
            {
               leadingZero[j]++;
            } else
            {
               break;
            }
            j--;
         }

      }
   }
   for(int i = 7; i >= 0; --i)
   {
      printf("%d ", leadingZero[i]);
   }
   printf("\n");
   return 0;
}

นี่คือตัวอย่างผลลัพธ์:

6332350 2525218 1041716 438741 192917 87159 41023 19908 

real 0m0.372s
user 0m0.371s
sys  0m0.001s

โปรแกรมได้รับการคอมไพล์ด้วย:

gcc -std=c++11 -O3 -msse4.2 -Wall -lstdc++ 26371.cpp -o fastPy

บน Fedora 20 ด้วย gcc 4.8.2 Cpu นั้นเป็น i7 8core

อาจเป็นไปได้ที่ฉันจะได้พารามิเตอร์ ms คอมไพเลอร์ tweaking

ขณะนี้เป็นเวลาแก้ปัญหา OP บนเครื่องของฉัน:

time pypy 26371.py
[6330609, 2523914, 1040885, 439303, 192708, 86987, 40710, 19498]

real 0m53.061s
user 0m53.016s
sys  0m0.022s

แก้ไข:

เพียงแค่เพิ่ม openmp และเปลี่ยนลำดับสำหรับฉันได้รับ x3 ซึ่งนำไปสู่การปรับปรุงประสิทธิภาพ x450 เมื่อเทียบกับรหัส OP : D ในกรณีนี้leadingZeroอาร์เรย์ต้องเป็นอะตอมมิก สุ่มทั่วโลก ... เป็นแบบสุ่มพวกเขาจะสุ่มมากขึ้น

 #pragma omp parallel for
 for(int i = 0; i < 1000; ++i)
 {
    int32_t random;
    do
    {
       random = 0xFFFF & myRand();
    }while(sumOnes(random) == 0);
    for(int s = 0; s < maxS; ++s)
    {
       const int32_t x = interleaveBit(s);
       int j = 7;
       while( j >= 0 )
       {
          const int32_t h = (x >> (j*2));
          const int32_t l = 0xFFFF & (~(random ^ h)); // inner product
          if( sumArray(l) == 0 )
          {
             leadingZero[j]++;
          } else
          {
             break;
          }
          j--;
       }
    }
 }

จำเป็นต้องเพิ่ม-fopenmpในธงคอมไพเลอร์


แก้ไข: 2 ในฐานะผู้แนะนำโดยผู้ใช้ 71404 ฉันได้เปลี่ยนฟังก์ชั่น sumOnes และ sumArray และตอนนี้มันเร็วกว่ามาก

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

ด้วย openmp ช้าลงทำให้อะตอมมิกส์เพิ่มโอเวอร์เฮดมากเกินไป

real  0m0.253s
user  0m1.870s
sys   0m0.001s

หากปราศจากอะตอมมิคก็ยิ่งเร็วขึ้น แต่ฉันก็รับผลที่ผิด

2137992 1147218 619297 321243 155815 70946 32919 15579

real   0m0.048s
user   0m0.338s
sys    0m0.001s

เพื่อให้เข้าใจ sumArray ให้พิจารณาว่า 16 บิตเป็นตัวแทนและอาร์เรย์ของตัวเลข 8 ตัว
00 มีหมายเลข 1 และแทน -1
01 และ 10 มีหนึ่ง 1 และแทน 0
11 มีสอง 1s และเป็นตัวแทน 1
ดังนั้นในตัวนับจำนวนบิตตั้งค่าเป็น 1 [ http://en.wikipedia.org/wiki/ Hamming_weight]และแต่ละกลุ่มที่เรานำออก 1. เด็ด

ซูโม่เป็นแค่มนต์ดำ

นี่คือการรวบรวมธงและรหัสล่าสุด

gcc -std = c ++ 11 -mfpmath = sse -O3 -flto -march = native -funroll-loops - ทั้งหมด -lstdc ++

#include <cstdint>
#include <cstdio>
#include <ctime>

inline int32_t interleaveBit(int32_t x)
{
   static const uint32_t B[] = { 0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF };
   x = (x | ( x << 8)) & B[3];
   x = (x | ( x << 4)) & B[2];
   x = (x | ( x << 2)) & B[1];
   x = (x | ( x << 1)) & B[0];
   return x | (x << 1);
}

inline int32_t sumOnes(int32_t v)
{
   /* 0xAAAA == 0b1010 1010 1010 1010 */
   return !!(0xAAAA & (v ^ ~(v << 1)));
}

inline int32_t sumArray(int32_t v)
{
   return __builtin_popcount(v) - 8;
}

uint32_t x, y = 24252, z = 57768, w = 1564; //PRNG seeds

int32_t myRand()
{
   uint32_t t;
   t = x ^ (x << 1);
   x = y;
   y = z;
   z = w;
   w = w ^ ( w >> 19) ^ t ^ (t >> 8);
   return w;
}

int main()
{
   int leadingZero[8] = { 0 };
   x = static_cast<int32_t>(time(nullptr)); // seed PRNG
   const int maxS = 1 << 15;
   for( int i = 0; i < 1000; ++i )
   {
      int32_t random;
      do
      {
         random = 0xFFFF & myRand();
      } while(sumOnes(random) == 0 );
      for( int s = 0; s < maxS; ++s )
      {
         const int32_t x = interleaveBit(s);
         int j = 7;
         while( j >= 0 )
         {
            const int32_t h = (x >> (j * 2));
            const int32_t l = 0xFFFF & (~(random ^ h)); // inner product
            if( sumArray(l) == 0 )
            {
               leadingZero[j]++;
            } else
            {
               break;
            }
            j--;
         }
      }
   }
   printf("[%d, %d, %d, %d, %d, %d, %d, %d]\n",
      leadingZero[7], leadingZero[6],
      leadingZero[5], leadingZero[4],
      leadingZero[3], leadingZero[2],
      leadingZero[1], leadingZero[0]);
   return 0;
}

ตอนนี้ฉันรอไม่ไหวที่จะทดสอบสิ่งนี้! น่าเศร้าที่นี้จะไม่กี่ชั่วโมง

1
ต่อไปนี้เป็นการแก้ไขที่แนะนำ แต่ฉันคิดว่ามันอาจจะเหมาะสมกว่าสำหรับความคิดเห็น คุณสามารถแทนที่ sumOnes ของคุณ sumArray ด้วยสิ่งต่อไปนี้ (ดูเหมือนจะให้ความเร็ว 2x กับฉันในเวอร์ชัน openmp) inline int32_t sumOnes(int32_t v) { /* 0xAAAA == 0b1010 1010 1010 1010 */ return !! (0xAAAA & (v ^ ~(v << 1))); } inline int32_t sumArray(int32_t v) { return __builtin_popcount(v) - 8; }ถูกแนะนำโดย @ user71404
ace_HongKongIndependence

@ user71404: ผู้สร้างโปรไฟล์กล่าวว่าโปรแกรมใช้เวลาทั้งหมดในฟังก์ชั่นทั้งสอง แต่ฉันเหนื่อยมากเมื่อวานนี้คิดว่าสิ่งที่ดีกว่านั้น ฉันจะลองเย็นนี้ (UTC) ขอบคุณ
ilmale

คุณคิดจะเปลี่ยนข้อมูลโค้ดที่สองเป็นสำเนาที่สมบูรณ์และวางรหัสได้หรือไม่ ฉันต้องทำอะไรผิดในความพยายามที่จะทำให้รหัส openmp ของคุณทำงานได้ซึ่งจะช่วยได้มาก

ดี ฉันคิดว่าสิ่งนี้สามารถทำได้ด้วยการดำเนินการบิต
Guy Sirton

10

จูเลีย: 0.7 วินาทีเร็วกว่า 120 เท่า

ดังที่ user20768 แสดงให้เห็นว่าพอร์ตที่ตรงไปตรงมาของรหัสไปยัง Julia นั้นเร็วกว่า PyPy ประมาณสองเท่า แต่เราสามารถทำได้ดีกว่านั้นมาก

function pleadingzerocounts(; n = 8,
                              m = 8,
                              iters = 1000)
  @parallel (+) for S = 1:2^(8+8-1)
    leading_counts = zeros(Int, m)
    F = Array(Int, n)
    for i = 1:iters
      flag = 0
      while flag == 0
        for i = 1:n
          v = (1-(rand(Int8)&3))%2
          @inbounds F[i] = v
          flag += v & 1
        end
      end
      for j = 1:m
        sum = 0
        for i = 1:n
          @inbounds sum += S & (1 << (j + i - 2)) > 0 ? F[i] : -F[i]
        end
        sum == 0 ?
          (leading_counts[j] += 1) :
          break
      end
    end
    leading_counts
  end
end

function main()
  # Warm up the JIT
  pleadingzerocounts()
  # Then go for real
  println(@time pleadingzerocounts())
end

คุณสามารถรันสิ่งนี้ได้โดยใช้julia -p 8 -e 'require("golf.jl");main()'(8 คือจำนวนโปรเซสที่คุณอาจต้องการเล่นด้วย) ในการวางจำหน่าย Julia รุ่นก่อนหน้านี้จะใช้เวลา 0.7s เทียบกับ 1m22s สำหรับ PyPy

หากคุณมีคอร์เพียงพอในคอมพิวเตอร์ของคุณและบางทีหมุน AWS สองสามอินสแตนซ์คุณควรจะสามารถกำจัดได้มากกว่านี้ :)


ฉันค่อนข้างแน่ใจว่าคุณกำลังวัดเวลาผิด Python กับ Pypy เป็นภาษาที่ใช้ JIT ด้วยเช่นกัน แต่การกำหนดเวลาโดย OP รวมถึงเวลารวบรวม JIT คุณไม่รวมมัน ฉันติดตั้งรุ่นล่าสุด Julia git และทดสอบรหัสของคุณและในเครื่องของฉันด้วยกระบวนการ 8 ขั้นตอนใช้เวลา 6.6 วินาทีให้เสร็จ แต่พิมพ์ "เวลาที่ผ่านไป 0.588 .. วินาที"
- extrinsic

ระยะเวลาของ Python นั้นรวมถึงการเริ่มต้น PyPy และการอุ่นเครื่อง JIT แต่ใช้เวลาไม่เกินวินาทีความแตกต่างในช่วงเวลาหนึ่งนาทีของรันไทม์นั้นเล็กน้อย ฉันดีใจถ้า OP เปลี่ยนวิธีการตั้งเวลาของ Python (จะไม่สร้างความแตกต่างใด ๆ ) แต่การรวมเวลาเริ่มต้นของ Julia จะไม่สมเหตุสมผล
อีกหนึ่งนาที

ฉันถาม OP ในความคิดเห็นต่อคำถามต้นฉบับและเขากล่าวว่า "การกำหนดเวลาควรรวมทุกอย่างสำหรับภาษา JIT" นอกจากนี้เขายังกล่าวว่าเขาจะสร้างความท้าทายใหม่ที่การแก้ปัญหาจะใช้เวลานานกว่า 1 วินาทีทำให้จูเลียออกจากการแข่งขัน
- extrinsic

ในกรณีนั้นทางออกที่ดีที่สุดคือการใช้อัลกอริธึมแบบอนุกรม - ซึ่งใช้เวลาประมาณ 2 วินาที ฉันจะเผยแพร่โค้ด แต่ตอนนี้การแข่งขันค่อนข้างซ้ำซ้อนเนื่องจากทุกคนรู้แล้วว่า C ++ บู๊ตเร็วกว่าทุกอย่าง
อีก

ฉันเพิ่งโพสต์โซลูชัน Fortran ของฉันดังนั้นฉันไม่เห็นว่าทำไมคุณไม่ควรโพสต์ Julia ที่เร็วที่สุด (ถ้าคุณมีรหัสอยู่แล้ว)
- extrinsic

5

C, 1.210 วินาที

ด้วยรหัสของ OP ที่ใช้ 1m45.729s บนเครื่องของฉัน

เรียบเรียง:

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

ขอขอบคุณเป็นพิเศษ: @dyp สำหรับการรวบรวมธงและแนวคิดสำหรับการปรับให้เหมาะสม

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

#define n (8)
#define m (8)
#define iters (1000)
int leadingzerocounts[m]; // declared as global so initialised to 0
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 dotproduct(int*F, int*S) {
    unsigned int i;
    int sum=0;
    for(i=0; i<n; i++) {
        sum+=F[i]*S[i];
    }
    return sum;
}

int main() {
    unsigned int i, j, tmp;
    x=(int)time(NULL); //seed PRNG

    int S[n+m-1];
    for(i=0; i<(1<<(n+m-1)); i++) {
        tmp=i;
        for(j=0; j<n+m-1; j++) {
            S[j]=(tmp&1)*(-2)+1;
            tmp>>=1;
        }
        for(j=0; j<iters; j++) {
            int F[n];
            unsigned int k, flag=0;
            do {
                for(k=0; k<n; k++) {
                    F[k]=(1-(myRand()&3))%2;
                    flag+=(F[k]&1);
                }
            } while(!flag);
            for(k=0; k<m&&!dotproduct(F, S+k); k++) {
                leadingzerocounts[k]++;
            }
        }
    }
    for(i=0; i<m; i++) printf("%d ", leadingzerocounts[i]);
    return 0;
}

ตัวอย่างผลลัพธ์:

6334411 2527506 1042239 439328 192914 87005 40847 19728

1
น่าสนใจจริง ๆ ฉันสามารถสังเกตคล้ายกันเมื่อวางแฟลกการเพิ่มประสิทธิภาพทั้งหมด ลอง-march=native -fwhole-program -fstrict-aliasing -ftree-vectorizeBtw ผมได้ลงไป <4 s โดยใช้บางส่วน C ++ 11 รวมทั้ง MT19937 uniform_int_distributionบวก
dyp

1
1.119s เพิ่มความเร็วประมาณ 59!

1
@ ใช่ใช่ฉันแค่ต้องการชี้ให้เห็นนี้ มันง่ายกว่าสำหรับฉันที่จะลองใช้ PRNGs ไลบรารี่มาตรฐานใน C ++ โปรดทราบว่าคุณสามารถใช้หนึ่งผลจำนวนเต็ม 32 บิตจาก PRNG ให้ผลิต 8 Fรายการสำหรับ
dyp

1
เนื่องจากnเท่ากับ8คุณอาจใช้ AVX (หรือ 2 * SSE) เพื่อคำนวณ dotproduct ด้วยที่Sเก็บข้อมูลที่เหมาะสม
Michael M.

2
รุ่น SSE, การเร่งความเร็วขนาดเล็ก: gist.github.com/anonymous/11394210 (อย่าลืมใส่smmintrin.h)
Michael M.

5

Perl

นี่ไม่มีที่ไหนใกล้เร็วเท่ากับโซลูชัน C แต่ค่อนข้างเร็วสำหรับภาษาที่ตีความในระดับสูงที่ฉันคิดว่า ประหยัดเวลาในการประมวลผลของ Python ได้ 40%

#!/usr/bin/env perl

use v5.10;
use strict;
use warnings;
use Algorithm::Combinatorics qw( variations_with_repetition );
use List::Util qw( any sum );

use constant {
  N        => 8,
  M        => 8,
  ITERS    => 1000,
};

my @leadingzerocounts;

my $variations = variations_with_repetition([-1, 1], N + M - 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;
    }

    my $j = 0;
    while ($j < M)
    {
      last if sum map $F[$_]*$S->[$j+$_], 0..N-1;
      $leadingzerocounts[$j++]++;
    }
  }
}

say join ", ", @leadingzerocounts;

อัลกอริทึม :: Combinatorics มีอยู่ใน Ubuntu ( sudo apt-get install libalgorithm-combinatorics-perl) โมดูลอื่น ๆ ที่ใช้เป็นโมดูลหลักของ Perl ดังนั้นควรติดตั้งเป็นส่วนหนึ่งของการติดตั้ง Ubuntu พื้นฐาน


1
มันจะไม่ส่งผลกระทบต่อความเร็ว แต่มัน0..N-1อยู่ในระยะสุดท้ายmapใช่ไหม คุณลืมไปuse warnings? :-) ตรรกะแม้ว่าใน OP Sเป็นความสับสนเลื่อนหน้าต่างไม่เคยได้รับไปยังองค์ประกอบสุดท้ายของ
2846289

อา ฉันเพิ่งค้นพบว่าอาร์เรย์มีขนาดไม่ตรงกันดังนั้นฉันจึงปิดการใช้งานwarningsเพื่อให้องค์ประกอบที่หายไปได้รับการปฏิบัติเป็นศูนย์ N-1ปรับปรุงสิ่งนี้ และมันจะปรับปรุงความเร็วให้ดีขึ้นเล็กน้อย - ตอนนี้เร็วกว่าการใช้ Python ประมาณ 40%
tobyink

ฉันคิดว่ารหัสของคุณต้องใช้ List :: Util รุ่นที่ทันสมัย ใน Ubuntu 14.04 ฉันได้รับ "ใด ๆ " ไม่ได้ถูกส่งออกโดยรายการ :: โมดูล Util

โอ้ใช่มันเป็นความจริง - คุณอาจต้องติดตั้งรายการ :: ใช้ประโยชน์จาก CPAN anyสามารถพบอีกทางเลือกหนึ่งในรายการ :: MoreUtils ซึ่งแม้ว่าจะไม่ใช่โมดูลหลักคือหนึ่งในโมดูล CPAN ที่ใช้บ่อยที่สุด
tobyink

4

จูเลีย: 4.66x ช้ากว่า!

ฉันเริ่มสงสัยสถิติในเว็บไซต์ของพวกเขาจริงๆ ...

โปรดทราบว่ารหัส Julia ต่อไปนี้เป็นการถอดรหัสโดยตรงของรหัส Python ของ OP อย่างมีประสิทธิภาพโดยไม่มีการเพิ่มประสิทธิภาพใด ๆ ฉันใช้time()ฟังก์ชันเพื่อยกเว้นเวลาเริ่มต้นที่ช้าของ Julia ...

srand(27182818284590)
t = time()

require("Iterators")

n = 8
m = 8
iters = 1000
bothzero = 0
leadingzerocounts = zeros(m)

for S in Iterators.product(fill([-1,1], n+m-1)...)
    for i = 1:iters
        F = [[-1 0 0 1][rand(1:4)] for j = 1:n]
        while all((x) -> x == 0, F)
            F = [[-1 0 0 1][rand(1:4)] for j = 1:n]
        end
        j = 1
        while j <= m && sum(map(*, F, S[j:j+n-1])) == 0
            leadingzerocounts[j] += 1
            j += 1
        end
    end
end

println(leadingzerocounts)

t = time() - t
println("$t seconds")

จูเลีย: 5 m 32.912 วิ

รหัสของ OP ใน PyPy: 1 m 11.506 s

จูเลียเอาท์พุท:

6332170
2525472
1041522
438761
193119
86873
40705
19662

7
+1 สำหรับ <s> ความไร้ยางอายของคุณ </s> กีฬา
ace_HongKongIndependence

ตัวแปรระดับโลก, การนำเข้าและความเข้าใจของอาเรย์นั้นช้า นี่ไม่ใช่วิธีที่คนทั่วไปจะเขียนโปรแกรม Julia เพื่อประสิทธิภาพ
Alex A.

4

RPython 0.187s (เร็วกว่า 258x)

แหล่งต้นฉบับ w / PyPy2.2.1: 1m 6.718s

ขณะนี้มีเธรดการสนับสนุนกลับสำหรับ Python มาตรฐานลดลง จำนวนเธรดผู้ปฏิบัติงานสามารถระบุได้เป็นพารามิเตอร์บรรทัดคำสั่งค่าเริ่มต้นคือสอง

from time import time, sleep
from math import fmod

from rpython.rlib.rthread import start_new_thread, allocate_lock, get_ident
class Random:
  __slots__ = ['s']

  def __init__(self, s=1):
    self.s = s

  def init_genrand(self, seed):
    self.s = seed

  def genrand32(self):
    # xorshift PRNG with period 2^32-1
    # see http://core.kmi.open.ac.uk/download/pdf/6250138.pdf
    self.s ^= (self.s << 13)
    self.s ^= (self.s >> 17)
    self.s ^= (self.s << 5)
    return self.s

class ThreadEnv:
  __slots__ = ['n', 'm', 'iters', 'counts', 'running', 'lock']

  def __init__(self):
    self.n = 8
    self.m = 8
    self.iters = 1000
    self.counts = [0]*8
    self.running = 0
    self.lock = None

env = ThreadEnv()
truth = [-1,0,0,1]

def main(argv):
  argc = len(argv)
  if argc < 4 or argc > 5:
    print 'Usage: %s N M ITERS [NUM_THREADS=2]'%argv[0]
    return 1

  if argc == 5:
    num_threads = int(argv[4])
  else:
    num_threads = 2

  env.n = int(argv[1])
  env.m = int(argv[2])
  env.iters = int(argv[3]) // num_threads
  env.counts = [0]*env.m
  env.lock = allocate_lock()

  for i in xrange(num_threads-1):
    start_new_thread(run,())
    env.running += 1

  env.running += 1

  # use the main process as a worker
  run()

  # wait for any laggers
  while env.running:
    sleep(0.01)

  print env.counts
  return 0

def run():
  n, m, iters = env.n, env.m, env.iters
  counts = [0]*m
  sbits = [0]*(n+m-1)

  random = Random()
  seed = int(fmod(time(), 2147483.648)*1000) ^ get_ident()
  random.init_genrand(seed)

  for S in xrange(1<<n+m-1):
    i = 0
    sbit = 0
    while not sbit:
      sbits[i] ^= 3
      sbit = sbits[i]
      i += 1

    for i in xrange(iters):
      f = 0
      while not f:
        F = random.genrand32()

        G, x = F, 0
        for k in xrange(n):
          x += truth[(G&3)^sbits[k]]
          f |= x
          G >>= 2

      if not x:
        counts[0] += 1
        for j in xrange(1, m):
          G, x = F, 0
          for k in xrange(j, n+j):
            x += truth[(G&3)^sbits[k]]
            G >>= 2
          if x: break
          counts[j] += 1

  # passing True stalls until a lock can be obtained
  env.lock.acquire(True)

  for i in xrange(m):
    env.counts[i] += counts[i]
  env.running -= 1

  env.lock.release()

def target(*args):
  return main, None

RPythonเป็นชุดย่อย จำกัด ของงูใหญ่ซึ่งสามารถแปลและเรียบเรียง C แล้วใช้RPython toolchain วัตถุประสงค์ของมันคือเพื่อช่วยในการสร้างล่ามภาษา แต่มันยังสามารถใช้ในการรวบรวมโปรแกรมง่าย ๆ เช่นเดียวกับข้างต้น คุณสมบัติ 'นักเล่น' ส่วนใหญ่ของ Python เช่นitertoolsหรือแม้กระทั่งmapไม่สามารถใช้งานได้

หากต้องการคอมไพล์ให้สร้างโลคัลโคลนของที่เก็บ pypyปัจจุบันและรันต่อไปนี้:

$ pypy %PYPY_REPO%/rpython/bin/rpython --thread convolution.py

ปฏิบัติการที่เกิดขึ้นจะมีชื่อconvolution-cหรือคล้ายกันในไดเรกทอรีการทำงานปัจจุบัน

ฉันได้กำหนดตัวแปรอินพุตดังนั้นโปรแกรมควรจะเรียกใช้เป็น:

convolution-c 8 8 1000

เพื่อให้ตรงกับรหัสตัวอย่าง


หมายเหตุการใช้งาน

S in itertools.product([-1,1], repeat = n+m-1)กลายเป็นS in xrange(1<<n+m-1)ตีความSแผนที่บิต: [ 0, 1] → [ -1, 1]

ในทำนองเดียวกันFนี้ยังมีแผนที่บิตด้วยกันสองบิตคิดเป็นค่าเดียว:
[ 00, 01, 10, 11] → [ -1, 0, 0, 1]

ตารางความจริงถูกใช้เพื่อค้นหาผลิตภัณฑ์แทนที่จะทำการล่อแหลม

เนื่องจากมีการใช้เลขจำนวนเต็มแบบ 32 บิตnอาจไม่มากกว่า 15 และn+mไม่เกิน 31 การสนับสนุนจำนวนเต็มแบบกำหนดเองสามารถทำได้ด้วยrpython.rlib.rbigintโมดูลหากจำเป็น

Fย้ำแรกของวงดอทผลิตภัณฑ์คลี่และรวมกับการทดสอบเป็นโมฆะของ

homebrew PRNG ใช้แหล่งที่มาแสดง ผู้เขียนบทความนี้แสดงให้เห็นว่ามีระยะเวลา 2 32 -1 และอ้างว่าผ่านการทดสอบของฮาร์ดฮาร์ดทั้งหมดยกเว้นหนึ่งครั้งถึงแม้ว่าฉันจะไม่ได้รับการยืนยันเป็นการส่วนตัวก็ตาม

การสุ่มเมล็ดจะเปลี่ยนทุกมิลลิวินาทีซึ่งใกล้เคียงกับการใช้การประทับเวลาจะช่วยให้ นอกจากนี้ผู้ปฏิบัติงานแต่ละเธรดxorid กระบวนการของพวกเขาด้วยค่านี้เพื่อให้แน่ใจว่าพวกเขาแต่ละคนมีเมล็ดที่แตกต่างกัน


ตัวอย่างกำหนดเวลา

2 คนงานหัวข้อ:

$ timeit convolution-c 8 8 1000 2
[6331845, 2526161, 1042330, 440018, 193724, 87147, 40943, 19603]

Elapsed Time:     0:00:00.375
Process Time:     0:00:00.687
System Calls:     6927

4 คนงานหัวข้อ:

$ timeit convolution-c 8 8 1000 4
[6334565, 2527684, 1043502, 440216, 193225, 87398, 40799, 19338]

Elapsed Time:     0:00:00.218
Process Time:     0:00:00.796
System Calls:     3417

8 คนงานหัวข้อ:

$ timeit convolution-c 8 8 1000 8
[6327639, 2522483, 1039869, 437884, 192460, 86771, 40420, 19403]

Elapsed Time:     0:00:00.187
Process Time:     0:00:00.734
System Calls:     3165

แหล่งดั้งเดิมของ OP:

$ timeit pypy convolution-orig.py
[6330610, 2525644, 1041481, 438980, 193001, 86622, 40598, 19449]

Elapsed Time:     0:01:06.718
Process Time:     0:01:06.718
System Calls:     11599808

เวลาสำหรับการทำซ้ำ 100,000 ครั้ง:

$ timeit convolution-c 8 8 100000 8
[633156171, 252540679, 104129386, 43903716, 19307215, 8709157, 4072133, 1959124]

Elapsed Time:     0:00:16.625
Process Time:     0:01:02.406
System Calls:     171341

ฉันไม่เคยเห็นโปรแกรม rpython มาก่อน มันเยี่ยมมาก ตอนนี้มีโปรแกรมไพ ธ อนเทียบเท่าที่ pypy สามารถทำงานใน 1.03s ได้หรือไม่

@ Lembik ฉันต้องการดู ฉันคิดว่า 4.7 วินาทีนั้นค่อนข้างดีเมื่อพิจารณาความพยายามครั้งแรกของฉันที่งูหลามบริสุทธิ์คือ ~ 15s
primo

ใช่ขออภัยในความล่าช้า ฉันยังไม่ได้ใช้รหัส แต่จะเร็วที่สุดเท่าที่จะเป็นไปได้

คุณควรลองเพิ่ม JIT ตอนนี้จะเร็ว!
kirbyfan64sos

@ Lembik ขอบคุณสำหรับการกล่าวถึง;) ออกมาจากความอยากรู้อยากเห็นมันทำงานเร็วที่สุดด้วย 4 หัวข้องานหรือ 8?
โม่

3

จูเลีย: 1 นาที 21.4 วินาที (2.2x เร็วกว่า) (แก้ไขโค้ดของ Arman)

รหัสของ Op ใน PyPy: 3 นาที 1.4 วิ

ทั้งสองทำใน REPL ไม่รวมเวลาในการโหลดแพ็กเกจ

function foo()                                                                                                                                                             
    n = 8                                                                                                                                                                  
    m = 8                                                                                                                                                                  
    iters = 1000                                                                                                                                                           
    bothzero = 0                                                                                                                                                           
    leadingzerocounts = zeros(Int,m)                                                                                                                                       
    P=[-1,0,0,1]                                                                                                                                                           

    for S in Iterators.product(fill([-1,1], n+m-1)...)                                                                                                                     
        Sm=[S...]                                                                                                                                                          
        for i = 1:iters                                                                                                                                                    
            F = P[rand(1:4,n)]                                                                                                                                             
            while all(F==0)                                                                                                                                                
                F = P[rand(1:4,n)]                                                                                                                                         
            end                                                                                                                                                            
            j = 1                                                                                                                                                          

            while j <= m && dot(F,Sm[j:j+n-1]) == 0                                                                                                                        
                leadingzerocounts[j] += 1                                                                                                                                  
                j += 1                                                                                                                                                     
            end                                                                                                                                                            
        end                                                                                                                                                                
    end                                                                                                                                                                    

    println(leadingzerocounts)                                                                                                                                             
end 

มีปัญหาบางอย่างเกี่ยวกับรหัสของ Arman ที่ทำให้มันช้ามาก: มันใช้ฟังก์ชั่นที่ไม่ระบุชื่อจำนวนมากและฟังก์ชั่นการสั่งซื้อที่สูงขึ้นโดยไม่จำเป็น เพื่อทดสอบว่าเวกเตอร์ F ทั้งหมดเป็นศูนย์ทำไมไม่ลองเขียนทั้งหมด (F == 0) แทนทั้งหมด (x-> x == 0, F) มันสั้นกว่าและเร็วกว่าพันเท่าอย่างแท้จริง

นอกจากนี้ยังใช้ผลรวม (แผนที่ (*, x, y)) เป็นผลิตภัณฑ์ดอทแทนเพียงแค่จุด (x, y) รุ่นแรกช้ากว่า 650 เท่าสำหรับเวกเตอร์ขนาด 10k และฟังก์ชั่นผลิตภัณฑ์ดอทถูกนำมาใช้เป็นลูปในจูเลียบริสุทธิ์

นอกจากนี้ความเข้าใจของอาร์เรย์ยังช้า จะดีกว่าถ้าเขียน [0,1,0, -1] [rand (1: 4, n)] แทน [[-1 0 0 1] [rand (1: 4)] สำหรับ j = 1: n] .

ในที่สุดตัวแปรระดับโลกคือจูจูที่ไม่ดีในจูเลีย Julia นั้นรวดเร็วถ้าคุณเขียนโค้ดในลักษณะที่ทำให้ JIT และการอนุมานประเภททำงานได้ ส่วนใหญ่ของเรื่องนี้คือความเสถียรของประเภท: คอมไพเลอร์จะต้องสามารถมั่นใจได้ว่าประเภทของตัวแปรจะไม่เปลี่ยนแปลงในขณะที่อยู่ในลูป


ขอบคุณ! ฉันเห็นว่าฉันยังคงเรียนรู้เกี่ยวกับ Julia Language ค่อนข้างน้อยก่อนที่ฉันจะสามารถเรียกร้องเกี่ยวกับความเร็วของมันได้ :) ดีใจจริง ๆ ที่เห็นว่าการแก้ไขเล็กน้อยในโค้ดของฉันเพิ่มเวลาในการประมวลผลหลายเท่า
Agar ว่องไว

2

นิม

import times, locks, strutils, unsigned

const
  N = 8
  M = 8
  iters = 1000
  numThreads = 8

type
  SVec = array[0..N+M-1, int]
  FVec = array[0..N-1, int]
  ComputeThread = TThread[int]

var
  rngSeed = int(epochTime()*1000)
  totalLeadingZeros: array[0..M-1, int]
  lock: TLock

type
  RNGState = object
    x, y, z, w: uint32

proc newRNG(seed: int): RNGState =
  result.x = uint32(seed)

proc random(rng: var RNGState): int =
  let t = rng.x xor (rng.x shl 11)
  rng.x = rng.y; rng.y = rng.z; rng.z = rng.w
  rng.w = rng.w xor (rng.w shr 19) xor t xor (t shr 8)
  result = int(rng.w)

proc initVecRand(v: var FVec, rng: var RNGState) =
  const values = [ -1, 0, 0, 1 ]
  var rnd = rng.random
  var bitAcc = 0
  for i in 0 .. <len(v):
    let val = values[rnd and 3]
    rnd = rnd shr 2
    v[i] = val
    bitAcc = bitAcc or val
  if bitAcc == 0:
    initVecRand(v, rng)

proc convolve(s: SVec, f: FVec, offset: int): int =
  for i in 0 .. <len(f):
    result += s[i+offset]*f[i]

proc iterate(v: var SVec) =
  for i in 0 .. <len(v):
    if v[i] == -1:
      v[i] = 1
      return
    v[i] = -1

proc mainThread(id: int) {.thread.} =
  const numS = 1 shl (N+M-1)
  var
    s: SVec
    f: FVec
    leadingZeros: array[0..M-1, int]
    rng = newRNG(rngSeed + id)
  for k in 0 .. <len(s):
    s[k] = -1
  for i in 1..numS:
    for j in countUp(id, iters, numThreads):
      initVecRand(f, rng)
      if convolve(s, f, 0) == 0:
        leadingZeros[0] += 1
        for k in 1 .. <M:
          if convolve(s, f, k) == 0:
            leadingZeros[k] += 1
          else:
            break
    iterate(s)
  acquire(lock)
  for i in 0 .. <M:
    totalLeadingZeros[i] += leadingZeros[i]
  release(lock)

proc main =
  let startTime = epochTime()
  var threads: array[1..numThreads, ComputeThread]
  initLock(lock)
  for i in 1..numThreads:
    createThread(threads[i], mainThread, i)
  for i in 1..numThreads:
    joinThread(threads[i])
  echo("Leading zeros: ", @totalLeadingZeros)
  let endTime = epochTime()
  echo("Time taken:    ", formatFloat(endTime - startTime, ffDecimal, 3),
       " seconds")

main()

ตัวอย่างผลลัพธ์:

Leading zeros: @[6333025, 2525808, 1042466, 439138, 192391, 86751, 40671, 19525]
Time taken:    0.145 seconds

นิมรอดคอมไพล์กับซีดังนั้นตัวเลือกของคอมไพเลอร์ C สำหรับแบ็กเอนด์ก็สำคัญ

ใช้เสียงดังกรไพล์รวบรวมกับ:

nimrod cc --threads:on --cc=clang --passc:-flto -d:release conv.nim

ใช้ gcc, คอมไพล์ด้วย:

nimrod cc --threads:on --cc=gcc --passc:-flto -d:release conv.nim

งด--passc:-fltoถ้าคุณมีคอมไพเลอร์ C รุ่นเก่าที่ไม่สนับสนุน LTO งด--cc=...ตัวเลือกหากคุณไม่พอใจกับตัวเลือกเริ่มต้นสำหรับคอมไพเลอร์ C รหัสต้องNimrod 0.9.4 หรือ 0.9.5

สำหรับ iMac quadcore ของฉัน (2.66 GHz core i5) โค้ดจะทำงานในเวลาประมาณ 0.15 วินาทีด้วย gcc 4.9, .16 วินาทีกับเสียงดังกราวเมื่อเทียบกับ 88 วินาทีสำหรับ PyPy 2.2.1 (เช่นเร่งความเร็ว 500+) น่าเสียดายที่ฉันไม่สามารถเข้าถึงเครื่องที่มีมากกว่าสี่แกนที่ติดตั้ง PyPy หรือที่ฉันสามารถติดตั้ง PyPy ได้อย่างง่ายดายแม้ว่าฉันจะได้รับประมาณ 0.1 วินาที (ด้วยเสียงรบกวนจากการวัดจำนวนมาก) บน AMD 64-core Opteron 6376 1.4 GHz (ตาม / proc / cpuinfo) ด้วย gcc 4.4.6

การใช้งานพยายามที่จะซื่อสัตย์กับต้นฉบับมากกว่าการปรับโค้ดให้เหมาะสมในราคาที่สามารถอ่านได้ในขณะที่ไม่ได้ทำการปรับให้เหมาะสมอย่างชัดเจน น่าสนใจมากการเรียกซ้ำหางinitVecRand()นั้นเร็วกว่าลูปเล็กน้อยโดยมีคำสั่ง break พร้อมทั้ง gcc และ clang การคลายการวนซ้ำหนึ่งรอบของการconvolveทดสอบด้วยตนเองภายในลูปหลักด้วยตนเองทำให้เกิดการเร่งความเร็วซึ่งน่าจะเกิดจากการทำนายของสาขาที่ดีกว่า


คุณจะรับ nimrod สำหรับ Ubuntu ได้อย่างไร

@Lembik การค้นหาอย่างรวดเร็วของ Google จะให้คุณnimrod-lang.org/download.html
ace_HongKongIndependence

@ace ฉันได้รวมลิงค์ไว้ในโพสต์ของฉันด้วย (ถึงแม้ว่ามันจะยากที่จะเห็นด้วยสีน้ำเงินบนพื้นสีดำในขณะที่ฉันมองมัน)
Reimer Behrends

คุณสามารถเร่งความเร็วได้มากขึ้นโดยเพิ่มขนาดเมล็ดเป็น 128 บิต: xorshift.di.unimi.it
user60561

2

ชวา

ฉันแปล C ++ ข้างบนเป็น Java:

import java.util.Random;
import java.util.Arrays;

public class Bench2 {
  public static int[] bits = { 0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF };
  public static int[] oneValues = { 1, 0, 0, 1 };
  public static int[] values = { -1, 0, 0, 1 };
  public static int n = 8;
  public static int m = 8;
  public static int iters = 1000;

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

  public static void main( String[] args ) {
    x = (int) (System.currentTimeMillis()/1000l);

    int[] leadingzerocounts = new int[ m ];
    Arrays.fill( leadingzerocounts, 0 );

    int maxS = 1 << 15;

    for( int s = 0; s < maxS; s++ ) {
      int x = interleaveBit( s );

      for( int i=0; i<iters; i++ ) {
        int random;

        do {
          random = 0xFFFF & fastRandom( );
        } while( sumOnes( random ) == 0 );

        int j = 7;

        while( j >= 0 ) {
          int h = ( x >> (j*2) );
          int l = 0xFFFF & (~(random ^ h));

          if( sumArray( l ) == 0 ) {
            leadingzerocounts[ j ]++;
          } else {
            break;
          }

          j--;
        }
      }
    }

    for( int i = 7; i >= 0; --i ) {
      System.out.print( leadingzerocounts[ i ] + " " );
    }

    System.out.println( );
  }

  public static int interleaveBit( int x ) {
    x = (x | ( x << 8)) & bits[3];
    x = (x | ( x << 4)) & bits[2];
    x = (x | ( x << 2)) & bits[1];
    x = (x | ( x << 1)) & bits[0];
    return x | (x << 1);
  }

  public static int sumOnes( int v ) {
    return (0xAAAA & (v ^ ~(v << 1)));
    // int s = 0;

    // for( int i = 0; i < 8; ++i ) {
    //   int a = 3 & ( v >> (i*2) );
    //   s += oneValues[ a ];
    // }

    // return s;
  }

  public static int sumArray( int v ) {
    return Integer.bitCount( v ) - 8;
    // int s = 0;

    // for( int i=0; i<8; ++i ) {
    //   int a = 3 & ( v >> (i*2) );
    //   s += values[ a ];
    // }

    // return s;
  }

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

บนเครื่องของฉันฉันได้รับผลลัพธ์ต่อไปนี้สำหรับโปรแกรม java:

time java Bench2
6330616 2524569 1040372 439615 193290 87131 40651 19607 
java Bench2  0.36s user 0.02s system 102% cpu 0.371 total

โปรแกรม OPs ทำงานประมาณ 53 วินาทีบนเครื่องของฉัน:

time pypy start.py
[6330944, 2524897, 1040621, 439317, 192731, 86850, 40830, 19555]
pypy start.py  52.96s user 0.06s system 99% cpu 53.271 total

โปรแกรม c ++ ดำเนินการเพียงประมาณ 0.15 วินาที:

time ./benchcc
[6112256, 2461184, 1025152, 435584, 193376, 87400, 40924, 19700]
./benchcc  0.15s user 0.00s system 99% cpu 0.151 total

นั่นคือประมาณ 2.5x เร็วกว่าโซลูชัน java ที่เกี่ยวข้อง (ฉันไม่ได้ยกเว้นการเริ่มต้น VM) วิธีแก้ปัญหาจาวานี้เร็วกว่าโปรแกรมที่รันด้วย PyPy ประมาณ 142 เท่า

เนื่องจากฉันสนใจเป็นการส่วนตัวฉันตั้งค่าitersเป็น 100_000 สำหรับ Java และ C ++ แต่ปัจจัย 2.5 ไม่ลดลงในความโปรดปรานของ Java หากมีอะไรที่ใหญ่กว่า

แก้ไข: ฉันรันโปรแกรมบน 64 บิต Arch Linux PC

แก้ไข 2: ฉันต้องการเพิ่มที่ฉันเริ่มต้นด้วยการแปลรหัสหลาม:

import java.util.Random;
import java.util.Arrays;

public class Bench {
    public static int[] values = { -1, 0, 0, 1 };
    public static int n = 8;
    public static int m = 8;
    public static int iters = 1000;

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

    public static void main( String[] args ) {
        x = (int) (System.currentTimeMillis()/1000l);

        int[] leadingzerocounts = new int[ m ];
        Arrays.fill( leadingzerocounts, 0 );

        int[] S = new int[ n+m-1 ];
        Arrays.fill( S, -1 );

        do {
            for( int i=0; i<iters; i++ ) {
                int[] F = new int[ n ];

                do {
                    randomArray( F );
                } while( containsOnlyZeros( F ) );

                for( int j=0; j < m && check( F, S, j ); j++ ) {
                    leadingzerocounts[ j ] += 1;
                }
            }
        } while( next( S ) );

        System.out.println( Arrays.toString( leadingzerocounts ) );
    }

    public static void randomArray( int[] F ) {
        for( int i = 0; i<F.length; i++ ) {
            F[ i ] = (1-(fastRandom()&3))%2;
        }
    }

    public static boolean containsOnlyZeros( int[] F ) {
        for( int x : F ) {
            if( x != 0 ) {
                return false;
            }
        }

        return true;
    }

    public static boolean next( int[] S ) {
        for( int i=0; i<S.length; i++ ) {
            if( ( S[ i ] = -S[ i ] ) == 1 ) {
                return true;    
            }
        }

        return false;
    }

    public static boolean check( int[] F, int[] S, int j ) {
      int sum = 0;

      for( int i=0; i<n; i++ ) {
          sum += F[ i ] * S[ j + i ];
      }

      return sum == 0;
    }

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

โปรแกรมนี้ใช้เวลาประมาณ 3.6 วินาที:

time java Bench   
[6330034, 2524369, 1040723, 439261, 193673, 87338, 40840, 19567]
java Bench  3.64s user 0.01s system 101% cpu 3.600 total

ซึ่งเร็วกว่าโซลูชัน PyPy ประมาณ 14 เท่า (การเลือกฟังก์ชั่นมาตรฐานแบบสุ่มในฟังก์ชั่น fastRandom จะนำไปสู่เวลาดำเนินการ 5 วินาที)


2

Python 3.5 + จำนวนมาก 1.10.1, 3.76 วินาที

การทดสอบทำงานบน Macbook Pro ของฉัน รหัสของ OP ใช้เวลาประมาณ 6 นาทีบนเครื่องเดียวกัน

ความจริงแล้วเหตุผลที่ฉันตอบคำถามนี้เพราะฉันไม่มีชื่อเสียง 10 ข้อและไม่สามารถตอบได้ในส่วนที่ฉัน :-p

ในช่วงไม่กี่วันที่ผ่านมาฉันพยายามคิดวิธีการโน้มน้าวใจอย่างมีประสิทธิภาพด้วยการใช้จำนวนมาก (โดยไม่ต้องพึ่งพาแพ็คเกจของบุคคลที่สาม เมื่อฉันเจอกับความท้าทายต่างๆในระหว่างการวิจัยของฉันฉันตัดสินใจลอง ฉันอาจจะมาเล่นเกมนี้ช้า แต่นี่คือความพยายามของฉันในการใช้ Python 3.5 และ numpy 1.10.1

def test_convolv():
    n = 8 
    m  = 8 
    iters = 1000
    ilow = np.ceil(0+n/2).astype(int)
    ihigh = np.ceil(m+n/2).astype(int)

    leadingzerocounts = np.zeros(m)

    # Pre-compute S & F
    S = np.array(list(itertools.product([-1,1], repeat = n+m-1)))
    choicesF = np.random.choice(np.array([-1, 0, 0, 1], dtype=np.int8), size=n*iters).reshape(iters,n)
    imask = ~np.any(choicesF, axis=1)
    while np.any(imask):
        imasksize = np.count_nonzero(imask)
        choicesF[imask,:] = np.random.choice(np.array([-1, 0, 0, 1], dtype=np.int8), size=n*imasksize).reshape(imasksize, n)
        imask = ~np.any(choicesF, axis=1)

    for i in np.arange(iters):
        F = choicesF[i, :]
        # This is where the magic is: by flattening the S array, 
        # I try to take advantage of speed of the np.convolve 
        # (really numpy.multiarray.correlate). 
        FS = (np.convolve(S.reshape(-1), F, 'same').reshape(S.shape))[:, ilow:ihigh]
        jmask_not = (FS[:, 0] != 0)
        leadingzerocounts[0] = leadingzerocounts[0]+np.count_nonzero(~jmask_not)
        for j in np.arange(n-1)+1:
            jmask = (FS[jmask_not, j] != 0)
            leadingzerocounts[j] = leadingzerocounts[j] + np.count_nonzero(~jmask)
            jmask_not[(jmask_not.nonzero()[0])[jmask]] = False

    print(leadingzerocounts)

ฉันคำนวณล่วงหน้าอาร์เรย์ S และ F และทำให้อาร์เรย์ S แบนในขณะที่ทำข้อตกลงซึ่ง (จากการทดลองของฉัน) สามารถใช้ประโยชน์จากความเร็วของ np.convolve ได้ กล่าวอีกนัยหนึ่งเมื่อฉันไม่พบรูทีนการแปลงเวกเตอร์ฉันปลอมโค้ดเวกเตอร์ด้วยการทำให้อาเรย์ทั้งหมดแบนและหวังว่า np.convolved จะทำให้การแปลงเป็นเวกเตอร์ภายใต้ฮู้ดสำหรับฉันซึ่งดูเหมือนจะใช้งานได้ หมายเหตุฉันใช้โหมด = 'เหมือนกัน' และตัดแต่งองค์ประกอบนำหน้าและส่วนท้ายที่ไร้ประโยชน์

บน MacBook Pro ของฉันผลการทดสอบให้3.76 วินาที เมื่อฉันวิ่งรหัสของ OP (แก้ไขหลาม 3.5) ผมได้ประมาณ6 นาที ความเร็วประมาณ 100 ครั้ง

ข้อเสียเปรียบประการหนึ่งคือเนื่องจากมีการจัดเก็บอาร์เรย์ S และ F ความต้องการหน่วยความจำอาจเป็นปัญหาได้หากขนาดใหญ่เกินไป

ฉันใช้วิธีการเดียวกันสำหรับส่วนที่ฉันและฉันได้รับ ~ 60-100x speedup บนแล็ปท็อปของฉัน

ในขณะที่ฉันทำทุกอย่างใน Macbook Pro ของฉันถ้ามีคนทดสอบโค้ดของฉันและบอกให้ฉันรู้ว่ามันทำงานอย่างไรในเครื่องของคุณฉันจะขอบคุณมันมาก!


1

J, 130x ~ 50x speedup?

n =: m =: 8
len =: 1000
S =: (] - 0 = ])S0=: #:i.2^<:+/n,m
k =: (n#0) -.~ (_1 0 0 1) {~ (n#4) #: i.4^n
sn =: (]-0=])#:i.2^n
ku =: ~. k
M =: 0=+/"1 sn *"1/ ku
fs =: (ku&i.)"1 k
snum =: n #.\"1 S0

run =: 3 : 0
 r =: n#0
 for_t. (snum) do.
   rn =: fs{~? len # #k
   r =: r + +/"1*/\rn{"1 t{M
 end.
 r
)
echo run 0
exit''

จำนวนครั้งของการเดเบียนแบบสุ่ม:

u#>time j slowpy.ijs
6334123 2526955 1041600 440039 193567 87321 40754 19714

real    0m2.453s
user    0m2.368s
sys     0m0.084s


u#>time python slow_pyth.py
[6331017, 2524166, 1041731, 438731, 193599, 87578, 40919, 19705]

real    5m25.541s
user    5m25.548s
sys     0m0.012s

ฉันคิดว่ามีห้องพักสำหรับการปรับปรุง


สคริปต์ Python ควรจะถูกเรียกใช้pypyงานไม่ใช่pythonซึ่งเป็นสาเหตุที่สคริปต์ของคุณดูเหมือนจะให้ความเร็ว 130x
ace_HongKong การพึ่งพากัน

@ ใช่ฉันสังเกตเห็น แต่ฉันไม่สามารถติดตั้ง pypy: - / ฉันคิดว่าลำดับความสำคัญจะยังคงอยู่
Eelvex

ไม่จำเป็นต้อง ... i.imgur.com/n566hzw.png
ace_HongKongIndependence

แน่นอนไม่จำเป็นต้อง
Eelvex

คุณติดตั้ง pypy มีปัญหาอะไร

1

C ++: x200 (4-core i7 ควรปรับเป็น x400 ใน 8-core)

พยายามหาวิธีแก้ปัญหาC ++ 11 ที่ตรงไปตรงมามากขึ้น(ทดสอบกับ VS 2012, gcc และ clang) ด้วยการขนาน

ในการทำให้คอมไพล์และรันภายใต้ Linux ด้วย gcc 4.8.1:

g ++ -O3 -msse -msse2 -msse3 -march = native -std = c ++ 11 -pthread -Wl, - ไม่จำเป็นต้อง golf.cpp

ภายใต้ Linux เราต้องstd::launch::asyncบังคับหลายเธรด ฉันหายไปในรุ่นก่อนหน้า

ใน Visual Studio (2012+) สิ่งนี้ควรใช้งานได้ แต่สร้างการเผยแพร่เพื่อกำหนดเวลา ...

ใน i3 ดูอัลคอร์เก่าของฉันทำงานได้ใน~ 0.9วินาที บน i7 quad core ของฉันนี่คือ 0.319s เทียบกับ pypy 66 วินาที

สำหรับ 8-core i7 สิ่งนี้ควรอยู่ในช่วงเร่งความเร็ว x400 การเปลี่ยนมาใช้อาร์เรย์สไตล์ C จะทำให้เร็วขึ้น แต่ฉันสนใจที่จะอยู่กับคอนเทนเนอร์ C ++ สำหรับฉันมันน่าสนใจที่จะเห็นการเร่งความเร็วที่คุณสามารถทำได้ในขณะที่อยู่ใกล้กับโดเมนปัญหาและในระดับที่ค่อนข้างสูงสิ่งที่ฉันคิดว่า C ++ นั้นดีจริงๆ สิ่งที่ควรทราบอีกประการหนึ่งคือความง่ายในการแยกวิเคราะห์โดยใช้โครงสร้าง C ++ 11

โซลูชัน bit ของ @ ilmale เจ๋งมากและใช้ได้กับวันที่ -1/1/01 หนึ่งสามารถโยน SSE ที่นี้และอาจได้รับการเร่งความเร็วที่สำคัญ

นอกเหนือจากการทำให้ขนานมี "เคล็ดลับ" อยู่ในนั้นซึ่งเป็นการลดจำนวนของการสรุป ตัวอย่างผลลัพธ์: 6332947 2525357 1041957 438353 193024 87331 40902 19649

#include <vector>
#include <iostream>
#include <thread>
#include <future>
#include <time.h>
#include <ctime>
#include <algorithm>

using namespace std;

// Bring some of these constants out to share
const size_t m = 8;
const int nthreads = 16;
const size_t cn = 15;
const int two_to_cn = 32768;

static unsigned int seed = 35;

int my_random() // not thread safe but let's call that more random!
{
   seed = seed*1664525UL + 1013904223UL; // numberical recipes, 32 bit
   return ((seed>>30)&1)-!!((seed>>30)&2); // Credit to Dave!
}

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

// Return the position of the first non-zero element
size_t convolve_until_nonzero(size_t max_n, const vector<int>& v1, const vector<int>& v2)
{
   for(size_t i = 0; i<max_n; ++i)
   {
      int result = 0;
      for(size_t j = 0; j<v2.size(); ++j)
      {
         result += v1[i+j]*v2[j];
      }
      if(result!=0)
      {
         return i;
      }
   }
   return max_n;
}

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

vector<int> convolve_random_arrays(vector<int> S, int range)
{
   const int iters = 1000;
   int bothzero = 0;
   int firstzero = 0;

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


   vector<int> F(m);
   vector<int> leadingzerocounts(m+1);

   for(auto &x: leadingzerocounts)
   {
      x = 0;
   }

   for(int i=0; i<range; ++i)
   {
      for(int j=0; j<iters; ++j)
      {
         do
         {
            for(auto &x : F)
            {
               x = my_random();
            }
         } while(allzero(F));
         leadingzerocounts[convolve_until_nonzero(m, S, F)]++;
      }
      advance(S);
   }

   // Finish adding things up...
   for(int i=m-1; i>0; --i)
   {
      leadingzerocounts[i] += leadingzerocounts[i+1];
   }

   vector<int> withoutfirst(leadingzerocounts.begin()+1, leadingzerocounts.end());
   return withoutfirst;
}

int main(int argc, char* argv[])
{

   vector<int> leadingzerocounts(m);

   for(auto &x: leadingzerocounts)
   {
      x = 0;
   }

   clock_t start = clock();

   vector<int> S(cn);
   for(auto &x : S)
   {
      x = -1;
   }

   vector< future< vector< int > > > fs; // The future results of the threads

   // Go make threads to work on parts of the problem
   for(int i=0; i<nthreads; ++i)
   {
      vector<int> S_reversed = S; // S counts using LSBs but we want the thread start to be in MSBs
      reverse(S_reversed.begin(), S_reversed.end());
      fs.push_back(async(std::launch::async, convolve_random_arrays, S_reversed, two_to_cn/nthreads));
      advance(S);
   }
   // And now collect the data
   for(auto &f : fs)
   {
      vector<int> result = f.get();
      for(int i=0; i<result.size(); ++i)
      {
         leadingzerocounts[i] += result[i];
      }
   }

   for(auto count : leadingzerocounts)
   {
      cout << count << endl;
   }

   return 0;
}

1

Fortran: 316x

โอเค Fortran: ฉันได้ความเร็วสูงสุด106x 155x 160x 316xเมื่อใช้ Xorshift RNG และ OpenMP ในซีพียู 4 คอร์ i7 นอกจากนั้นไม่มีกลอุบายใหญ่ ๆ เพื่อให้ iterator สร้าง S ฉันใช้การแทนเลขฐานสองของจำนวนเต็ม 16 บิต i คุณจะทราบว่านอกเหนือจาก inline RNG และ "iterator" / การแมปจาก i ถึง S แล้วรหัสนั้นก็อยู่ในระดับสูงเท่ากับรหัส Python

แก้ไข: ลบ "if" ใน Xorshift ตอนนี้ใช้ "r = abs (w / ... )" แทน "r = w / ... " เปลี่ยนจาก 106x เป็น 155x

แก้ไข 2: สิ่งนี้สร้างตัวเลขสุ่มได้มากถึง 15 เท่าของโซลูชัน C ++ หากใครบางคนมีวิธีแก้ปัญหาค่าใช้จ่ายเป็นศูนย์สำหรับการแปลง int สุ่มเป็นอาร์เรย์ 0s และ 1s ใน Fortran ฉันหูทั้งหมด จากนั้นเราสามารถเอาชนะ C ++ ได้ :)

แก้ไข 3: การแก้ไขครั้งแรกแนะนำข้อบกพร่องตามที่ Lembik ชี้ให้เห็น สิ่งนี้ได้รับการแก้ไขแล้วในขณะนี้ด้วยการปรับปรุงความเร็วเล็กน้อย ฉันจะพยายามใช้คำแนะนำโดย Eelvex เพื่อให้ได้ความเร็วมากขึ้น

Edit4: การทำโปรไฟล์ระบุว่าการแปลงเป็นจริงและกลับไปเป็นจำนวนเต็มด้วย nint () ช้า ฉันแทนที่สิ่งนี้ด้วยการหารจำนวนเต็มหนึ่งตัวที่ทำทั้งการปรับและการปัดเศษจาก 160x เป็น 316x speedup

รวบรวมกับ:

gfortran -O3 -march = native -fopenmp golf.f90

program golf
implicit none
integer, parameter :: m=8, n=8
integer :: F(n), S(m+n-1), leadingzerocounts(m)
integer :: j,k,bindec,enc,tmp,x=123456789,y=362436069,z=521288629,w=88675123
integer*2 :: i
real :: r

leadingzerocounts=0

!$OMP parallel do private(i,enc,j,bindec,S,F,k,tmp,x,y,z,w,r) reduction(+:leadingzerocounts) schedule(dynamic)
do i=0,32766
  enc=i
  ! Short loop to convert i into the array S with -1s and 1s
  do j=16,2,-1
    bindec=2**(j-1)
    if (enc-bindec .ge. 0) then
      S(j-1)=1
      enc=enc-bindec
    else
      S(j-1)=-1
    endif
  end do
  do j=1,1000
    F=0
    do while (.not. any(F /= 0))
      do k=1,n
        ! Start Xorshift RNG
        tmp = ieor(x,ishft(x,11))
        x = y
        y = z
        z = w
        w = ieor(ieor(w,ishft(w,-19)),ieor(tmp,ishft(tmp,-8)))
        ! End Xorshift RNG
        ! Just scale it inside the nint:
        !F(k)=nint(w/2147483648.0)
        ! Scaling by integer division is faster, but then we need the random 
        ! number to be in (-2,2) instead of [-1,1]:
        F(k)=w/1073741824

      end do
    end do
    do k=1,m
      if (dot_product(F,S(k:k+n-1)) /= 0) exit
      leadingzerocounts(k)=leadingzerocounts(k)+1
    end do
  end do
end do
!$OMP end parallel do

print *, leadingzerocounts

end

ตัวอย่างผลลัพธ์:

$ time ./a.out
6329624 2524831 1039787 438809 193044 6860 40486
19517 ./a.out ผู้ใช้ 1.45 วินาที 0.00 ระบบ 746% cpu 0.192 ทั้งหมด

รหัสของ OP:

$ time pypy golf.py
pypy golf.py ผู้ใช้ 60.68 ระบบระบบ 0.04s 99% cpu 1: 00.74 ทั้งหมด


สิ่งที่ฉันใช้ใน J คือรายการ prebuild ของตัวเลข 4 ^ n ใน base-4 จากนั้นแปลงเป็น triadic และไม่รวม 0 RNG เพิ่งเลือกจากรายการนี้
Eelvex

ฉันไม่แน่ใจว่ารหัสของคุณถูกต้อง สำหรับการทำซ้ำ 100,000 ครั้งฉันได้รับ 633140285 271390368 118307997 52751245 23725837 10744292 4944464 2388125 แต่ฉันคิดว่ามันควรจะอยู่ใกล้กับ 633170604 252560981 104156146 43911426 19316309 8713324 4073378 40409334 4073378 19594324407337840

1
ขอบคุณ @Lembik การแก้ไขการเร่งความเร็วของฉัน (การลบคำสั่ง if) เป็นข้อผิดพลาดอย่างแน่นอน ฉันได้อัปเดตโค้ดแล้วดังนั้นควรแก้ไขให้ถูกต้องในตอนนี้ ฉันจะพยายามโพสต์เวอร์ชันโดยใช้ข้อเสนอแนะโดย Eelvex ในภายหลัง
กึ่ง

ที่เร่งมันขึ้นดูเหมือนว่า!

ใช่เร็วขึ้นเล็กน้อยฉันเดา ฉันรู้ว่าฉันกำลังเพิ่ม 1.0 แล้วลบ 1.0 ภายในวงที่แน่น
- extrinsic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.