এত দ্রুত ফাইলের জন্য `হ্যাঁ` কীভাবে লিখবেন?


58

আমাকে একটি উদাহরণ দিতে দাও:

$ timeout 1 yes "GNU" > file1
$ wc -l file1
11504640 file1

$ for ((sec0=`date +%S`;sec<=$(($sec0+5));sec=`date +%S`)); do echo "GNU" >> file2; done
$ wc -l file2
1953 file2

এখানে আপনি দেখতে পাচ্ছেন যে কমান্ডটি এক সেকেন্ডে লাইন yesলিখছে 11504640যখন আমি 1953বাশ forএবং এবং ব্যবহার করে 5 সেকেন্ডের মধ্যে কেবল লাইন লিখতে পারি echo

মন্তব্যে যেমন পরামর্শ দেওয়া হয়েছে, এটিকে আরও দক্ষ করে তোলার জন্য বিভিন্ন কৌশল রয়েছে তবে কোনওটি এর গতির সাথে মিলে যায় না yes:

$ ( while :; do echo "GNU" >> file3; done) & pid=$! ; sleep 1 ; kill $pid
[1] 3054
$ wc -l file3
19596 file3

$ timeout 1 bash -c 'while true; do echo "GNU" >> file4; done'
$ wc -l file4
18912 file4

এগুলি এক সেকেন্ডে 20 হাজার লাইন লিখতে পারে। এবং এগুলিতে আরও উন্নতি করা যেতে পারে:

$ timeout 1 bash -c 'while true; do echo "GNU"; done >> file5' 
$ wc -l file5
34517 file5

$ ( while :; do echo "GNU"; done >> file6 ) & pid=$! ; sleep 1 ; kill $pid
[1] 5690
$ wc -l file6
40961 file6

এগুলি আমাদের এক সেকেন্ডে ৪০ হাজার লাইনে পৌঁছে দেয়। আরও ভাল, তবে এখনও একটি কান্না yesযা থেকে এক সেকেন্ডে প্রায় 11 মিলিয়ন লাইন লিখতে পারে!

সুতরাং, কিভাবে yesএত দ্রুত ফাইল লিখতে হয়?



9
দ্বিতীয় উদাহরণে, লুপের প্রতিটি পুনরাবৃত্তির জন্য আপনার কাছে দুটি বাহ্যিক কমান্ড অনুরোধ রয়েছে এবং dateএটি কিছুটা ভারী ওজনযুক্ত, এবং শেলের echoপ্রতিটি লুপ পুনরাবৃত্তির জন্য আউটপুট প্রবাহটি পুনরায় খুলতে হবে । প্রথম উদাহরণে, কেবলমাত্র একক আউটপুট পুনর্নির্দেশের সাথে একটি কমান্ডের অনুরোধ রয়েছে এবং কমান্ডটি অত্যন্ত লাইটওয়েট। দুটি কোনওভাবেই তুলনামূলক নয়।
একটি সিভিএন

@ মাইকেলKjörling আপনি ঠিকই dateভারী ওজন হতে পারে, আমার প্রশ্নের সম্পাদনা দেখুন।
পান্ড্য 15

1
timeout 1 $(while true; do echo "GNU">>file2; done;)ব্যবহার করতে ভুল পদ্ধতিতে timeout যেহেতু timeoutকমান্ড শুধুমাত্র একবার কমান্ড প্রতিকল্পন সমাপ্ত হয় শুরু হবে। ব্যবহার timeout 1 sh -c 'while true; do echo "GNU">>file2; done'
মুরু

1
উত্তরের সংক্ষিপ্তসার: কেবলমাত্র সিপিইউ সময় ব্যয় করে write(2)সিস্টেম কলগুলিতে নয়, অন্যান্য সিস্টোলের নৌকা বোঝা, শেল ওভারহেড, এমনকি আপনার প্রথম উদাহরণে তৈরির প্রক্রিয়া করা (যা dateফাইলটিতে মুদ্রিত প্রতিটি লাইনের জন্য অপেক্ষা করে এবং অপেক্ষা করে)। প্রচুর র‌্যাম সহ একটি আধুনিক সিস্টেমে ডিস্ক I / O (সিপিইউ / মেমরির চেয়ে বরং) রচনার এক সেকেন্ড সবেই যথেষ্ট। যদি বেশি দিন চলার অনুমতি দেওয়া হয় তবে পার্থক্যটি আরও কম হবে। (আপনি যে কোনও বাশ প্রয়োগ করছেন তা কতটা খারাপ, এবং সিপিইউ এবং ডিস্কের আপেক্ষিক গতির উপর নির্ভর করে আপনি ব্যাশের সাথে ডিস্ক আই / ও পর্যন্ত পরিপূর্ণ করতে পারেন না)।
পিটার কর্ডেস

