กรองไฟล์ขนาดใหญ่อย่างรวดเร็ว


11

ความท้าทายคือการกรองไฟล์ขนาดใหญ่อย่างรวดเร็ว

  • อินพุต: แต่ละบรรทัดมีช่องว่างจำนวนเต็มบวกจำนวนสามช่องว่าง
  • เอาท์พุท: เส้นที่นำเข้าทั้งหมดA B, Tที่ตอบสนองอย่างใดอย่างหนึ่งของเกณฑ์ดังต่อไปนี้

    1. มีอยู่สายป้อนข้อมูลอื่นC, D, Uที่และD = A0 <= T - U < 100
    2. มีอยู่สายป้อนข้อมูลอื่นC, D, Uที่และB = C0 <= U - T < 100

ในการสร้างไฟล์ทดสอบให้ใช้สคริปต์ python ต่อไปนี้ซึ่งจะใช้สำหรับการทดสอบ มันจะสร้างไฟล์ 1.3G แน่นอนคุณสามารถลด nolines สำหรับการทดสอบ

import random    
nolines = 50000000 # 50 million
for i in xrange(nolines):
    print random.randint(0,nolines-1), random.randint(0,nolines-1), random.randint(0,nolines-1)

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

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

ข้อมูลเวลาที่เกี่ยวข้องบางอย่าง

อัพเดตเวลาเพื่อรันต่อไปนี้ก่อนการทดสอบแต่ละครั้ง

sync && sudo bash -c 'echo  3 > /proc/sys/vm/drop_caches'

time wc test.file

real    0m26.835s
user    0m18.363s
sys     0m0.495s

time sort -n largefile.file  > /dev/null

real    1m32.344s
user    2m9.530s
sys     0m6.543s

สถานะของรายการ

ฉันเรียกใช้บรรทัดต่อไปนี้ก่อนการทดสอบแต่ละครั้ง

sync && sudo bash -c 'echo  3 > /proc/sys/vm/drop_caches'
  • Perl (กำลังรอการแก้ไขข้อบกพร่อง)
  • Scala 1 นาที 37 วินาทีโดย @James_pic (การใช้ scala -J-Xmx6g ตัวกรอง largefile.file output.txt)
  • ชวา 1 นาที 23 วินาทีโดย @Geobits (การใช้ java -Xmx6g Filter_26643)
  • . 2 นาที 21 วินาทีโดย @ScottLeadley
  • . 28 วินาทีโดย @James_pic
  • หมีแพนด้าหลาม + อาจจะมีวิธีแก้ปัญหา "groupby" อย่างง่าย?
  • . 28 วินาทีโดย @KeithRandall

ผู้ชนะคือ Keith Randall และ James_pic

ฉันไม่สามารถบอกเวลาวิ่งของพวกเขาได้และพวกเขาก็เกือบจะเร็วเท่ากับ wc!


1
บางทีคุณควรลองเขียนคำท้าที่ไม่ใช่ [รหัสเร็วที่สุด]
Justin


2
โปรดกำหนดจำนวนเต็มบวก 1 < n < 2147483647?
durron597

1
@ScottLeadley ไม่เว้นแต่จะปรากฏขึ้นหลายครั้งในการป้อนข้อมูลของหลักสูตร (ซึ่งฉันคิดว่าไม่น่าจะเป็นไปได้มาก)

1
คุณมีการ์ดกราฟิกแบบใดและมีหน่วยความจำวิดีโอเท่าใด
IchBinKeinBaum

คำตอบ:


10

C, ~ 7 4.1 วินาที

Radix เรียงลำดับบน T จากนั้นเดินผ่านอาร์เรย์เพื่อค้นหารายการที่ตรงกัน

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

เพิ่ม: ฉันไม่ต้องตรวจสอบแต่ละแถวกับการสแกน 100 แถวอื่น ๆ อีกต่อไป ตารางจำนวนเล็กน้อยของบิตลำดับต่ำของ b's ในหน้าต่างพอเพียงเพื่อกำจัดอินสแตนซ์ส่วนใหญ่ของการสแกนนี้

ตอนนี้ประมาณ 1/2 การแยกเวลาการเรียงลำดับ 1/3 เวลา 1/6 เวลาทำการจับคู่ที่เกิดขึ้นจริง

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>

// B = # of bits per radix pass
// R = # of radix passes
#define B 9
#define R 3
#define M ((1<<B)-1)
#define MAXN 50000000

int count[R][1<<B];

typedef struct {
  int a,b,t,print;
} entry;

entry A[MAXN];
entry C[MAXN];

// Sized to fit well in L1 cache
unsigned char bcount[16384];

int main(int argc, char *argv[]) {
  FILE *f = fopen(argv[1], "r");
  fseek(f, 0, SEEK_END);
  int size = ftell(f);
  fclose(f);

  int fd = open(argv[1], O_RDONLY);
  const char *p = (const char*)mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
  const char *endp = p + size;

  // parse, insert into array
  int n = 0;
  while(p < endp) {

    // parse line
    int a = 0;
    while(*p != ' ') {
      a *= 10;
      a += *p - '0';
      p++;
    }
    p++;
    int b = 0;
    while(*p != ' ') {
      b *= 10;
      b += *p - '0';
      p++;
    }
    p++;
    int t = 0;
    while(*p != '\n') {
      t *= 10;
      t += *p - '0';
      p++;
    }
    p++;

    // insert it
    if(n == MAXN) {
      printf("too many elements\n");
      exit(1);
    }
    A[n].a = a;
    A[n].b = b;
    A[n].t = t;
    n++;

    // compute counts for radix sort
    count[0][t&M]++;
    count[1][(t>>B)&M]++;
    count[2][t>>2*B]++;
  }

  // accumulate count entries
  for(int r = 0; r < R; r++) {
    for(int i = 0; i < M; i++) {
      count[r][i+1]+=count[r][i];
    }
  }

  // radix sort, 3 rounds
  for(int i = n-1; i >= 0; i--) {
    C[--count[0][A[i].t&M]] = A[i];
  }
  for(int i = n-1; i >= 0; i--) {
    A[--count[1][(C[i].t>>B)&M]] = C[i];
  }
  for(int i = n-1; i >= 0; i--) {
    C[--count[2][A[i].t>>2*B]] = A[i];
  }

  // Walk through array (now sorted by T) and find matches.
  // We maintain a window of T values that might match.
  // To facilitate finding matches within that window, bcount
  // keeps track of a count of how many b's in that window
  // have the given low 14 bits.
  int j = 0;
  for(int i = 0; i < n; i++) {
    int a = C[i].a;
    int t = C[i].t;
    while(C[j].t <= t - 100) {
      int x = C[j].b & 16383;
      if(bcount[x] != 255) bcount[x]--;
      j++;
    }
    if(bcount[a & 16383] > 0) {
      // somewhere in the window is a b that matches the
      // low 14 bits of a.  Find out if there is a full match.
      for(int k = j; k < i; k++) {
        if(a == C[k].b)
          C[k].print = C[i].print = 1;
      }
    }
    int x = C[i].b & 16383;
    if(bcount[x] != 255) bcount[x]++;
  }
  for(int i = 0; i < n; i++) {
    if(C[i].print)
      printf("%d %d %d\n", C[i].a, C[i].b, C[i].t);
  }
}

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

ใช่! ฉันรักมัน. ฉันรู้สึกว่าตำแหน่งแคชจะทำให้การเข้าร่วม T เร็วขึ้น แต่ฉันคิดเสมอว่าขั้นตอนการเรียงลำดับจะชดเชยกำไรใด ๆ การใช้การจัดเรียงของ Radix ค่อนข้างกำจัด
James_pic

การเรียงลำดับ Radix ทำงานได้ดีในแคชเนื่องจากมีสตรีมการอ่านหนึ่งรายการและสตรีมการเขียน N เส้น (ในรหัสของฉัน N = 512) ตราบใดที่แคชของคุณมีเส้นแคช N + 1 ทุกอย่างจะยังคงอยู่ในแคช
Keith Randall

อืม แท้จริงฉันเพิ่งสร้างขึ้นfilter.cเพื่อทำสิ่งเดียวกันมาถึงคำถามและพบสิ่งนี้ +1
Geobits

1
@ Lembik: รหัสตามประเภท B * R = 27 บิตเท่านั้น ตอนนี้คุณมีตัวเลข 29 บิต - คุณต้องมีอีกหนึ่งบัตร (R ++) หรืออีกหนึ่งบิตต่อบัตร (B ++) B ++ น่าจะง่ายกว่า R เป็นฮาร์ดโค้ดในลูปที่ไม่สามารถควบคุมได้
Keith Randall

7

สกาล่า 2.10 - 0:41

ปัญหาคือโดยทั่วไป:

select * from data x, data x where x.a = y.b and 0 <= x.t - y.t and x.t - y.t < 100

RDBMS ส่วนใหญ่จะสังเกตเห็นว่าการเข้าร่วมจากx.aถึงy.bมีความเฉพาะเจาะจงสูงสุดและวางแผนสิ่งนี้เป็นการเข้าร่วมแฮช

นั่นคือสิ่งที่เราจะทำ เราสร้าง Hashtable ของข้อมูลบนa, กัญชาเข้าร่วมกับโต๊ะเดียวกันบนและกรองความแตกต่างในbt

import scala.io.Source
import scala.reflect.ClassTag
import java.io._

object Filterer {
  def roundUpToNextPowerOfTwo(x: Int) = {
    // blatantly stolen from http://bits.stephan-brumme.com/roundUpToNextPowerOfTwo.html
    var y = x - 1
    y |= y >> 1
    y |= y >> 2
    y |= y >> 4
    y |= y >> 8
    y |= y >> 16
    y + 1
  }

  // We hash join the array with itself, a to b, and emit both rows if t is within 100. 50m records should fit into 8GB OK.
  def main(args: Array[String]): Unit = {
    val input = Source.fromFile(args(0), "ASCII").getLines()
    val output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(args(1)), "US-ASCII"))
    try {
      val data1: Array[Row] = input.map{line =>
        Row(line)
      }.toArray

      /*
       * In theory, data1 and data2 could be created in parallel, but OpenHashMultiMap needs
       * to know its size at creation time, to sidestep the need for rehashing. I could just
       * hard-code 50 million (the size of the data in the challenge), but that seems dishonest.
       */
      val data2 = new OpenHashMultiMap[Int, Row](roundUpToNextPowerOfTwo(data1.size) * 2, -1)
      for (r <- data1) data2.insert(r.a, r) // data2 is hashed by a

      for (row1 <- data1.par) {
        val Row(a, b, t) = row1
        for (Row(c, d, u) <- data2.get(b) if (0 <= u - t) && (u - t < 100)) {
          // The conditions are symmetric, so if row1 matches, so does row2
          output.write(s"$a $b $t\n$c $d $u\n")
        }
      }
    } finally {
      output.close()
    }
  }
}

