วิธีที่เร็วที่สุดในการเชื่อมไฟล์เข้าด้วยกัน


25

ฉันมีไฟล์ 10k + รวมกว่า 20GB ที่ฉันต้องต่อกันเป็นไฟล์เดียว

มีวิธีที่เร็วกว่า

cat input_file* >> out

?

วิธีที่ต้องการจะเป็นคำสั่ง bash, Python เป็นที่ยอมรับเช่นกันหากไม่ช้ากว่านี้มาก


อัปเดตคำตอบของฉันfindไม่เรียงลำดับไฟล์เหมือน shell glob
แกรม

5
โซลูชันใด ๆ และทั้งหมดจะมีความเร็วเท่ากับที่นี่เนื่องจากเวลาจะเป็นระบบ I / O 99%
goldilocks


3
พิจารณาการเขียนไฟล์ที่ตัดแบ่งในดิสก์อื่นที่ไม่ใช่ไฟล์ที่คุณกำลังอ่าน
Luis

1
มันจะเร็วขึ้นหากoutอยู่บนดิสก์อื่น

คำตอบ:


30

ไม่ใช่แมวเป็นวิธีที่ดีที่สุดในการทำเช่นนี้ ทำไมต้องใช้ python เมื่อมีโปรแกรมที่เขียนไว้ใน C เพื่อจุดประสงค์นี้? แต่คุณ แต่อาจต้องการพิจารณาใช้xargsในกรณีที่มีความยาวบรรทัดคำสั่งเกินและคุณต้องมากกว่าหนึ่งARG_MAX catการใช้เครื่องมือ GNU นี้เทียบเท่ากับสิ่งที่คุณมีอยู่แล้ว:

find . -maxdepth 1 -type f -name 'input_file*' -print0 |
  sort -z |
  xargs -0 cat -- >>out

1
คุณสามารถประกันในกรณีนี้ว่าไฟล์ของคุณจะถูกอ่านตามลำดับหรือไม่?
Kiwy

1
ใช่เพราะการส่งออกของประปาผ่านfind sortหากไม่มีสิ่งนี้ไฟล์จะแสดงรายการตามลำดับโดยพลการ (กำหนดโดยระบบไฟล์ซึ่งอาจเป็นลำดับการสร้างไฟล์)
scai

@scai ฉันคิดถึงคำขอโทษด้วยการเรียงลำดับมันค่อนข้างชัดเจน
Kiwy

1
@ กีวี, กรณีเดียวที่ฉันเห็นคือถ้าสถานที่ตั้งไม่ถูกต้องในสภาพแวดล้อมจากนั้นเรียงลำดับอาจทำงานแตกต่างจากbashกลม มิฉะนั้นฉันไม่เห็นกรณีใด ๆ ที่xargsหรือcatจะไม่ทำงานตามที่คาดไว้
แกรม

3
@MarcvanLeeuwen xargsจะโทรหาเท่าcatที่จำเป็นเพื่อหลีกเลี่ยงข้อผิดพลาด E2BIG ของ execve (2)
Stéphane Chazelas

21

การจัดสรรพื้นที่สำหรับไฟล์เอาต์พุตก่อนอาจปรับปรุงความเร็วโดยรวมเนื่องจากระบบไม่จำเป็นต้องอัพเดตการจัดสรรสำหรับการเขียนทุกครั้ง

ตัวอย่างเช่นหากบน Linux:

size=$({ find . -maxdepth 1 -type f -name 'input_file*' -printf '%s+'; echo 0;} | bc)
fallocate -l "$size" out &&
  find . -maxdepth 1 -type f -name 'input_file*' -print0 |
  sort -z | xargs -r0 cat 1<> out

ข้อดีอีกอย่างคือถ้ามีพื้นที่ว่างไม่เพียงพอจะไม่พยายามคัดลอก

หากเปิดbtrfsคุณสามารถcopy --reflink=alwaysเปิดไฟล์แรกได้ (ซึ่งไม่มีนัยในการคัดลอกข้อมูลและจะทำให้เกือบจะทันที) และผนวกส่วนที่เหลือ หากมี 10,000 ไฟล์นั่นอาจจะไม่สร้างความแตกต่างมากนักเว้นแต่ว่าไฟล์แรกจะมีขนาดใหญ่มาก

มี API เพื่อสรุปว่าการคัดลอกไฟล์ทั้งหมด ( BTRFS_IOC_CLONE_RANGE ioctl) แต่ฉันไม่พบยูทิลิตีใด ๆ ที่เปิดเผย API นั้นดังนั้นคุณต้องทำใน C (หรือpythonภาษาอื่น ๆ ที่พวกเขาสามารถเรียกได้เองioctl) .

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

find . -maxdepth 1 -type f -name 'input_file*' -print0 |
  sort -z | xargs -r0 cat | cp --sparse=always /dev/stdin out

1
@XTian ไม่มีมันควรจะเป็นค่า>มิได้>>แต่1<>ที่ผมกล่าวว่าจะเขียนลงในแฟ้ม
Stéphane Chazelas

5
@grebneke <>เป็นตัวดำเนินการเปลี่ยนเส้นทางการอ่าน + เขียนแบบ Bourne / POSIX มาตรฐาน ดูคู่มือเชลล์ของคุณหรือข้อมูลจำเพาะ POSIXสำหรับรายละเอียด เริ่มต้นfdเป็น0สำหรับ<>ผู้ประกอบการ ( <>สั้นสำหรับ0<>เช่น<สั้นสำหรับ0<และ>สั้น1>) ดังนั้นคุณจำเป็นต้องมี1การเปลี่ยนเส้นทาง stdout อย่างชัดเจน ที่นี่มีไม่มากที่เราต้องการอ่าน + เขียน ( O_RDWR) แต่เราไม่ต้องการO_TRUNC(เหมือนใน>) ซึ่งจะยกเลิกการจัดสรรสิ่งที่เราเพิ่งจัดสรร
Stéphane Chazelas

1
@grebneke, unix.stackexchange.com/search?q=user%3A22565+%22%3C%3E%22จะให้คุณไม่กี่คน ksh93 หาตัวดำเนินการ BTW และคุณสามารถค้นหาด้วยddหรือผ่านการอ่าน
Stéphane Chazelas

1
@StephaneChazelas - ขอบคุณมากความช่วยเหลือและความรู้ของคุณได้รับการชื่นชมอย่างลึกซึ้ง!
grebneke

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