উত্তর:


65

সংক্ষেপে:

yesঅন্যান্য অধিকাংশ মান ইউটিলিটি যা সাধারণত অনুরূপ আচরণ প্রদর্শণ লিখতে একটি থেকে ফাইল STREAM আউটপুট মাধ্যমে libC দ্বারা বাফার stdio । এগুলি কেবল write()প্রতিটি 4kb (16kb বা 64kb) বা আউটপুট ব্লক BUFSIZ যাই হোক না কেন সিস্কালটি করেechoএকটি হল write()প্রতি GNU। এটা একটা ব্যাপার অনেক এর মোড-সুইচিং (যা নয়, দৃশ্যতঃ হিসেবে ব্যয়বহুল হিসাবে প্রসঙ্গ-সুইচ )

এবং এটি মোটেও উল্লেখ করার মতো নয় যে এটির প্রাথমিক অপ্টিমাইজেশন লুপটি ছাড়াও yesএকটি খুব সাধারণ, ক্ষুদ্র, সংকলিত সি লুপ এবং আপনার শেল লুপ কোনওভাবেই সংকলক অপ্টিমাইজড প্রোগ্রামের সাথে তুলনীয় নয়।


কিন্তু আমি ভুল ছিলাম:

আমি যখন yesএটির আগে ব্যবহৃত স্টডিও বলেছিলাম, তখন আমি কেবল ধরে নিয়েছিলাম এটি এটি করেছে কারণ এটি এর মতো অনেকগুলি আচরণ করে। এটি সঠিক ছিল না - এটি কেবল এইভাবে তাদের আচরণ অনুকরণ করে। এটি আসলে যা করে তা হ'ল আমি নীচে শেলটি দিয়ে যা করেছি তার সাথে একটি এনালগের মতো: এটি প্রথমে তার তর্কগুলি (বা yযদি না হয়) ঝাঁকুনি দেয় যতক্ষণ না তারা বাড়িয়ে না ফেলে আরও বাড়তে পারে না BUFSIZ

উত্স থেকে তাত্ক্ষণিক প্রাসঙ্গিক forলুপের পূর্বে একটি মন্তব্য বলে:

/* Buffer data locally once, rather than having the
large overhead of stdio buffering each item.  */

yeswrite()এর পরে এটি তার নিজস্ব কাজ করে।


অবান্তরতা:

(যেমনটি মূলত প্রশ্নটিতে অন্তর্ভুক্ত রয়েছে এবং ইতিমধ্যে এখানে লিখিত সম্ভাব্য তথ্যমূলক ব্যাখ্যার প্রসঙ্গটি ধরে রেখেছি) :

আমি চেষ্টা করেছি timeout 1 $(while true; do echo "GNU">>file2; done;)কিন্তু লুপ থামাতে পারিনি।

timeoutকমান্ড প্রতিস্থাপনের সাথে আপনার যে সমস্যাটি রয়েছে - আমি মনে করি এটি এখনই পেয়েছি এবং এটি কেন থামছে না তা ব্যাখ্যা করতে পারি। timeoutশুরু হয় না কারণ এর কমান্ড-লাইনটি কখনও চালিত হয় না। আপনার শেল একটি শিশু শেল কাঁটাচামচ করে, তার স্টাডআউটে একটি পাইপ খুলবে এবং এটি পড়বে। এটি পড়া বন্ধ করবে যখন শিশুটি প্রস্থান করবে এবং তারপরে এটি $IFSম্যাংলিং এবং গ্লোব বিস্তারের জন্য সমস্ত শিশু লিখেছেন এবং এর ফলাফলের $(সাথে এটি মিল থেকে শুরু করে সবকিছুকে প্রতিস্থাপন করবে )