object Row {
  def apply(data: String): Row = {
    val l = data.length
    var i = 0
    var a = 0
    var b = 0
    var c = 0
    while (data.charAt(i) != ' ') {
      a = a * 10 + (data.charAt(i) - '0')
      i += 1
    }
    i += 1
    while (data.charAt(i) != ' ') {
      b = b * 10 + (data.charAt(i) - '0')
      i += 1
    }
    i += 1
    while (i < l) {
      c = c * 10 + (data.charAt(i) - '0')
      i += 1
    }
    Row(a, b, c)
  }
}

final case class Row(a: Int, b: Int, t: Int)

/*
 * None of the standard Java or Scala collections are particularly efficient as large MultiMaps,
 * so we write our own. We use open hashing with quadratic probing.
 */
class OpenHashMultiMap[@specialized(Int) K: ClassTag, V: ClassTag](capacity: Int, default: K) {
  require((capacity & (capacity - 1)) == 0) // Power of 2 capacity
  private val keys = Array.fill(capacity)(default)
  private val values = new Array[V](capacity)
  private val mask = capacity - 1

  private def hash(k: K) = {
    // Hash mingling - Int has a particularly poor hash
    k.hashCode * 428916315
  }

  def insert(k: K, v: V) = {
    var found = false
    var loc = hash(k) & mask
    var inc = 0
    while (inc <= capacity && !found) {
      loc = (loc + inc) & mask
      inc += 1
      found = keys(loc) == default
    }
    keys(loc) = k
    values(loc) = v
  }

  def get(key: K) = new Traversable[V] {
    override def foreach[U](f: V => U) = {
      var break = false
      var loc = hash(key) & mask
      var inc = 0
      while (inc <= capacity && !break) {
        loc = (loc + inc) & mask
        inc += 1
        val k = keys(loc)
        if (key == k) f(values(loc))
        else if (k == default) break = true
      }
    }
  }
}

รวบรวมกับ:

scalac Filterer.scala

และทำงานด้วย:

scala -J-server -J-XX:+AggressiveOpts -J-Xms6g -J-Xmx6g Filterer input_file.dat output_file.txt

บนเครื่องของฉันสิ่งนี้จะทำงานใน 2 นาที 27

ถึงกระนั้นก็อาจเป็นเรื่องที่น่าสนใจที่จะลองใช้วิธีการจากคำตอบของ @ Lembik แต่เป็นภาษาที่เร็วขึ้น tมันสอดคล้องกับสิ่งที่ต้องการผสานเข้าร่วมใน บนกระดาษควรช้าลง แต่มีตำแหน่งแคชที่ดีกว่าซึ่งอาจดันไปข้างหน้า

ปรับปรุง

ฉันพยายามที่จะโกนเวลาเป็นจำนวนมากด้วยการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ที่น่าแปลกใจ - mingler แฮชที่ดีกว่า แผนที่แฮชมีความอ่อนไหวต่อการแฮ็คก้อนมากดังนั้นการเปลี่ยนแปลงนี้จะนำไปสู่ ​​1:45 ในเครื่องของฉัน

เวลาส่วนใหญ่ที่ใช้ในการอ่านข้อมูลลงในอาร์เรย์

ฉันสงสัยว่าทำไมรหัสการอ่านข้อมูลของฉันจึงช้ากว่า @Geobits มาก รหัสของฉันใช้เวลา 70 วินาทีในการอ่านข้อมูล - นานกว่า @Geobits โปรแกรมทั้งหมดเมื่อแก้ไขThread.startข้อบกพร่องแล้ว ฉันถูกล่อลวงให้ขโมย @Geits วิธีการอ่านข้อมูล แต่ฉันไม่แน่ใจว่าเหล่าเทพเจ้า Exchange จะรู้สึกอย่างไร

อัปเดต 2

ฉันได้ทำการปรับปรุงเพิ่มเติมในครั้งนี้กับผู้อ่านข้อมูล การใช้การจับคู่รูปแบบและการดำเนินการ monad ภายในลูปทำให้ประสิทธิภาพการทำงานลดลงดังนั้นฉันจึงทำให้มันง่ายขึ้น ฉันคิดว่าscala.io.Sourceเป็นคอขวดต่อไปที่จะแก้ไขปัญหา

มันลงไปที่ 1:26 ในเครื่องของฉันตอนนี้

อัปเดต 3

กำจัดprobeออกจาก OpenHashMultiMap ตอนนี้โค้ดตอนนี้คือ java-ish และทำงานใน 1:15

อัปเดต 4

ตอนนี้ฉันใช้ FSM เพื่อแยกวิเคราะห์อินพุต เวลารันไทม์ลดลงเหลือ 0:41


ฉันได้ James_pic.scala: 42: ข้อผิดพลาด: ')' คาด แต่พบสตริงตามตัวอักษร output.write (s "$ a $ b $ t \ n $ c $ d $ u \ n") ^ พบข้อผิดพลาด นี่คือคอมไพเลอร์ Scala รุ่น 2.9.2

1
ฉันได้มันไปทำงานกับ 2.10.3 มันเป็นทางออกที่ดีมากแม้ว่าคอมพิวเตอร์ที่ไม่ดีของฉันจะใช้งานไม่ได้มากกว่าหรือน้อยกว่าหนึ่งนาทีหลังจากนั้นเพราะมันพยายามที่จะจัดสรรคืน RAM ขนาด 6GB

ใช่ขอโทษ. ฉันคิดว่าคุณอาจมีปัญหานั้น Ubuntu ยังมาพร้อมกับ Scala 2.9 และการแก้ไขสตริงต้องมี 2.10 หรือสูงกว่า ฉันสงสัยว่ามันจะเร็วกว่าภายใต้ Java 8 แต่ Ubuntu มาพร้อมกับ 7 เท่านั้นและนั่นคือโลกแห่งความเจ็บปวดที่คุณไม่ต้องการ!
James_pic

ป้อนข้อมูลอีกครั้ง: ฉันไม่ได้ใช้เสมอStringTokenizerแต่เมื่อฉันทำฉันจะวิเคราะห์สตริงเป็นล้านๆ ครั้ง
Geobits

@Geobits ใช่ตอนString.splitนี้เป็นคอขวด แต่ตอนStringTokenizerนี้ยังไม่ดีกว่านี้มาก - การจัดสรรในวงในที่แน่นหนากำลังทำให้ GC ของฉันเครียดอยู่แล้ว ฉันกำลังทำงานกับ FSM ซึ่งดูเหมือนว่าจะมีสัญญา (ในขณะที่กำลังเติมเต็ม)
James_pic

6

Java: 1m54s

(ใน i7 ของฉัน)

นับตั้งแต่การแข่งขันทุกจะได้รับภายใน 100 ของเพื่อนของฉันตัดสินใจที่จะถังปัจจัยการผลิตโดยt tมีที่ฝากข้อมูลสำหรับแต่ละ 100 ดังนั้นเพื่อตรวจสอบตัวเลขมันจะต้องตรวจสอบกับ +/- 1 ถัง

โดยเฉลี่ยแต่ละถังมี 100 รายการเท่านั้นดังนั้นจึงใช้เวลาไม่นานในการสแกนถังสองสามใบสำหรับแต่ละถัง ใช้เวลามากกว่าครึ่งในการอ่านและการจับคู่การจับคู่ใช้เวลาเพียง 40 วินาทีหรือมากกว่านั้น

หมายเหตุ:ขึ้นอยู่กับการตั้งค่า JVM ของคุณคุณอาจต้องเพิ่มขนาดฮีพ test.fileนอกจากนี้ยังถือว่าชื่อแฟ้มของ เพียงแค่เปลี่ยนเป็นบรรทัดที่ 24 หากไม่ใช่ในกรณีนี้

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class Filter_26643 {

    final static int numThreads = 8; 
    final static int numInputs = 50000000;
    final static int bucketSize = 100;
    final static int numBuckets = numInputs/bucketSize;
    ArrayList<ArrayList<int[]>> buckets;

    public static void main(String[] args) {
        new Filter_26643().run();
    }

    void run(){
        try{
            buckets = new ArrayList<ArrayList<int[]>>(numBuckets);
            for(int i=0;i<numBuckets;i++)
                buckets.add(new ArrayList<int[]>(bucketSize*2));

            BufferedReader reader = new BufferedReader(new FileReader("test.file"));
            int c=0,e[];
            while(c++<numInputs){
                StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
                e = new int[] {
                                Integer.parseInt(tokenizer.nextToken()),
                                Integer.parseInt(tokenizer.nextToken()),
                                Integer.parseInt(tokenizer.nextToken())
                                }; 
                buckets.get(e[2]/100).add(e);
            }
            reader.close();

            MatchThread[] threads = new MatchThread[numThreads];
            for(int i=0;i<numThreads;i++){
                threads[i] = new MatchThread(i);
                threads[i].start();
            }
            for(int i=0;i<numThreads;i++)
                threads[i].join();

        } catch(Exception e){
            e.printStackTrace();
        }
    }

    class MatchThread extends Thread{
        int index;

        public MatchThread(int index){
            this.index = index;
        }

        @Override
        public void run() {
            for(int i=index;i<numBuckets;i+=numThreads){
                int max = i+2 >= numBuckets ? numBuckets : i+2;
                int min = i-1 < 0 ? i : i-1;
                for(int[] entry : buckets.get(i)){
                    outer:
                    for(int j=min;j<max;j++){
                        ArrayList<int[]> bucket = buckets.get(j);
                        for(int[] other : bucket){
                            if(((entry[0]==other[1] && entry[2]-other[2]<100 && entry[2]>=other[2]) || 
                                (entry[1]==other[0] && other[2]-entry[2]<100 && other[2]>=entry[2]))
                                && entry != other){
                                 System.out.println(entry[0] + " " + entry[1] + " " + entry[2]);
                                 break outer;
                            }
                        }                           

                    }   
                }
            }
        }
    }
}

หลังจาก 5 นาทีครึ่งฉันได้รับข้อยกเว้นในเธรด "main" java.lang.OutOfMemoryError: เกินขีด จำกัด โอเวอร์เฮด GC ตามที่คุณแนะนำ ฉันต้องเพิ่มขนาดฮีพเป็นเท่าไร