তবে যদি শিশুটি একটি অন্তহীন লুপ হয় যা পাইপে কখনও লেখা হয় না, তবে শিশুটি কখনই লুপিং বন্ধ করে না, এবং timeoutকমান্ড-লাইনটি কখনই সম্পন্ন হয় না (যেমন আমার ধারণা) আপনি CTRL-Cসন্তানের লুপটি মেরে ফেলেন । সুতরাং কখনই লুপটি মারা timeoutযাবে না যা এটি শুরু হওয়ার আগেই শেষ করা দরকার।


অন্যান্য timeoutগুলি:

... কেবল আপনার পারফরম্যান্স সমস্যার সাথে প্রাসঙ্গিক নয় যতটা আপনার শেল প্রোগ্রামটি আউটপুট পরিচালনা করতে ব্যবহারকারী এবং কার্নেল-মোডের মধ্যে স্যুইচিংয়ে ব্যয় করতে পারে। timeoutযদিও, শেল যেমন এই উদ্দেশ্যে হতে পারে ততটা নমনীয় নয়: যেখানে শেলস এক্সেল তাদের তর্কগুলি যুক্ত করতে এবং অন্যান্য প্রক্রিয়াগুলি পরিচালনা করতে সক্ষম হয়।

অন্য কোথাও উল্লেখ করা আছে যে কেবলমাত্র [fd-num] >> named_fileপুনর্নির্দেশটি লুপের আউটপুট লক্ষ্যটিতে সরিয়ে নিয়ে যাওয়া ছাড়া কেবল লুপ করা কমান্ডের জন্য কেবলমাত্র আউটপুট নির্দেশ না করে কার্যকারিতা উন্নত করতে পারে কারণ সেই পথে কমপক্ষে open()সিস্কলটি কেবল একবার করা দরকার। এটি নীচের |অভ্যন্তরের লুপগুলির আউটপুট হিসাবে লক্ষ্যযুক্ত পাইপ দিয়েও করা হয় ।


সরাসরি তুলনা:

আপনি পছন্দ করতে পারেন:

for cmd in  exec\ yes 'while echo y; do :; done'
do      set +m
        sh  -c '{ sleep 1; kill "$$"; }&'"$cmd" | wc -l
        set -m
done

256659456
505401

কোনটি ধরনের নির্দেশে সাব সামনে বর্ণনা সম্পর্ক মতো কিন্তু কোন নল এবং যতক্ষণ না পিতা বা মাতা নিহত শিশু পশ্চাদপটে করা হয়। ইন yesকেস পিতা বা মাতা আসলে প্রতিস্থাপিত হয়েছে যেহেতু সন্তান উত্পন্ন হওয়া হয়, কিন্তু শেল কল yesনতুন এক সঙ্গে নিজস্ব প্রক্রিয়া আটকানোর মাধ্যেমে তাই PID, অবশেষ একই এবং তার বোকচন্দর শিশু এখনও যারা সব পরে হত্যা করার জানেন।


বড় বাফার:

এখন শেলের write()বাফার বাড়ানোর বিষয়ে দেখতে দিন ।

IFS="
";    set y ""              ### sets up the macro expansion       
until [ "${512+1}" ]        ### gather at least 512 args
do    set "$@$@";done       ### exponentially expands "$@"
printf %s "$*"| wc -c       ### 1 write of 512 concatenated "y\n"'s  

1024

আমি এই সংখ্যাটি বেছে নিয়েছি কারণ আউটপুট স্ট্রিংগুলি 1kb এর চেয়ে বেশি দীর্ঘতর ছিল write()আমার জন্য আলাদা ' এবং তাই এখানে আবার লুপ:

for cmd in 'exec  yes' \
           'until [ "${512+:}" ]; do set "$@$@"; done
            while printf %s "$*"; do :; done'
do      set +m
        sh  -c $'IFS="\n"; { sleep 1; kill "$$"; }&'"$cmd" shyes y ""| wc -l
        set -m
done

268627968
15850496

শেষের চেয়ে এই পরীক্ষার জন্য একই পরিমাণে শেলের দ্বারা লিখিত ডেটা পরিমাণের তুলনায় এটি 300 গুণ। খুব বাজে না. তবে তা নয় yes


সম্পর্কিত:

অনুরোধ হিসাবে, এই লিঙ্কে এখানে কী করা হয়েছে সম্পর্কে নিছক কোড মন্তব্যগুলির চেয়ে আরও বিশদ বিবরণ রয়েছে ।


@হিমাইল - হয়ত? আমি পুরোপুরি নিশ্চিত নই যে আপনি যা জিজ্ঞাসা করছেন তা আমি বুঝতে পারি? আউটপুট লেখার জন্য যখন কোনও প্রোগ্রাম স্টডিও ব্যবহার করে তবে তা কোনও বাফারিং (ডিফল্টরূপে স্ট্ডারের মতো) বা লাইন বাফারিং (ডিফল্টরূপে টার্মিনালগুলিতে) বা ব্লক বাফারিংয়ের সাথে হয় না (মূলত বেশিরভাগ অন্যান্য জিনিস ডিফল্টভাবে এভাবে সেট করা থাকে) । আউটপুট বাফারের আকারটি কী সেট করে সে সম্পর্কে আমি কিছুটা অস্পষ্ট but তবে এটি সাধারণত 4 কেবি এর কিছু অংশ। এবং সুতরাং stdio lib ফাংশনগুলি তাদের পুরো আটকানো না পাওয়া পর্যন্ত তাদের আউটপুট সংগ্রহ করবে। ddএকটি মানক সরঞ্জাম যা অবশ্যই স্টিডিও ব্যবহার করে না, উদাহরণস্বরূপ। বেশিরভাগ অন্যরা করেন।
মাইক্রজারভ

3
শেল সংস্করণটি চলছে open(বিদ্যমান) writeএবং close(যা আমি বিশ্বাস করি যে এখনও ফ্লাশের জন্য অপেক্ষা করা হয়), এবং dateপ্রতিটি লুপের জন্য একটি নতুন প্রক্রিয়া তৈরি এবং কার্যকর করা হবে ।
dave_thompson_085

@ dave_thompson_085 - / dev / chat এ যান । এবং আপনি যা বলছেন তা অগত্যা সত্য নয়, কারণ আপনি সেখানে দেখতে পারেন। উদাহরণস্বরূপ, আমার wc -lসাথে সেই লুপটি করা bashআউটপুটটির 1/5 তম পায় shলুপটি করে - bash100k writes()থেকে dash500k এর কিছুটা বেশি পরিচালনা করে ।
মাইক্রজারভ