คุณทำผิดเธรดที่ยอมรับแล้ว! On line ที่ 40, คุณเคยใช้Thread::runไม่ได้Thread.startดังนั้นมันคือทั้งหมดที่ทำงานในmainหัวข้อ ด้วยThread::startเวลาทำงานลดลงจาก 1:38 เป็น 0:46 ในเครื่องของฉัน
James_pic

@James_pic คุณเพิ่มขนาดฮีพหรือไม่ นอกจากนี้ 0:46 เปรียบเทียบกับเวลาสำหรับ sort -n test.file บนคอมพิวเตอร์ของคุณอย่างไร (หากคุณสามารถจัดให้มันไม่ได้อยู่ใน RAM แล้ว)

เครื่องที่ฉันอยู่ในขณะนี้คือกล่อง Windows ดังนั้นฉันไม่สามารถวัดsortเวลาได้ ฉันชนกองมากถึง 6G เช่นเดียวกับฉัน (คุณบอกว่าคุณมี 8G ดังนั้นจึงดูเหมือนเดาที่เหมาะสม)
James_pic

1
@Geobits บังเอิญฉันชอบอัลกอริทึมนี้ คุณจะได้รับประโยชน์มากที่สุดจากการรวมการเข้าร่วมโดยไม่มีค่าใช้จ่ายในการจัดเรียง - มันเป็นการรวมที่เหมือนการรวมเข้าด้วยกันของนกพิราบ
James_pic

6

C - 12 วินาที

ฉันตัดสินใจย้ายคำตอบสกาล่าของฉันไปที่ C เพื่อดูว่าฉันจะได้ประสิทธิภาพเพิ่มขึ้นเท่าใด

มันเป็นวิธีการเดียวกันมากขึ้นหรือน้อยลง (สร้างตารางแฮชแบบเปิดa) ยกเว้นว่าฉันข้ามขั้นตอนที่ฉันสร้างอาร์เรย์เริ่มต้นและทำซ้ำจากตารางแฮชโดยตรง (ด้วยเหตุผลบางอย่างที่ฉันไม่เคยได้รับวิธีนี้เพื่อแสดงใน Scala - ฉันสงสัยว่าการวางแนว JVM คือการตำหนิ)

ฉันไม่ได้กังวลกับหัวข้อเพราะมันเป็นความเจ็บปวดที่จะทำแบบพกพา

รหัสคือ:

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

// Should be 37% occupied with 50m entries
#define TABLE_SIZE 0x8000000
#define MASK (TABLE_SIZE - 1)
#define BUFFER_SIZE 16384
#define END_OF_FILE (-1)
#define DEFAULT_VALUE (-1)

typedef struct Row {
  int32_t a;
  int32_t b;
  int32_t t;
} Row;

int32_t hash(int32_t a) {
  return a * 428916315;
}

void insert(Row * table, Row row) {
  long loc = hash(row.a) & MASK; // Entries are hashed on a
  long inc = 0;
  while (inc <= TABLE_SIZE) {
    loc = (loc + inc) & MASK;
    inc++;
    if (table[loc].a == DEFAULT_VALUE) {
      table[loc] = row;
      break;
    }
  }
}

int readChar(FILE * input, char * buffer, int * pos, int * limit) {
  if (*limit < *pos) {
    return buffer[(*limit)++];
  } else {
    *limit = 0;
    *pos = fread(buffer, sizeof(char), BUFFER_SIZE, input);
    if (*limit < *pos) {
      return buffer[(*limit)++];
    } else return END_OF_FILE;
  }
}

void readAll(char * fileName, Row * table) {
  char* buffer = (char*) malloc(sizeof(char) * BUFFER_SIZE);
  int limit = 0;
  int pos = 0;

  FILE * input = fopen(fileName, "rb");

  int lastRead;
  Row currentRow;
  uint32_t * currentElement = &(currentRow.a);

  // As with the Scala version, we read rows with an FSM. We can
  // roll up some of the code using the `currentElement` pointer
  while (1) {
    switch(lastRead = readChar(input, buffer, &pos, &limit)) {
      case END_OF_FILE:
        fclose(input);
        return;
      case ' ':
        if (currentElement == &(currentRow.a)) currentElement = &(currentRow.b);
        else currentElement = &(currentRow.t);
        break;
      case '\n':
        insert(table, currentRow);
        currentRow.a = 0;
        currentRow.b = 0;
        currentRow.t = 0;
        currentElement = &(currentRow.a);
        break;
      default:
        *currentElement = *currentElement * 10 + (lastRead - '0');
        break;
    }
  }
  //printf("Read %d", lastRead);
}

int main() {
  Row* table = (Row*) malloc(sizeof(Row) * TABLE_SIZE);
  memset(table, 255, sizeof(Row) * TABLE_SIZE);

  readAll("test.file", table);

  // We'll iterate through our hash table inline - passing a callback
  // is trickier in C than in Scala, so we just don't bother
  for (size_t i = 0; i < TABLE_SIZE; i++) {
    Row * this = table + i;
    if (this->a != DEFAULT_VALUE) {
      // Lookup entries `that`, where `that.a == this.b`
      long loc = hash(this->b) & MASK;
      long inc = 0;
      while (inc <= TABLE_SIZE) {
        loc = (loc + inc) & MASK;
        inc++;
        Row * that = table + loc;
        if ((this->b == that->a) && (0 <= that->t - this->t) && (that->t - this->t < 100)) {
          // Conditions are symmetric, so we output both rows
          printf("%d %d %d\n", this->a, this->b, this->t);
          printf("%d %d %d\n", that->a, that->b, that->t);
        }
        else if (that->b == DEFAULT_VALUE) break;
      }
    }
  }

  free(table);
  return 0;
}

รวบรวมกับ:

gcc -std=c99 -O3 -m64 filter.c

และทำงานด้วย:

./a.out

ตำแหน่งของไฟล์ทดสอบนั้นกำหนดค่าตายตัวว่า "test.file"

การอ่านข้อมูลจะใช้เวลาส่วนใหญ่อีกครั้ง (ต่ำกว่า 9 วินาที) การจับคู่ใช้เวลาที่เหลือ

อีกครั้งมันจะน่าสนใจที่จะเห็นว่าค่าโดยสารนี้เทียบกับคำตอบของ Scott Leadley ซึ่งใช้ภาษาเดียวกัน แต่เป็นกลยุทธ์ที่แตกต่างกัน สกอตต์กำลังเข้าร่วมใน T ซึ่งโดยหลักการแล้วหมายความว่าเขาจะมีส่วนร่วมมากขึ้น แต่อีกครั้งการเข้าร่วมที่ T จะทำให้พื้นที่แคชดีขึ้น


ฉันจะได้รับ James_pic.c: ในฟังก์ชั่น 'readAll': James_pic.c: 67: 28: คำเตือน: การเปรียบเทียบประเภทตัวชี้ที่แตกต่างขาด cast [เปิดใช้งานโดยค่าเริ่มต้น] ถ้า (currentElement == & (currentRow.a)) currentElement = & (currentRow.b);

ฉันได้รับผลลัพธ์ที่แตกต่างกันเล็กน้อยจากโค้ดสกาล่าและซีของคุณ อันที่จริงมีเพียงหนึ่งบรรทัดเท่านั้นที่แตกต่างกัน ฉันเพิ่งทำdiff <(sort -n James_pic-c.out) <(sort -n James_pic-scala.out)

สิ่งนี้จะล้มเหลวสำหรับอินพุตที่aค่าที่กำหนดเกิดขึ้นในnช่วงเวลาใดn >= BUFFER_SIZE + 2
laindir

ฉันคิดว่าเป็นเพียงแค่คุณมี <= 100 ในโค้ดนี้และ <100 ในสเกลโค้ด

@ Lembik ฉันคิดว่าคุณพูดถูก เกิดข้อผิดพลาดแบบออฟไลน์หนึ่งข้อ!
James_pic

2

Perl 17m46s บนคอร์ i7 i7 / 8GB

อันดับแรกเราใช้ sort -n -k3 เพื่อให้ได้ฟิลด์ที่สำคัญที่สุดตามลำดับโดยใช้ประโยชน์จากการขนานในตัวกับการเรียงลำดับรุ่นใหม่ (1) จากนั้นเนื่องจาก Perl ถูกขัดขวางอย่างมากจากความจริงที่ว่าสเกลาร์แบบง่ายใช้คำสั่ง 80 ไบต์ต่อแต่ละ (50 ล้าน * 3 * 80 มากเกินไป - อย่างน้อย 12GB) เราจึงส่งออกไปยัง 50 ล้าน * 12 ไบต์ อาร์เรย์ (12 ไบต์ต่อบรรทัดแต่ละบรรทัดมีจำนวนเต็ม 3 ตัวที่สามารถแทนได้เป็นจำนวนเต็ม 32 บิต) จากนั้นเราจะทำการปิด 8 เธรดที่ครอบคลุม (ประมาณ) 1 / 8th ของข้อมูลแต่ละรายการ (+ ทับซ้อนกันบ้าง)

#!perl

use strict;
use warnings;

# find lines s.t. $lines[$M]->{a} == $lines[$N]->{b} and
#                 0 <= $lines[$M]->{t} - $lines[$N]->{t} < 100
# OR              $lines[$M]->{b} == $lines[$N]->{a} and
#                 0 <= $lines[$N]->{t} - $lines[$M]->{t} < 100

my $infile = shift;
open(my $fh, "sort -n -k3 $infile |") || die "open sort pipe: $@";

my @lines;
my $bytes_per_int = 4;
my $bytes_per_line = $bytes_per_int * 3;
my $nlines = 50_000_000;
my $buf = "\0" x ($nlines * $bytes_per_line);
my $ln = 0;
my $nprocs = 8;
my $last_group_start = 0;
my $this_group_start;
my $group = $nlines / $nprocs;
my @pids;
while(<$fh>) {
  my ($A, $B, $T) = split/\s+/;
  substr($buf, $ln * $bytes_per_line, $bytes_per_line, pack "L3", ($A, $B, $T));
  if( defined $this_group_start ) {
    if( $T - $last_group_start >= $group + 100 ) {
      if(my $pid = fork()) {
        push @pids, $pid;
        $last_group_start = $this_group_start;
        undef $this_group_start;
      } else {
#warn "checking $last_group_start - $ln...\n";
        for(my $l=$last_group_start; $l<=$ln; ++$l) {
          my $lpos = $l * $bytes_per_line;
          my ($A, $B, $T) = unpack "L3", substr($buf, $lpos, $bytes_per_line);
          my ($lA, $lB);
          my $lT = $T;
          for(my $lb=$l; $lb>=$last_group_start && $T - $lT <= 100; $lb--, $lpos -= $bytes_per_line) {
            ($lA, $lB, $lT) = unpack "L3", substr($buf, $lpos, $bytes_per_line);
            if($A == $lB || $B == $lA) {
              #print "($last_group_start) $A $B $T matches $lA $lB $lT\n";
              print "$lA $lB $lT\n$A $B $T\n";
            }
          }
        }
        exit;
      }
    }
  } elsif( !defined $this_group_start && $T - $last_group_start >= $group ) {
    $this_group_start = $ln;
  }
  $ln++;
}

waitpid $_, 0 for @pids;

เอาต์พุตที่ไม่เรียงลำดับ:

8455767 30937130 50130
20468509 8455767 50175
47249523 17051933 111141
17051933 34508661 111215
39504040 36752393 196668
42758015 39504040 196685
25072294 28422439 329284
35458609 25072294 329375
45340163 42711710 6480186
39315845 45340163 6480248
1435779 49643646 12704996
38229692 1435779 12705039
18487099 24556657 6665821
24556657 28498505 6665884
6330540 35363455 18877328
22500774 6330540 18877347
10236123 22026399 598647
39941282 10236123 598717
45756517 24831687 6726642
34578158 45756517 6726670
29385533 7181838 621179
7181838 29036551 621189
40647929 11895227 25075557
11895227 1900895 25075652
17921258 42642822 18935923
40140275 17921258 18935949
44573044 38139831 12899467
38139831 1321655 12899468
11223983 1788656 12920946
1788656 21905607 12921040
1357565 8148234 801402
8148234 46556089 801498
30929735 303373 19105532
31258424 30929735 19105543
34899776 9929507 6990057
9929507 49221343 6990078
49779853 43951357 25306335
41120244 49779853 25306424
6177313 41551055 25343755
24462722 6177313 25343804
16392217 32915797 31472388
32915797 19696674 31472479
6834305 36264354 25440771
44983650 6834305 25440800
26559923 47360227 19356637
47360227 49749757 19356700
33018256 36233269 37654651
36233269 5459333 37654671
6932997 23123567 25502355
23123567 7882426 25502356
5878434 43421728 25510707
43421728 40827189 25510765
38695636 33504665 1099515
13504170 38695636 1099605
32832720 40188845 37689854
8335398 32832720 37689927
35858995 41917651 1130028
41917651 28797444 1130096
47102665 6796460 43806189
6796460 6113288 43806229
21248273 5422675 43819677
48011830 21248273 43819728
32187324 39177373 25624030
39177373 42539402 25624102
41722647 14351373 25626925
14351373 45070518 25627013
22298566 25860163 37862683
2273777 22298566 37862692
10617763 32776583 7561272
35581425 10617763 7561279
18526541 18709244 31960780
18709244 32777622 31960867
36976439 24222624 31973215
24222624 9534777 31973262
25751007 11612449 38066826
43652333 25751007 38066923
8303520 2615666 7633297
2615666 29961938 7633357
22317573 31811902 31982722
14298221 22317573 31982819
43089781 7653813 44154683
8732322 43089781 44154769
24227311 43800700 13711475
40906680 24227311 13711539
48061947 30109196 7660402
43993467 48061947 7660488
29580639 5292950 38140285
5292950 21293538 38140356
17646232 47737600 32058831
47737600 42934248 32058836
13262640 23462343 1617194
23462343 1901587 1617259
5150775 7046596 44270140
7046596 22819218 44270181
17749796 34924638 32171251
8386063 17749796 32171346
30095973 12202864 38257881
12202864 42679593 38257912
10353022 40646034 26158412
40646034 36237182 26158412
8416485 16245525 32223010
16245525 32420032 32223081
20420340 1371966 7893319
1371966 2031617 7893335
2864137 20279212 26199008
29145409 2864137 26199080
29141766 19729396 44433106
44115780 29141766 44433141
6513924 34515379 32283579
12686666 6513924 32283636
20116056 49736865 44464394
49736865 47918939 44464416
38212450 3465543 32302772
3465543 39217131 32302873
12019664 37367876 44485630
3639658 12019664 44485639
18053021 1279896 7973955
2220749 18053021 7974031
19701732 12984505 1857435
24625926 19701732 1857528
9876789 34881917 26285125
27687743 9876789 26285134
5696632 6064263 44534580
34888313 5696632 44534629
14865531 46418593 38457138
5929897 14865531 38457191
44378135 4051962 38485208
4051962 10804515 38485308
11865822 21793388 14142622
7760360 11865822 14142649
32333570 24478420 44702533
24478420 23749609 44702588
29098286 25015092 44723985
32171647 29098286 44723985
20522503 20522503 2127735
20522503 20522503 2127735
22597975 20938239 8260902
20938239 48618802 8260905
8310032 34659671 2153994
34659671 25406149 2154075
49085033 5708432 26644257
5708432 32265692 26644305
18751513 18226037 32726402
18226037 33885794 32726424
45877488 23211339 20566948
23211339 26209405 20567002
48554034 25770643 38853402
9683274 48554034 38853467
9770420 14556349 2309265
27255587 9770420 2309324
32926392 16744099 44954824
24840989 32926392 44954840
29066838 49434549 26755357
49434549 12635292 26755407
21927714 32352409 20626921
32352409 15895076 20626932
7422009 23559357 14550898
32743911 7422009 14550982
38816601 5850890 26851026
5850890 32996623 26851107
42148171 47021378 26872907
47021378 32628418 26872908
9850929 10501741 32998960
10501741 24899993 32999043
27491904 4393602 33033499
4393602 17712085 33033570
37978226 42226216 39114479
42226216 2511412 39114525
42859989 49908919 45241083
48131208 42859989 45241088
39753103 30674979 14807321
30674979 45637890 14807371
30154199 11988643 2641926
11988643 11241926 2641976
7191871 13518594 45370275
13518594 45354921 45370344
54745 19711137 8871851
24814115 54745 8871937
38770495 34574748 2756244
41962321 38770495 2756337
26229406 39306415 21057327
10735951 26229406 21057347
46704290 11506122 39359422
18181795 46704290 39359481
38796645 28410469 45452212
28410469 13478996 45452222
412456 27727741 39466147
27727741 19639136 39466226
24470627 13030982 21266756
13030982 21713410 21266825
6058593 23139172 27435254
19236012 6058593 27435303
14457750 39190113 39701131
30253141 14457750 39701227
26898421 39016446 45812750
40952330 26898421 45812829
18647206 27663400 45817956
27663400 21728474 45817989
5559358 41319001 33664547
41319001 37210583 33664636
29066692 30653068 39759813
30653068 38963132 39759856
12086617 49971187 3232640
49971187 32302154 3232649
12008399 13656671 3239395
43088998 12008399 3239439
10061612 38594475 39804389
38594475 6327106 39804405
16703379 21150436 39851057
21150436 34093320 39851136
1035486 4199407 3314170
26974438 1035486 3314196
21869320 14532221 33851404
15208937 21869320 33851473
38840190 4742355 3402401
4742355 46055869 3402462
34432016 8734566 39966972
27614117 34432016 39967002
9988172 49209666 46063078
49209666 29374155 46063087
3208946 47030309 21722002
47030309 39809983 21722030
10928661 46423741 3496854
46423741 29486710 3496862
42464855 22978174 46154827
22978174 3814497 46154901
47090840 16768393 46169667
39523858 47090840 46169714
28186104 11618234 34024001
11618234 33711158 34024019
45471813 37332848 3585557
37332848 4607526 3585600
14885742 38990612 15863749
38990612 3710491 15863779
42391514 33643913 22005928
33643913 32254640 22006022
4299590 19482026 34202327
19482026 35838894 34202406
24298776 16276160 3858885
16276160 3198758 3858958
29322567 12536696 40433239
12536696 26083938 40433317
16080151 9648322 22221443
9648322 43846385 22221458
999302 19218350 10078183
10296062 999302 10078189
40544377 34492433 34463953
19908418 40544377 34463993
10765321 45143043 34542584
39154522 10765321 34542646
48642526 31097951 4104790
2940654 48642526 4104887
26972730 47422139 46846889
39228577 26972730 46846901
13788696 11503551 34728076
11503551 9151627 34728130
8676030 30463644 10406398
15204754 8676030 10406405
42984277 41087708 34805119
48741576 42984277 34805143
29634598 2151247 22699609
12264074 29634598 22699614
47525963 48470003 16667878
48470003 4566846 16667953
9725907 43325112 4498307
26465445 9725907 4498368
306967 11708860 10633595
11708860 31017081 10633669
39420965 46595640 41089015
46595640 41260374 41089048
29232745 39705052 16754836
4739295 29232745 16754840
35246405 42811088 41273637
48986699 35246405 41273719
2398239 36985098 35181790
36985098 7460784 35181841
18955749 23678549 35221035
47264406 18955749 35221129
18105816 26003002 17044057
26003002 17467477 17044087
14430126 46039962 47492180
46039962 29118827 47492275
30329324 40926812 41425850
43304610 30329324 41425912
34966996 36567528 17095113
3967517 34966996 17095144
42829171 42530474 23209891
25923738 42829171 23209967
28187681 26297990 35474412
48986691 28187681 35474475
5707126 41598794 17298139
40466899 5707126 17298188
28838696 30725820 5142797
30725820 35360418 5142798
44642019 42570370 17339657
42570370 19022469 17339727
42193681 8389736 17386517
48906013 42193681 17386586
42303185 30337820 41795129
30337820 42473956 41795170
30935782 8441903 17515229
41549758 30935782 17515275
41239019 10011768 23619001
10011768 25386353 23619062
494288 13341166 29815779
49113152 494288 29815876
7106674 26227442 29833029
47459682 7106674 29833047
17246497 35389391 17628365
35389391 34005133 17628371
23347674 48243185 17792799
48243185 22907892 17792836
21852744 1662414 36088704
8040124 21852744 36088775
32384657 27122374 36100767
24980361 32384657 36100782
31016207 26300043 42222489
26300043 36869529 42222544
17178756 44315094 42223989
44315094 11222466 42224042
34139317 39164101 36197907
39164101 27563542 36197947
31638631 22215137 17999735
22215137 10771707 17999769
30257199 32883043 24127009
32883043 179099 24127047
47774058 17451960 30283073
44583527 47774058 30283162
13816647 12695130 24145102
12695130 42284941 24145188
42749234 20004242 5893793
20004242 38129713 5893819
22210359 22178109 18109989
22178109 112961 18110049
42509645 28599506 42508465
28599506 3722411 42508513
34412629 22547405 48610262
22547405 16664124 48610296
2330283 32267749 24256113
35915758 2330283 24256157
44560231 49353986 12101694
6471293 44560231 12101780
23289721 8186827 30407293
10624448 23289721 30407389
12329357 35765163 30560085
4511908 12329357 30560158
31332240 39704929 12269193
39704929 47770487 12269249
22286152 22082044 36734758
22082044 25076919 36734833
47381309 9459604 36735886
9459604 31071680 36735890
43832763 45342283 30707519
45342283 26992816 30707602
2883029 18642608 42989696
14697025 2883029 42989793
15149987 40746227 24700535
40746227 34776566 24700549
2387554 49015265 43057085
49015265 21103141 43057139
23057202 13308993 30982514
34596334 23057202 30982553
44598498 31714790 43285828
18170064 44598498 43285841
38273701 11976319 31179763
15344094 38273701 31179764
3651338 27427037 37188945
12876654 3651338 37189007
10081580 3418061 37221143
3418061 38353019 37221143
172544 18699860 37295343
824744 172544 37295372
13914 8890169 37303853
8890169 14008003 37303898
18716557 29456130 49605004
29456130 16390535 49605083
15398102 22446674 43711290
22446674 38760679 43711383