দুঃখিত আমি অস্পষ্ট ছিলাম; আমি প্রশ্নটির শেল সংস্করণ বোঝাতে চাইছিলাম, যা আমি পড়ার for((sec0=`date +%S`;...সময়টিতে সময় এবং লুপটিতে পুনর্নির্দেশ নিয়ন্ত্রণ করার সাথে কেবলমাত্র মূল সংস্করণ ছিল , পরবর্তী উন্নতি নয়।
dave_thompson_085

@ ডেভ_থমপসন_085 - এটি ঠিক আছে। কিছু মৌলিক বিষয় সম্পর্কে উত্তরটি যাইহোক ভুল ছিল, এবং আশা করি এখন বেশ সঠিক হওয়া উচিত l
মাইক্রজারভ

20

আরও ভাল প্রশ্ন হবে আপনার শেলটি এত ধীরে ধীরে ফাইলটি কেন লিখছে? যে কোনও স্ব-অন্তর্ভুক্ত সংকলিত প্রোগ্রাম যা ফাইল রাইটিং সিস্কলগুলি দায়বদ্ধতার সাথে ব্যবহার করে (এক সাথে প্রতিটি চরিত্রকে ফ্লাশ করে না) এটি যুক্তিসঙ্গত দ্রুতই করত। আপনি যা করছেন তা হ'ল ব্যাখ্যামূলক ভাষায় (শেল) লাইন লিখছেন এবং এর সাথে আপনি প্রচুর অপ্রয়োজনীয় ইনপুট আউটপুট ক্রিয়াকলাপ করেন। কি yesকরে:

  • লেখার জন্য একটি ফাইল খোলে
  • একটি স্ট্রিমে লেখার জন্য অনুকূলিত এবং সংকলিত ফাংশনগুলি কল করে
  • স্ট্রিমটি বাফার হয়ে গেছে, সুতরাং একটি সিস্টাল (কার্নেল মোডে ব্যয়বহুল সুইচ) খুব কমই ঘটে, বড় অংশগুলিতে
  • একটি ফাইল বন্ধ করে দেয়

আপনার লিপিটি কী করে:

  • কোডের একটি লাইনে পড়ে
  • কোডটি ব্যাখ্যা করে, আপনার ইনপুটটি পার্স করার জন্য অনেক অতিরিক্ত ক্রিয়াকলাপ তৈরি করে এবং কী করণীয় তা নির্ধারণ করে
  • লুপের প্রতিটি পুনরাবৃত্তির জন্য (যা সম্ভবত কোনও বর্ণিত ভাষায় সস্তা নয়):
    • dateবাহ্যিক কমান্ড কল করুন এবং তার আউটপুট সংরক্ষণ করুন (শুধুমাত্র মূল সংস্করণে - সংশোধিত সংস্করণে আপনি এটি না করে 10 এর ফ্যাক্টর অর্জন করেন)
    • লুপের সমাপ্তির শর্তটি পূরণ হয়েছে কিনা তা পরীক্ষা করুন
    • সংযোজন মোডে একটি ফাইল খুলুন
    • echoকমান্ডটি বিশ্লেষণ করুন , শেল বিল্টিন হিসাবে এটি (কিছু প্যাটার্ন মেলানো কোড সহ) সনাক্ত করুন, "GNU" আর্গুমেন্টে প্যারামিটার সম্প্রসারণ এবং সমস্ত কিছু কল করুন এবং অবশেষে খোলার ফাইলটিতে লাইনটি লিখুন
    • ফাইলটি আবার বন্ধ করুন
    • প্রক্রিয়া পুনরাবৃত্তি

ব্যয়বহুল অংশ: পুরো ব্যাখ্যাটি অত্যন্ত ব্যয়বহুল (ব্যাশ সমস্ত ইনপুট প্রিপ্রোসেসিংয়ের ভয়ঙ্কর কাজ করছে - আপনার স্ট্রিংয়ে সম্ভাব্য পরিবর্তনশীল প্রতিস্থাপন, প্রক্রিয়া প্রতিস্থাপন, ব্রেস সম্প্রসারণ, পালানোর অক্ষর এবং আরও অনেক কিছু থাকতে পারে), বিল্টিনের প্রতিটি কল রয়েছে সম্ভবত একটি ফাংশন পুনর্নির্দেশ সহ একটি স্যুইচ স্টেটমেন্ট যা বিল্টিনের সাথে কাজ করে এবং খুব গুরুত্বপূর্ণ, আপনি আউটপুট প্রতিটি লাইন জন্য একটি ফাইল খুলুন এবং বন্ধ। আপনি করা হতে পারে >> fileযখন লুপ বাইরে এটি না করা অনেক দ্রুততর , কিন্তু আপনি এখনও একটি ব্যাখ্যা ভাষা আছেন। আপনি যে বেশ ভাগ্যবানechoএকটি শেল অন্তর্নির্মিত, বাহ্যিক কমান্ড নয় - অন্যথায়, আপনার লুপটিতে প্রতিটি একক পুনরাবৃত্তিতে একটি নতুন প্রক্রিয়া (কাঁটাচামচ এবং এক্সিকিউট) তৈরি করা জড়িত। যা প্রক্রিয়াটিকে থামতে দেবে - আপনি যখন দেখেন যে লুপটিতে dateকমান্ডটি ছিল তখন এটি কত ব্যয়বহুল ।


11

অন্যান্য উত্তরগুলি মূল বিষয়গুলিকে সম্বোধন করেছে। পার্শ্ব নোটে, আপনি গণনার শেষে আউটপুট ফাইলে লিখে আপনার উইল লুপের থ্রুপুট বাড়িয়ে নিতে পারেন। তুলনা করা:

$ i=0;time while  [ $i -le 1000 ]; do ((++i)); echo "GNU" >>/tmp/f; done;

real    0m0.080s
user    0m0.032s
sys     0m0.037s

সঙ্গে

$ i=0;time while  [ $i -le 1000 ]; do ((++i)); echo "GNU"; done>>/tmp/f;

real    0m0.030s
user    0m0.019s
sys     0m0.011s

হ্যাঁ, এটি গুরুত্বপূর্ণ এবং লেখার গতি (কমপক্ষে) আমার ক্ষেত্রে দ্বিগুণ হয়
পান্ড্যা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.