ฉันแน่ใจว่านี่จะเป็นลำดับความสำคัญได้เร็วขึ้นใน C แต่ฉันอาจจะไม่ใช้เวลาในการทำเช่นนั้น


2
ฉันไม่แน่ใจว่าผลลัพธ์ของคุณค่อนข้างถูกต้อง มองไปที่สองแถวแรก: A = D = 8455767แต่U = 50175, T = 50130และอื่น ๆT - U = -45
James_pic

2

C # - 30 วินาที

วิธีการที่แตกต่างจากส่วนใหญ่ถ้าฉันอ่านถูกต้อง - ฉันไม่ได้ใช้โครงสร้างแบบแฮช

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

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FilterFile
{
    class Program
    {
        const int COUNT = 50000000;

        static string inputFile = "data" + COUNT + ".txt";
        static string outputFile = "results.txt";

        static void Main(string[] args)
        {
            Console.WriteLine("Prepping Test");
            if (args.Length > 0) inputFile = args[0];
            if (args.Length > 1) outputFile = args[1];

            if (!File.Exists(inputFile))
            {
                Console.WriteLine(inputFile);

                File.WriteAllLines(inputFile,
                                     GenerateData(COUNT)
                                     .Select(r => string.Format("{0} {1} {2}", r.A, r.B, r.C)));
            }

            File.Delete("results.txt");

            Console.WriteLine("Starting Test \n\n");

            using (Timer.Create("Total Time"))
            {
                Row[] sortedA, sortedB;
                //http://codegolf.stackexchange.com/questions/26643/filter-a-large-file-quickly
                using (Timer.Create("Reading Data"))
                    FillData(out sortedA, out sortedB);

                using (Timer.Create("Parallel Sort A"))
                    ParallelSort.QuicksortParallel(sortedA);
                using (Timer.Create("Parallel Sort B"))
                    ParallelSort.QuicksortParallel(sortedB, (x, y) => x.B - y.B);

                object rLock = new object();
                List<Row> results = new List<Row>();

                var comparison = Comparer<Row>.Create((B, A) => B.B - A.A);
                using (Timer.Create("Compute Results"))
                    Parallel.ForEach(sortedA, row =>
                    //foreach (var row in sortedA)
                    {
                        var i = Array.BinarySearch(sortedB, row, comparison);
                        if (i < 0) return;

                        Row other;
                        bool solved = false;
                        for (var tempI = i; tempI < sortedB.Length && row.A == (other = sortedB[tempI]).B; tempI++)
                        {
                            var diff = row.C - other.C;
                            if (diff >= 0 && diff < 100)
                            {
                                lock (rLock) results.Add(row);
                                return;
                            }
                        }

                        for (var tempI = i - 1; tempI >= 0 && row.A == (other = sortedB[tempI]).B; tempI--)
                        {
                            var diff = row.C - other.C;
                            if (diff >= 0 && diff < 100)
                            {
                                lock (rLock) results.Add(row);
                                return;
                            }
                        }
                    });

                using (Timer.Create("Save Results"))
                {
                    File.WriteAllLines(outputFile, results.Select(r => r.ToString()));
                }
            }
        }

        private static void FillData(out Row[] sortedA, out Row[] sortedB)
        {
            var tempA = new Row[COUNT];
            var tempB = tempA;//new Row[COUNT];

            const int PARTITION_SIZE = 1 << 22;

            ReadAndSort(tempA, tempB, PARTITION_SIZE);

            sortedA = tempA;
            sortedB = new Row[COUNT];
            Array.Copy(sortedA, sortedB, COUNT);
            /*using (Timer.Create("MergeA"))
            {
                int destIndex = 0;
                int[][] partitions = Enumerable.Range(0, COUNT / PARTITION_SIZE + 1)
                    .Select(i => new[] { i * PARTITION_SIZE, Math.Min(i * PARTITION_SIZE + PARTITION_SIZE, COUNT) - 1 })
                    .ToArray();

                for (int i = 0; i < COUNT; i++)
                {
                    foreach (var partition in partitions)
                    {
                        while (partition[0] <= partition[1] && tempA[partition[0]].A == i)
                        {
                            sortedA[destIndex++] = tempA[partition[0]++];
                        }
                    }
                }
            }*/

            /*//Verify Paritioning Works
            var results = new List<Tuple<Row, int>> { Tuple.Create(tempA[0], 0) };
            for (int i = 1; i < tempA.Length; i++)
            {
                var r = tempA[i];
                if (r.A < tempA[i-1].A)
                    results.Add(Tuple.Create(r, i % PARTITION_SIZE));
            }
            results.ForEach(t => Console.WriteLine(t.Item1 + " " + t.Item2));*/
        }

        private static void ReadAndSort(Row[] tempA, Row[] tempB, int PARTITION_SIZE)
        {
            List<Task> tasks = new List<Task>();

            using (var stream = File.OpenRead(inputFile))
            {
                int b;
                int tempMember = 0;
                int memberIndex = 0;
                int elementIndex = 0;

                using (Timer.Create("Read From Disk"))
                    while ((b = stream.ReadByte()) >= 0)
                    {
                        switch (b)
                        {
                            case (byte)'\r':
                            case (byte)' ':
                                switch (memberIndex)
                                {
                                    case 0: tempA[elementIndex].A = tempMember; memberIndex = 1; break;
                                    case 1: tempA[elementIndex].B = tempMember; memberIndex = 2; break;
                                    case 2: tempA[elementIndex].C = tempMember; memberIndex = 0; break;
                                }
                                tempMember = 0;
                                break;
                            case (byte)'\n':
                                /*if (elementIndex % PARTITION_SIZE == 0 && elementIndex > 0)
                                {
                                    var copiedIndex = elementIndex;
                                    tasks.Add(Task.Run(() =>
                                    {
                                        var startIndex = copiedIndex - PARTITION_SIZE;
                                        Array.Copy(tempA, startIndex, tempB, startIndex, PARTITION_SIZE);
                                        ParallelSort.QuicksortSequentialInPlace(tempA, startIndex, copiedIndex - 1);
                                        ParallelSort.QuicksortSequentialInPlace(tempB, startIndex, copiedIndex - 1, (x, y) => x.B - y.B);
                                    }));
                                }*/
                                elementIndex++;
                                break;
                            default:
                                tempMember = tempMember * 10 + b - '0';
                                break;
                        }
                    }

                /* tasks.Add(Task.Run(() =>
                 {
                     elementIndex--;  //forget about the last \n
                     var startIndex = (elementIndex / PARTITION_SIZE) * PARTITION_SIZE;
                     Array.Copy(tempA, startIndex, tempB, startIndex, elementIndex - startIndex + 1);
                     ParallelSort.QuicksortParallelInPlace(tempA, startIndex, elementIndex);
                     ParallelSort.QuicksortSequentialInPlace(tempB, startIndex, elementIndex, (x, y) => x.B - y.B);
                 }));

                 using (Timer.Create("WaitForSortingToFinish"))
                     Task.WaitAll(tasks.ToArray());*/
            }
        }

        static Random rand = new Random();

        public struct Row : IComparable<Row>
        {
            public int A;
            public int B;
            public int C;
            public static Row RandomRow(int count)
            {
                return new Row { A = rand.Next(count), B = rand.Next(count), C = rand.Next(count) };
            }

            public int CompareTo(Row other)
            {
                return A - other.A;
            }

            public override string ToString()
            {
                return string.Format("{0} {1} {2}", A, B, C);
            }
        }

        public static Row[] GenerateData(int count)
        {
            var data = new Row[count];
            for (int i = 0; i < count; i++)
                data[i] = Row.RandomRow(count);
            return data;
        }

        public static Row[] GenerateSplitData(int count)
        {
            var data = new Row[count];
            for (int i = 0; i < count; i++)
                data[i] = Row.RandomRow(count);
            return data;
        }

        public class Timer : IDisposable
        {
            string message;
            Stopwatch sw;
            public static Timer Create(string message)
            {
                Console.WriteLine("Started: " + message);
                var t = new Timer();
                t.message = message;
                t.sw = Stopwatch.StartNew();
                return t;
            }
            public void Dispose()
            {
                Console.WriteLine("Finished: " + message + " in " + sw.ElapsedMilliseconds + "ms");
            }
        }

        // <summary> 
        /// Parallel quicksort algorithm. 
        /// </summary> 
        public class ParallelSort
        {
            const int SEQUENTIAL_THRESHOLD = 4096;
            #region Public Static Methods

            /// <summary> 
            /// Sequential quicksort. 
            /// </summary> 
            /// <typeparam name="T"></typeparam> 
            /// <param name="arr"></param> 
            public static void QuicksortSequential<T>(T[] arr) where T : IComparable<T>
            {
                QuicksortSequentialInPlace(arr, 0, arr.Length - 1);
            }

            /// <summary> 
            /// Parallel quicksort 
            /// </summary> 
            /// <typeparam name="T"></typeparam> 
            /// <param name="arr"></param> 
            public static void QuicksortParallel<T>(T[] arr) where T : IComparable<T>
            {
                QuicksortParallelInPlace(arr, 0, arr.Length - 1);
            }

            #endregion

            #region Private Static Methods

            public static void QuicksortSequentialInPlace<T>(T[] arr, int left, int right)
                where T : IComparable<T>
            {
                if (right > left)
                {
                    int pivot = Partition(arr, left, right);
                    QuicksortSequentialInPlace(arr, left, pivot - 1);
                    QuicksortSequentialInPlace(arr, pivot + 1, right);
                }
            }

            public static void QuicksortParallelInPlace<T>(T[] arr, int left, int right)
                where T : IComparable<T>
            {
                if (right > left)
                {
                    if (right - left < SEQUENTIAL_THRESHOLD)
                        QuicksortSequentialInPlace(arr, left, right);
                    else
                    {
                        int pivot = Partition(arr, left, right);
                        Parallel.Invoke(() => QuicksortParallelInPlace(arr, left, pivot - 1),
                                        () => QuicksortParallelInPlace(arr, pivot + 1, right));
                    }
                }
            }

            private static void Swap<T>(T[] arr, int i, int j)
            {
                T tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
            }

            private static int Partition<T>(T[] arr, int low, int high)
                where T : IComparable<T>
            {
                // Simple partitioning implementation 
                int pivotPos = (high + low) / 2;
                T pivot = arr[pivotPos];
                Swap(arr, low, pivotPos);

                int left = low;
                for (int i = low + 1; i <= high; i++)
                {
                    if (arr[i].CompareTo(pivot) < 0)
                    {
                        left++;
                        Swap(arr, i, left);
                    }
                }

                Swap(arr, low, left);
                return left;
            }

            #endregion

            #region Public Static Methods

            /// <summary> 
            /// Sequential quicksort. 
            /// </summary> 
            /// <typeparam name="T"></typeparam> 
            /// <param name="arr"></param> 
            public static void QuicksortSequential<T>(T[] arr, Func<T, T, int> comparer)
            {
                QuicksortSequentialInPlace(arr, 0, arr.Length - 1, comparer);
            }

            /// <summary> 
            /// Parallel quicksort 
            /// </summary> 
            /// <typeparam name="T"></typeparam> 
            /// <param name="arr"></param> 
            public static void QuicksortParallel<T>(T[] arr, Func<T, T, int> comparer)
            {
                QuicksortParallelInPlace(arr, 0, arr.Length - 1, comparer);
            }

            #endregion

            #region Private Static Methods

            public static void QuicksortSequentialInPlace<T>(T[] arr, int left, int right, Func<T, T, int> comparer)
            {
                if (right > left)
                {
                    int pivot = Partition(arr, left, right, comparer);
                    QuicksortSequentialInPlace(arr, left, pivot - 1, comparer);
                    QuicksortSequentialInPlace(arr, pivot + 1, right, comparer);
                }
            }

            public static void QuicksortParallelInPlace<T>(T[] arr, int left, int right, Func<T, T, int> comparer)
            {
                if (right > left)
                {
                    if (right - left < SEQUENTIAL_THRESHOLD)
                    {
                        QuicksortSequentialInPlace(arr, left, right, comparer);
                    }
                    else
                    {
                        int pivot = Partition(arr, left, right, comparer);
                        Parallel.Invoke(() => QuicksortParallelInPlace(arr, left, pivot - 1, comparer),
                                        () => QuicksortParallelInPlace(arr, pivot + 1, right, comparer));
                    }
                }
            }

            private static int Partition<T>(T[] arr, int low, int high, Func<T, T, int> comparer)
            {
                // Simple partitioning implementation 
                int pivotPos = (high + low) / 2;
                T pivot = arr[pivotPos];
                Swap(arr, low, pivotPos);

                int left = low;
                for (int i = low + 1; i <= high; i++)
                {
                    if (comparer(arr[i], pivot) < 0)
                    {
                        left++;
                        Swap(arr, i, left);
                    }
                }

                Swap(arr, low, left);
                return left;
            }
            #endregion
        }
    }
}

คุณควรได้รับผลลัพธ์ประมาณ 200 รายการโดยไม่คำนึงถึงขนาดของข้อมูลอินพุตของคุณ ฉันสงสัยว่าปัญหาของคุณเกี่ยวข้องกับวิธีที่คุณใช้การค้นหาแบบไบนารีในบรรทัด 98-102 - ฉันสงสัยว่าคุณคิดว่าx.AจะมาจากsortedAและx.BจะมาจากsortedBในขณะที่ในความเป็นจริงทั้งสองจะมาจากsortedBและสิ่งนี้Comparerจะผลิต ผลลัพธ์ไร้สาระ
James_pic

โดยทั่วไปหากคุณกำลังจัดเรียงAและBมีอัลกอริทึมที่เร็วกว่าการวนซ้ำAและการค้นหาแบบไบนารีBซึ่งเป็นO(n log(n))(และมีประสิทธิภาพเป็นตารางแฮชของคนจน) O(n)คุณสามารถแทนผสานเข้าร่วมทั้งสองรายการซึ่งเป็น
James_pic

ตัวเลือกสนุกอีกเพราะคุณรู้ว่าค่าของBจะถูกกระจายอย่างสม่ำเสมอในช่วงที่เฉพาะเจาะจงที่จะแลกเปลี่ยนค้นหา binary สำหรับการค้นหาการแก้ไขซึ่งจะช่วยลดเวลาในการค้นหาจากไปO(log(n)) O(log(log(n))
James_pic

@ James_pic ขอบคุณสำหรับคำแนะนำฉันจะไล่ล่าพวกเขาหากฉันมีเวลา ฉันเพิ่งตัด 40 วินาทีออกจาก IO ของฉันเพื่อให้ฉันสามารถมุ่งเน้นไปที่การเรียงลำดับและการคำนวณอีกครั้ง
NPSF3000

ผลการเปรียบเทียบคงที่แล้ว การคำนวณคิดเป็นเพียงประมาณ 5 วินาทีในสามสิบของฉัน (ใส่ 12, เรียงลำดับ 5 แต่ละอัน) ดังนั้นฉันจึงคิดถึงการโจมตีบรรทัดต่อไปของฉัน IO กำลังประมวลผลที่ ~ 100MBps ดังนั้นการเพิ่มความเร็วอาจมี จำกัด
NPSF3000

1

โหดร้ายกำลังดุร้ายหน้าของคุณน่าเกลียด C. ในการทำมากกว่าฉันจะเลือกภาษาที่รวบรวมอื่น ๆ

/*
Filter a file based on these rules:

Input:
    - each item is an ordered list of three integers ( A B T )
    - each line represents an item
    - each line is formated as <number> <w> <number> <w> <number>
    - <w> is whitespace (a single blank in the challenge)
    - <number> is an integer in the range 0..49_999_999
    - the first number on a line is A, second B, third T

Output a given item ( A B T ) if:
    1 - there exists an item ( C D U ) such that 0 <= T-U < 100 and D == A 
    OR
    2 - there exists an item ( C D U ) such that 0 <= U-T < 100 and B == C 

CLARIFICATION:
An item should be output only once, even if there is more than one match.

We're sorting on T, we know the number of Ts to be sorted and the Ts are random.
Trade space for speed and create a lookup table that can handle collisions
(AKA hash table).
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <assert.h>


#define NTHREADS    (16)
#define BINSPERTHREAD   (1*1000*1000)
bool    oneThread = false;

typedef struct {
    pthread_t   tid;
    long        begin;
    long        end;
} threadState;

void *initTID() {
    return NULL;
}


#define MAXITEMS    (50*1000*1000)
//  items on the boundary are not included in the search
#define SEARCHBOUNDARY  (100)


void usage(char *name) {
    fprintf(stderr, "usage: %s [-n 1..%d]\n", name, MAXITEMS);
}

typedef struct item {
    long    A;
    long    B;
    long    T;
    bool    unprinted;
    struct item *b;         // b(ackward to previous item)
    struct item *f;         // f(orward to next item)
    struct item *BINb;          // backward to previous bin
    struct item *BINf;          // forward to next bin
#ifdef DEVTEST
    long    lineNumber;
#endif
} item;
#ifdef DEVTEST
bool    printVerbose = false;
#endif


//  Why global variables? Because large MAXITEMS overflow the stack.
long    maxItems;           // entries allocated in list & lookup
item    *list;
long    listN;              // number of entries (max index + 1)
item    **lookup;
long    lookupN;            // number of entries (max index + 1)


/*
input -
    n       - index of current bin
    list        - global
    lookup      - global
    lookupN     - global
side-effects -
    list[] (.unprinted)
    stdout
*/
static inline void *walkThisBin(long n) {
    item    *p;
    item    *searchHead;
    item    *searchTail;
    item    *currentItem;
    long    i;

    //  for all items in bin
    for ( currentItem = lookup[n]; currentItem != lookup[n]->BINf;
        currentItem = currentItem->f)
    {
    /*
        merged forward&backward search
    */
    searchHead = currentItem;
    //  step to index min((T+100-1),lookupN-1), find largest U<T+100
    i = ((n+SEARCHBOUNDARY-1) < lookupN) ?
        n+SEARCHBOUNDARY-1 :
        lookupN-1;
    //  find largest i such that U-T<100 (U is lookup[i]->T)
    //  degenerate case is i == n
    for(p=lookup[i];
        !((p->T-searchHead->T)<SEARCHBOUNDARY);
        p=lookup[i]) {
        i--;
    }
    searchTail = p->BINf;       // boundary, not included in search
    p = currentItem;
    do {
        if (searchHead->B == p->A) {
        //  matches are symmetric
        if (searchHead->unprinted) {
            printf("%ld %ld %ld\n", searchHead->A, searchHead->B,
            searchHead->T);
            searchHead->unprinted = false;
        }
        if (p->unprinted) {
            printf("%ld %ld %ld\n", p->A, p->B, p->T);
            p->unprinted = false;
        }
        }
        p = p->f;
    } while (p!=searchTail);
    }
    return NULL;
}

/*
Must handle out-of-range indexes for lookup.

input -
    n       - index of current bin
    list        - global
    lookup      - global
    lookupN     - global
side-effects -
    list (.unprinted)
    stdout
*/

static inline void *walkTheseBins(void *tState) {
    long    startIndex = ((threadState *)tState)->begin;
    long    finishIndex = ((threadState *)tState)->end;
    long    n;

    startIndex = (startIndex<0) ? 0 : startIndex;
    finishIndex = (finishIndex>lookupN-1) ? lookupN-1 : finishIndex;
    for (n=startIndex; n<=finishIndex; n++) {
    walkThisBin(n);
    }
    return NULL;
}


int main(int argc, char *argv[]) {
#ifdef DEVTEST
item    *head;
item    *tail;
long    count = 0;
#endif
    //  subroutines? subroutines? we don't need no stinkin' subroutines
    //  this is all the scoping you're going to need
    //                      ... truuuuust me
    /*
    Allocate list[] and lookup[]. Set maxItems.

    input -
        argc
        argv
    side-effects -
        list
        lookup
        maxItems
        malloc()
        DEVTEST stuff
    */
    {
    int c;          // option character

    maxItems = MAXITEMS;
    while ((c = getopt(argc, argv, ":n:sv")) != -1) {
        switch(c) {
#ifdef DEVTEST
        case 'v':
        //  print some reassuring messages
        printVerbose = true;
        break;
#else
        case 'v':
        fprintf(stderr, "unknown option -%c\n", optopt);
        usage(argv[0]);
        exit(1);
        break;
#endif
        case 'n':
        if (sscanf(optarg, "%ld", &maxItems) != 1) {
            fprintf(stderr, "-n argument \"%s\" unscannable\n", optarg);
            usage(argv[0]);
            exit(1);
        }
        break;
        case 's':
        //  use only one thread?
        oneThread = true;
        break;
        case ':':           // -s needs an argument
        usage(argv[0]);
        exit(1);
        break;
        case '?':           // not a valid option
        fprintf(stderr, "unknown option -%c\n", optopt);
        usage(argv[0]);
        exit(1);
        break;
        }
    }
    if ((maxItems<1) || (maxItems>MAXITEMS)) {
        fprintf(stderr, "-s argument \"%ld\" out of range\n", maxItems);
        usage(argv[0]);
        exit(1);
    }
    list = (item *) malloc(sizeof(item) * maxItems);
    if (list == NULL) {
        fprintf(stderr, "ERROR: list = malloc() failure\n");
        exit(1);
    }
    lookup = (item **) malloc(sizeof(item *) * maxItems);
    if (lookup == NULL) {
        fprintf(stderr, "ERROR: lookup = malloc() failure\n");
        exit(1);
    }
    }

    /*
    Convert STDIN into an array of items.

    input -
        list
        lookup
        maxItems
    side-effects -
        list
        lookup
        listN
        stdin
    */
    {
    long    largestT = 0;
    item    x;

    for (listN=0; scanf("%ld%ld%ld", &x.A, &x.B, &x.T)==3; listN++) {
        if (listN == maxItems) {
        fprintf(stderr, "ERROR: > %ld input items read\n", maxItems);
        exit(1);
        }
        x.b = x.f = NULL;
        x.unprinted = true;
        x.BINb = x.BINf = NULL;
        largestT = (x.T>largestT) ? x.T : largestT;
#ifdef DEVTEST
        x.lineNumber = listN + 1;
#endif
        list[listN] = x;
    }
    if (!feof(stdin)) {
        fprintf(stderr, "ERROR: ferror() = %d\n", ferror(stdin));
        exit(1);
    }
    //  Be paranoid. Because cores are obnoxious.
    if (largestT>=maxItems) {
        fprintf(stderr, "ERROR: T:%ld > %ld \n", largestT, maxItems-1);
        exit(1);
    }
    }
#ifdef DEVTEST
(printVerbose) && printf("in: %ld\n", listN);
#endif
    //  Short-circuit on 0 items. Simplifies things like finding the head.
    if  (listN == 0) {
    exit(0);
    }

    /*
    Populate the lookup table. Build a doubly linked list through it.

    input -
        list
        lookup
        listN
    side-effects -
        list[]
        lookup[]
        lookupN
        DEVTEST stuff
    */
    {
    long    n;

    /*
        Populate the lookup table. The lookup table is an array-of-lists.
    The lists are LIFO. This is the most primitive of hashes, where the
    key, item.T, is used as the index into the lookup table.
    */
    for (n=0; n<maxItems; n++) {
        lookup[n] = NULL;
    }
    for (n=0; n<listN; n++) {
        long    t = list[n].T;

        if (lookup[t] == NULL) {
        lookup[t] = &(list[n]);
        } else {
        // collision
        list[n].f = lookup[t];  // forward pointer assigned
        lookup[t] = &(list[n]);
        }
    }
    /*
        Collapse lookup to squeeze out NULL references. This breaks
    the linear mapping between T value & lookup index, but worth it for
    simpler search logic. Build a doubly linked list of bins.
    */
    item    *previousBin = NULL;    // last non-NULL lookup entry
    lookupN = 0;
    for (n=0; n<maxItems; n++) {
        if (lookup[n] != NULL) {
        lookup[lookupN] = lookup[n];
        lookup[lookupN]->BINb = previousBin;
        if (previousBin) {
            previousBin->BINf = lookup[lookupN];
        }
        previousBin = lookup[lookupN];
        lookupN++;
        }
    }
    previousBin->BINf = NULL;

    /*
        Build a doubly linked list. The forward pointers already exist
    within each lookup table bin.
    */
    item    *p;
    item    *binHead;
    item    *previous;

    //  create a loop in each bin
    for (n=0; n<lookupN; n++) {
#ifdef DEVTEST
count++;
#endif
        binHead = lookup[n];
        for (p=binHead; p->f; p=p->f) {
        p->f->b = p;
#ifdef DEVTEST
count++;
#endif
        }
        p->f = binHead;
        binHead->b = p;
    }
    //  break the loops and connect them tail-to-head
#ifdef DEVTEST
head = lookup[0];
#endif
    previous = NULL;
    for (n=0; n<lookupN; n++) {
        binHead = lookup[n];
        p = binHead->b;     // p => tail of this bin list
        binHead->b = previous;  // connect bin head to list
        if (previous) {     // connect list to bin head
        previous->f = binHead;
        }
        previous = p;
    }
    previous->f = NULL;
#ifdef DEVTEST
tail = previous;
#endif
    }

#ifdef DEVTEST
if (printVerbose) {
    printf("out: %ld\n", count);

    //  run through the list forwards
    item    *p;
    count = 0;
    for (p=head; p; p=p->f) {
    count++;
    }
    printf("forwards: %ld\n", count);
    //  run through the list backwards
    count = 0;
    for (p=tail; p; p=p->b) {
    count++;
    }
    printf("backwards: %ld\n", count);
    /*
        //  print the list
        for (p=head; p; p=p->f) {
        printf("%ld %ld %ld\n", p->A, p->B, p->T);
        }
    */
}
#endif

    /*
    Find matches & print.

    (authoritative statement)
    Print item ( A B T ) if:
    1 - there exists an item ( C D U ) such that 0 <= T-U < 100 and D == A 
        OR
    2 - there exists an item ( C D U ) such that 0 <= U-T < 100 and B == C 


    TBD
    - threading


    input -
        lookupN
    side-effects -
        lots hidden in walkTheseBins(), all thread-local or thread-safe
    */
    {
    volatile threadState    tState[NTHREADS]; // use as cicular buffer
    long                h;  // cicular buffer head
    long                n;

    if (oneThread) {
        tState[0].begin = 0;
        tState[0].end = lookupN-1;
        walkTheseBins((void *)tState);
    } else {
        //  every slot has a thread to wait for
        for (h=0; h<NTHREADS; h++) {
        assert( pthread_create(&(tState[h].tid), NULL, initTID, NULL) == 0);
        }
        h = 0;
        for (n=0; n<lookupN+BINSPERTHREAD; n+=BINSPERTHREAD) {
        pthread_join(tState[h].tid, NULL);
        tState[h].begin = n;
        tState[h].end = n + BINSPERTHREAD - 1;
        assert( pthread_create(&(tState[h].tid), NULL, walkTheseBins, (void *)(tState+h)) == 0);
        h = (h + 1) % NTHREADS;
        }
        //  wait for any remaining threads
        for (h=0; h<NTHREADS; h++) {
        pthread_join(tState[h].tid, NULL); // may have already join'ed some
        }
    }
    }

    return 0;
}

คอมไพล์ด้วย "gcc -m64 -pthreads -O" ต้องการอินพุตบน stdin รันมัลติเธรดโดยค่าเริ่มต้น ใช้ตัวเลือก "-s" เพื่อใช้เธรดเดียวเท่านั้น


ฉันได้รับคำเตือน: รูปแบบ '% d' คาดว่าจะมีอาร์กิวเมนต์เป็น 'int' แต่อาร์กิวเมนต์ 3 มีประเภท 'long int' [-Wformat =] fprintf (stderr, "ข้อผิดพลาด: T:% d>% d \ n" ที่ใหญ่ที่สุด , listN-1);

@Lembik ฉันแก้ไขซอร์สโค้ดสำหรับคำเตือนคอมไพเลอร์ & จะเพิ่มไปยัง makefile ของฉัน ฉันได้เพิ่มประโยคเกี่ยวกับวิธีใช้เมื่อสิ้นสุดการโพสต์ ฉันได้รับรุ่นที่มีเธรดแล้ว แต่ฉันต้องการรับการตรวจสอบประสิทธิภาพของประสิทธิภาพที่ไม่ได้อ่านบนเครื่องของคุณ
Scott Leadley

รหัสนี้ช้าสำหรับฉันอย่างแปลก (ดูเวลาที่กำหนด) เปรียบเทียบกับการส่ง Java หรือการส่ง C อื่น ๆ สำหรับคุณอย่างไร

ฉันคิดว่ารหัสของคุณไม่อนุญาตให้ TU = 0 ฉันต้องการทดสอบมันในไฟล์ที่มีเฉพาะบรรทัด 18662170 45121353 3365641 (ขึ้นบรรทัดใหม่) 44329255 18662170 3365641 แต่มันกลับข้อผิดพลาด

@ Lembik Ahh, T ต้อง <50M ไม่ใช่จำนวนบรรทัดของอินพุต ฉันแก้ไขและเพิ่มเธรด
Scott Leadley

1

ในที่สุดฉันก็มีโอกาสที่จะสร้างระบบ Ubuntu 14.04 ทางกายภาพที่คล้ายกับ Lembik's และทำการโพสต์ชันสูตรบนโซลูชันของฉันสำหรับปริศนานี้ ในการเลือกความสำคัญของฉัน:

  1. นายที่แท้จริงคือ James_pic เพราะเขาไม่ได้ปรับตัวก่อนเวลาอันควร
    • เขามีแผน
    • เขาดำเนินการตามแผนในระดับสูงของนามธรรม (Scala) และขัดเกลามันที่นั่น
    • เขากลั่นมันเพิ่มเติมใน C
    • เขาไม่ได้ขัดเกลามันมากเกินไป (ดูประเด็นต่อไป)
  2. ระบบไฟล์เวลา I / O น่าจะเป็นขอบเขตที่ต่ำกว่าในเวลาที่ผ่านไปสำหรับระบบเป้าหมาย
    • Lembik อ้างถึงสิ่งนี้คือ "ผู้ชนะ ... ทั้งคู่เกือบจะเร็วเท่ากับ wc!"
  3. สาเหตุบางประการที่โซลูชันดั้งเดิมของฉันดูดมีสาเหตุมาจาก:
    • สถานที่อ้างอิงเป็นปัจจัยสำคัญในระบบเป้าหมาย
    • การเรียงลำดับบน A หรือ B เป็นความคิดที่ดีเมื่อทำการเรียงลำดับแฮช การเรียงลำดับบน T จะเพิ่มความซับซ้อน (และการแคชทางอ้อม - ทางอ้อม) ให้กับการเรียงแฮชอย่างน้อยที่สุดวิธีที่ฉันทำ
    • Scanf () เป็นหมู
    • แบนด์วิดท์ขนาดใหญ่ (ดิสก์ -> หน่วยความจำ -> แคช) จะเปลี่ยนตำแหน่งที่เป็นคอขวด ระบบเป้าหมายไม่มีแบนด์วิดท์ขนาดใหญ่ (ดูประเด็นต่อไป)
  4. การพัฒนาอย่างรวดเร็วนั้นดีที่สุดหากทำในสภาพแวดล้อมเป้าหมาย
    • ดุจ! แต่เดิมฉันติดอยู่กับ Solaris / SPARC แต่เดิมและไม่สามารถเล่นเป็นอย่างอื่นได้
    • เป็นการยากที่จะกำจัดเอฟเฟกต์แคชในสภาพแวดล้อมเสมือนจริงและ SAN
    • Linux VMs มักมีปัญหาเดียวกัน
  5. คณิตศาสตร์ช่วยนิดหน่อย
    • การดึงหนึ่ง tuple โดยตรงจากตารางแฮชจะลดความน่าจะเป็นของการอ้างอิงทางอ้อมถึง ~ 37% (~ 1 / e)
    • การดึงทูเปิลสองตัวโดยตรงจากตารางแฮชจะตัดการอ้างอิงไปยังตารางโอเวอร์โฟลว์ถึง ~ 10% มันไม่จำเป็น
  6. รุ่นหน่วยความจำ 32 บิต (gcc -m32) เป็นสิ่งที่ทำให้ไขว้เขว
    • บางครั้งการชนะเล็กน้อยสำหรับโปรแกรมที่ยังไม่ได้อ่านบางครั้งก็ขาดทุนเล็กน้อย
    • บางครั้งการสูญเสียที่สำคัญสำหรับโปรแกรมเธรด
    • หาก 32- บิตเป็นผู้ชนะที่สำคัญ (และเป้าหมายไม่ใช่ตัวควบคุมแบบฝัง) อาจมีราคาถูกกว่าในการรีเฟรชฮาร์ดแวร์
    • จดทะเบียนเพิ่มเติมและเพิ่มพื้นที่ที่อยู่ให้กว้างขึ้นและอย่าหันหลังกลับ
  7. Scanf () เป็นหมู แต่การใช้ stdio นั้นไม่สิ้นหวัง
    • ค่าใช้จ่ายส่วนใหญ่ของ scanf () ดูเหมือนว่าอยู่ในรูปแบบการแยกวิเคราะห์ที่ขับเคลื่อนด้วยรูปแบบและการแปลงสตริงเป็นจำนวนเต็ม
    • เปลี่ยน sscanf () ด้วย:
      • strtok () + atoi () เร็วกว่า 2x2 เท่า (ดูตารางด้านล่าง)
      • strtol () เร็วขึ้น ~ 3x
      • กำหนดเอง strtol ท้องถิ่น () คือ ~ 6.5x เร็วขึ้น
      • แทนที่ strtol () ด้วยโซลูชันท้องถิ่นทำให้มันเสมอกับ "wc"
      • FSM ที่ใช้ getc_unlocked () เกือบเร็วเท่ากับโซลูชัน mmap () ที่เรียบง่ายของ Keith Randall
      • ผลลัพธ์ของการทดสอบของฉันในขณะที่นำไปใช้ใหม่ใน C [ใน CSV เนื่องจากเห็นได้ชัดว่า Exchange Exchange ไม่ได้ทำตาราง]:
        
        "solution (64-bit unless noted)","disposition of input","user","system","elapsed"
        "dd if=? of=/dev/null bs=1024k","","0.010","1.107","26.47"
        "wc {LANG=C}","","4.921","0.752","26.38"
        "","","","",""
        "fscanf()","discard","13.130","0.490","26.43"
        "fgets(), no integer conversion","discard","1.636","0.468","26.42"
        "fgets() + sscanf()","discard","16.173","0.498","26.48"
        "fgets() + strtok(), no integer conversion","discard","4.659","0.481","26.48"
        "fgets() + strtok() + atoi()","discard","8.929","0.490","26.49"
        "fgets() + strtol()","discard","6.009","0.483","26.50"
        "fgets() + custom-strtol()","discard","3.842","0.474","26.43"
        "fgets() + custom-strtol()","sort (load hash) while reading","7.118","1.207","26.70"
        "fgets() + custom-strtol()","sort, match & print","10.096","1.357","28.40"
        "fgets() + custom-strtol(), 32-bit","sort, match & print","10.065","1.159","28.38"
        "","","","",""
        "james_pic's solution","sort, match & print","9.764","1.113","28.21"
        


แทนที่จะทำให้คุณเบื่อด้วยตัวแยกวิเคราะห์ FSM อีกตัวโซลูชันด้านล่างใช้ fgets () และ strtol () แทน local () [มองหา s2i ()]

การใช้งานอ้างอิงใน Ruby:

#!/usr/bin/ruby2.0
# only tested against ruby v1.9 & v2.0
=begin
Filter a file based on these rules:
Input:
  - each line is a set of three integers
  - each line is formatted as <number> <w> <number> <w> <number>
    - <w> is whitespace (a single blank in the challenge)
    - <number> is an integer in the range 1..50_000_000
Output a given tuple ( A B T ) if:
  - there exists a tuple ( C D U ) 0 <= T - U < 100 and D == A
    OR
  - there exists a tuple ( C D U ) 0 <= U - T < 100 and B == C

Typical use:
  filter.rb test.input | sort | uniq > test.output
=end
list = Array.new
lookupB = Hash.new { |hash, key| hash[key] = Array.new }
ARGF.each_with_index do |line, index|
  abt = line.split.map { |s| s.to_i }
  list << abt
  lookupB[abt[1]] << index
end
for abt in list do
  for i in Array( lookupB[abt[0]] ) do
    delta = abt[2] - list[i][2]     # T - U
    if (0<=delta) && (delta<100)
      puts "#{abt.join(' ')}"
      puts "#{list[i].join(' ')}"
    end
  end
end

มันเป็นสุนัขที่ช้ากว่าสารละลายซีถึง 50 เท่า แต่ Perl นั้นช้าและกระชับน้อยกว่า

การแก้ปัญหา C:


#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
//      Throw caution, and error checking, to the winds.
// #include <assert.h>

#define RANGEMIN        (1)
#define RANGEMAX        (50*1000*1000)
#define SEARCHBOUNDARY  (100)
typedef struct {
    int             A;
    int             B;
    int             T;
} tuple_t;
typedef struct bin {
    tuple_t         slot;
    struct bin     *next;       // NULL=>0 items, self=>1 item, other=>overflow
} bin_t;
#define LISTSIZE        (RANGEMAX)
tuple_t         list[LISTSIZE];
#define HASH(x)         (x-1)
#define LOOKUPSIZE      (LISTSIZE)
bin_t           lookup[LOOKUPSIZE];
bin_t           overflow[LISTSIZE];
int             overflowNext = 0;

// based on strtol()
static inline int s2i(char *s, char **r)
{
    char            c;
    int             l = 0;

    do {
        c = *s++;
    } while (!isdigit(c));
    do {
        l = l * 10 + (c - '0');
        c = *s++;
    } while (isdigit(c));
    *r = s - 1;
    return l;
}

static inline void lookupInsert(tuple_t x)
{
    bin_t          *p = lookup + HASH(x.B);

    if (p->next) {
        overflow[overflowNext].slot = x;
        overflow[overflowNext].next = (p->next == p) ? p : p->next;
        p->next = overflow + overflowNext;
        overflowNext++;
    } else {
        p->slot = x;
        p->next = p;
    }
}

static void printOverflow(bin_t * head, bin_t * tail)
{
    if (head->next != tail) {
        printOverflow(head->next, tail);
    }
    printf("%d %d %d\n", head->slot.A, head->slot.B, head->slot.T);
}

static inline void dumpLookupSortedOnB()
{
    bin_t          *p;

    for (p = lookup; p < (lookup + LOOKUPSIZE); p++) {
        if (p->next) {
            printf("%d %d %d\n", p->slot.A, p->slot.B, p->slot.T);
            if (p != p->next) {
                printOverflow(p->next, p);
            }
        }
    }
}

static inline void printIfMatch(tuple_t abt, tuple_t cdu)
{
    int             A, B, T;
    int             C, D, U;

    A = abt.A;
    D = cdu.B;
    if (D == A) {
        T = abt.T;
        U = cdu.T;
        if ((0 <= (T - U)) && ((T - U) < SEARCHBOUNDARY)) {
            B = abt.B;
            C = cdu.A;
            printf("%d %d %d\n", A, B, T);
            printf("%d %d %d\n", C, D, U);
        }
    }
}

static inline void printMatches(int n)
{
    tuple_t        *p;

    for (p = list; p < (list + n); p++) {
        bin_t          *b = lookup + HASH(p->A);

        if (b->next) {
            bin_t          *q;

            printIfMatch(*p, b->slot);
            for (q = b->next; q != b; q = q->next) {
                printIfMatch(*p, q->slot);
            }
        }
    }
}

static inline void overflowTattle(int n)
{
    fprintf(stderr, "%d/%d items in overflow\n", overflowNext, n);
}

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

    // initialize lookup[]
    {
        bin_t          *p = lookup;

        for (n = 0; n < LOOKUPSIZE; n++) {
            p->next = NULL;
            p++;
        }
    }
    // read all tuples into list[] and insert into lookup[] & overflow[]
    {
        char            line[64];
        char           *lp;
        tuple_t        *p = list;

        for (n = 0; fgets(line, sizeof(line), stdin); n++) {
            p->A = s2i(line, &lp);
            p->B = s2i(lp, &lp);
            p->T = s2i(lp, &lp);
            lookupInsert(*p);
            p++;
        }
    }
    printMatches(n);
    exit(0);
}

คอมไพล์ด้วย "gcc -O3 -std = c99 -Wall -m64"

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