আমার স্ক্রিপ্টে কিছু ভুল আছে বা বাশ পাইথনের চেয়ে অনেক ধীর গতির?


29

আমি 1 বিলিয়ন বার লুপ চালিয়ে বাশ এবং পাইথনের গতি পরীক্ষা করছিলাম।

$ cat python.py
#!/bin/python
# python v3.5
i=0;
while i<=1000000000:
    i=i+1;

বাশ কোড:

$ cat bash2.sh
#!/bin/bash
# bash v4.3
i=0
while [[ $i -le 1000000000 ]]
do
let i++
done

timeকমান্ডটি ব্যবহার করে আমি জানতে পেরেছিলাম যে পাইথন কোডটি শেষ করতে মাত্র 48 সেকেন্ড সময় লাগে যখন ব্যাশ কোডটি স্ক্রিপ্টটি মারা যাওয়ার 1 ঘন্টা আগে গ্রহণ করেছিল।

কেন এমন হয়? আমি আশা করি বাশ দ্রুত হবে। আমার স্ক্রিপ্টে কিছু ভুল আছে বা বাশ আসলে এই স্ক্রিপ্টটির সাথে অনেক ধীর গতিতে আছে?


49
আপনি কেন বাশ পাইথনের চেয়ে দ্রুততর হবে বলে আশাবাদী তা আমি নিশ্চিত নই ।
কুসালানন্দ

9
@ মাটিজা নালিস না আপনি পারবেন না! স্ক্রিপ্টটি মেমোরিতে লোড করা হয়, পাঠ্য ফাইলটি সম্পাদনা করা (স্ক্রিপ্ট ফাইল) থেকে এটি পড়ার স্ক্রিপ্টে একেবারে কোনও প্রভাব ফেলবে না। খুব ভাল একটি জিনিস, প্রতিবার লুপটি চালানোর সময় কোনও ফাইল খোলার এবং পুনরায় পড়া না করে বাশ ইতিমধ্যে যথেষ্ট ধীর হয়ে যায়!
টেরডন


4
বাশ ফাইলটি লাইন বাই লাইন পড়ার সাথে সাথে পড়তে পারে তবে এটি যদি সেই লাইনে আবার আসে তবে এটি কী পড়ে তা মনে রাখে (কারণ এটি কোনও লুপে বা কোনও ফাংশনে রয়েছে)। প্রতিটি পুনরাবৃত্তি পুনরায় পড়ার মূল দাবীটি সত্য নয়, তবে এখনও পৌঁছানো লাইনে পরিবর্তন কার্যকর হবে। একটি আকর্ষণীয় বিক্ষোভ: একটি ফাইল রয়েছে echo echo hello >> $0এবং এটি চালান run
মাইকেল হোমার

3
@ মাটিজা নালিস আহ, ঠিক আছে, আমি এটি বুঝতে পারি। এটি আমাকে ছুঁড়ে ফেলেছে এমন একটি চলমান লুপ পরিবর্তন করার ধারণা ছিল। সম্ভবত, প্রতিটি লাইন ধারাবাহিকভাবে এবং শুধুমাত্র শেষেরটি শেষ হওয়ার পরে পঠিত হয়। তবে, একটি লুপকে একটি একক কমান্ড হিসাবে বিবেচনা করা হয় এবং এটি সম্পূর্ণরূপে পড়তে হবে, সুতরাং এটি পরিবর্তন করা চলমান প্রক্রিয়াটিকে প্রভাবিত করবে না। আকর্ষণীয় পার্থক্য যদিও, আমি সবসময় ধরে নিয়েছিলাম যে পুরো স্ক্রিপ্টটি মৃত্যুদন্ড কার্যকর করার আগে স্মৃতিতে লোড করা হয়। এটা ইশারা জন্য ধন্যবাদ!
টেরডন

উত্তর:


17

এটি বাশের একটি বিখ্যাত বাগ; ম্যান পৃষ্ঠাটি দেখুন এবং "বাগস" অনুসন্ধান করুন:

BUGS
       It's too big and too slow.

;)


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

সর্বাধিক প্রাসঙ্গিক অংশ:

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

...

আইডাব্লু, শেলগুলিতে, বিশেষত পাঠ্য প্রক্রিয়া করার জন্য, আপনি যথাসম্ভব কয়েকটি ইউটিলিটিটি আহ্বান করুন এবং তাদের কার্যটিতে সহযোগিতা করতে বলুন, পরেরটি চালানোর আগে প্রতিটি একসাথে শুরু করার জন্য, চালানোর জন্য, পরিষ্কার করার জন্য কয়েক হাজার সরঞ্জাম ক্রমানুসারে চালাবেন না।

...

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

এবং শেলগুলি সেভাবে চলার জন্য ডিজাইন করা হয়নি, তাদের পারফরম্যান্ট প্রোগ্রামিং ভাষা হওয়ার প্রবণতা নেই। তারা নয়, তারা কেবল কমান্ড লাইন ইন্টারপ্রেটার। সুতরাং, এই ফ্রন্টে সামান্য অপ্টিমাইজেশন করা হয়েছে।


শেল স্ক্রিপ্টিংয়ে বড় লুপ ব্যবহার করবেন না।


54

শেল লুপগুলি ধীর এবং বাশ হ'ল ধীর। শাঁসগুলি লুপগুলিতে ভারী কাজ করার উদ্দেশ্যে নয়। শেলগুলি বোঝানো হয় ডেটাগুলির ব্যাচে কয়েকটি বাহ্যিক, অনুকূলিত প্রক্রিয়াগুলি প্রবর্তন করতে।


যাইহোক, আমি কৌতূহল ছিল কিভাবে শেল লুপগুলি তুলনা করে তাই আমি একটি সামান্য বেঞ্চমার্ক তৈরি করেছি:

#!/bin/bash

export IT=$((10**6))

echo POSIX:
for sh in dash bash ksh zsh; do
    TIMEFORMAT="%RR %UU %SS $sh"
    time $sh -c 'i=0; while [ "$IT" -gt "$i" ]; do i=$((i+1)); done'
done


echo C-LIKE:
for sh in bash ksh zsh; do
    TIMEFORMAT="%RR %UU %SS $sh"
    time $sh -c 'for ((i=0;i<IT;i++)); do :; done'
done

G=$((10**9))
TIMEFORMAT="%RR %UU %SS 1000*C"
echo 'int main(){ int i,sum; for(i=0;i<IT;i++) sum+=i; printf("%d\n", sum); return 0; }' |
   gcc -include stdio.h -O3 -x c -DIT=$G - 
time ./a.out

( বিশদ:

  • সিপিইউ: ইনটেল (আর) কোর (টিএম) আই 5 সিপিইউ এম 430 @ 2.27GHz
  • ksh: সংস্করণ sh (এটিএন্ডটি গবেষণা) 93u + 2012-08-01
  • ব্যাশ: জিএনইউ ব্যাশ, সংস্করণ 4.3.11 (1) -রেলিজ (x86_64-pc-linux-gnu)
  • zsh: zsh 5.2 (x86_64- অজানা-লিনাক্স-গনু)
  • ড্যাশ: 0.5.7-4buntu1

)

(সংক্ষেপিত) ফলাফল (প্রতি পুনরাবৃত্তির সময়) হ'ল:

POSIX:
5.8 µs  dash
8.5 µs ksh
14.6 µs zsh
22.6 µs bash

C-LIKE:
2.7 µs ksh
5.8 µs zsh
11.7 µs bash

C:
0.4 ns C

ফলাফলগুলি থেকে:

আপনি যদি কিছুটা দ্রুত শেল লুপ চান তবে আপনার যদি [[সিনট্যাক্স থাকে এবং আপনি একটি দ্রুত শেল লুপ চান তবে আপনি একটি উন্নত শেল এবং আপনার লুপের জন্য সি-লাইকও রয়েছে। লুপের জন্য সি পছন্দ করুন, তারপরে। এগুলি while [একই শেলটিতে লুপের চেয়ে প্রায় 2 গুণ দ্রুত হতে পারে ।

  • ksh এর পুনরাবৃত্তির for (প্রায় 2.7µ দ্রুততম লুপ রয়েছে
  • ড্যাশ দ্রুততম হয়েছে while [সম্পর্কে এ লুপ 5.8μs পুনরাবৃত্তির প্রতি

লুপগুলির জন্য সি দ্রুতগতির 3-4 দশমিক অর্ডার হতে পারে। (শুনেছি টরভাল্ডস সি ভালবাসে)।

লুপের জন্য অনুকূলিত সি বাশের while [লুপের (ধীরতম শেল লুপ) চেয়ে 56500 গুণ বেশি এবং কেএসএসের for (লুপের (দ্রুততম শেল লুপ) এর চেয়ে 6750 গুণ বেশি দ্রুত ।


আবার শাঁসের স্বচ্ছলতা যদিও ততটা গুরুত্ব পাবে না, কারণ শাঁসগুলির সাথে আদর্শ প্যাটার্নটি হ'ল বাহ্যিক, অনুকূলিত প্রোগ্রামগুলির কয়েকটি প্রক্রিয়াতে অফলোড।

এই নিদর্শন দিয়ে, শেলগুলি প্রায়শই পাইথন স্ক্রিপ্টগুলির চেয়ে বেশি পারফরম্যান্স সহ স্ক্রিপ্টগুলি লেখার পক্ষে সহজ করে তোলে (গতবার আমি পরীক্ষা করে দেখলাম, পাইথনগুলিতে প্রক্রিয়া পাইপলাইন তৈরি করা বরং আনাড়ি ছিল))

আর একটি বিষয় বিবেচনার জন্য হ'ল শুরুর সময়।

time python3 -c ' '

আমার পিসিতে 30 থেকে 40 এমএস লাগে যদিও শেলগুলি প্রায় 3 এমএস লাগে। আপনি যদি প্রচুর স্ক্রিপ্ট চালু করেন তবে এটি দ্রুত যুক্ত হয়ে যায় এবং অজগরটি শুরু করতে যে অতিরিক্ত 27-37 এমএস লাগে তার মধ্যে আপনি খুব বেশি কিছু করতে পারেন। ছোট স্ক্রিপ্টগুলি সেই সময়ের ফ্রেমে বেশ কয়েকবার সমাপ্ত হতে পারে।

(নোডজেস সম্ভবত এই বিভাগের সবচেয়ে খারাপ স্ক্রিপ্টিং রানটাইম হিসাবে শুরু হতে প্রায় 100 মিমি সময় নেয় (যদিও এটি একবার শুরু হয়ে গেলেও, আপনাকে স্ক্রিপ্টিং ভাষার মধ্যে আরও ভাল পারফর্মার খুঁজে পেতে খুব চাপ দেওয়া হবে))।


Ksh জন্য, আপনি (যেমন AT & T বাস্তবায়ন উল্লেখ করতে পারেন ksh88, যেমন AT & T ksh93, pdksh, mkshসেখানে বেশ তাদের মধ্যে ভেরিয়েশন অনেক হিসাবে ...)। জন্য bash, আপনি সংস্করণ নির্দিষ্ট করতে চাইতে পারেন। এটি ইদানীং কিছু অগ্রগতি করেছে (এটি অন্যান্য শেলের ক্ষেত্রেও প্রযোজ্য)।
স্টাফেন চেজেলাস

@ স্টাফেন চ্যাজেলাস ধন্যবাদ আমি ব্যবহৃত সফ্টওয়্যার এবং হার্ডওয়্যারের সংস্করণগুলি যুক্ত করেছি।
পিএসকোকিক

রেফারেন্স জন্য: একটি প্রক্রিয়া পাইপলাইন পাইথন আপনি ভালো কিছু করতে হবে তৈরি করতে: from subprocess import *; p1=Popen(['echo', 'something'], stdout=PIPE); p2 = Popen(['grep', 'pattern'], stdin=p1.stdout, stdout=PIPE); Popen(['wc', '-c'], stdin=PIPE)। এটি প্রকৃতপক্ষে আনাড়ি, তবে কোনও pipelineফাংশন কোড করা কঠিন হওয়া উচিত নয় যা ফলস্বরূপ যে কোনও সংখ্যক প্রক্রিয়ার জন্য এটি করে pipeline(['echo', 'something'], ['grep', 'patter'], ['wc', '-c'])
বকুরিউ

1
আমি ভেবেছিলাম সম্ভবত জিসিসি অপ্টিমাইজারটি লুপটি পুরোপুরি সরিয়ে ফেলছে। এটা SIMD নির্দেশাবলী ব্যবহার 4 সমান্তরাল যোগ করতে হবে, 250000. থেকে লুপ পুনরাবৃত্তিও সংখ্যা হ্রাস: এটা নয়, কিন্তু এটি এখনও একটি আকর্ষণীয় অপ্টিমাইজেশান করছে
মার্ক Plotnick

1
@ এসপোসিক: এটি ২০১ optim সালে অপ্টিমাইজাররা কী করতে পারে তার ঠিক প্রান্তে C এটি দেখতে দেখতে সি ++ 17 এর মতো মনে হচ্ছে যে কম্পাইলাররা অবশ্যই সংকলন সময়ে অনুরূপ অভিব্যক্তি গণনা করতে সক্ষম হবে (এমনকি অপ্টিমাইজেশন হিসাবেও নয়)। সেই সি ++ সক্ষমতার জায়গায়, জিসিসি এটিকে সি এর জন্য একটি অপ্টিমাইজেশন হিসাবেও তুলতে পারে।
এমসাল্টার

18

আমি কিছুটা পরীক্ষা করেছি এবং আমার সিস্টেমে নিম্নলিখিতগুলি চালিয়েছি - প্রতিযোগিতামূলক হওয়ার জন্য প্রয়োজনীয় গতিরোধের গতি বাড়ানোর ক্রমটি কেউ তৈরি করেনি, তবে আপনি এটি আরও দ্রুত তৈরি করতে পারেন:

পরীক্ষা 1: 18.233s

#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do
    let i++
done

পরীক্ষা 2: 20.45 এস

#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do 
    i=$(($i+1))
done

পরীক্ষা 3: 17.64 এস

#!/bin/bash
i=0
while [[ $i -le 4000000 ]]; do let i++; done

পরীক্ষা 4: 26.69 এস

#!/bin/bash
i=0
while [ $i -le 4000000 ]; do let i++; done

পরীক্ষা 5: 12.79s

#!/bin/bash
export LC_ALL=C

for ((i=0; i != 4000000; i++)) { 
:
}

এই শেষটির গুরুত্বপূর্ণ অংশটি হ'ল রফতানি এলসি_এলএল = সি। আমি খুঁজে পেয়েছি যে অনেকগুলি ব্যাশ অপারেশন উল্লেখযোগ্যভাবে দ্রুত শেষ হয় যদি এটি ব্যবহৃত হয়, বিশেষত কোনও রেজেক্স ফাংশন। এটি সিনট্যাক্সের জন্য {} এবং: কোনও নন-অপ্ট হিসাবে ব্যবহার করার জন্য একটি অনিবন্ধিতও দেখায়।


3
LC_ALL পরামর্শের জন্য +1, আমি এটি জানতাম না।
আইনপোকলুম - মনিকা

+1 আকর্ষণীয় কীভাবে এর [[চেয়ে এত দ্রুত [। আমি এলসি_এলএল = সি জানি না (বিটিডাব্লু আপনার এটিকে রফতানি করার দরকার নেই) একটি পার্থক্য করেছে।
পিএসকোকিক

@ এসপোকিক যতদূর আমি জানি, [[এটি একটি বাশ অন্তর্নির্মিত, এবং [সত্যই /bin/[, যা এটির মতো /bin/test- একটি বহিরাগত প্রোগ্রাম। যে কারণে থাই এর ধীর।
টমসডিং

@ টমসেমেন্ডিং [সমস্ত সাধারণ শেলগুলিতে একটি অন্তর্নির্মিত (চেষ্টা করুন type [)। বাহ্যিক প্রোগ্রামটি বর্তমানে বেশিরভাগ অব্যবহৃত।
পিএসকোকিক

10

আপনি যদি এটির জন্য ডিজাইন করা হয়েছে তার জন্য যদি আপনি এটি ব্যবহার করেন তবে একটি শেল দক্ষ (

একটি শেল একটি কমান্ড-লাইন ইন্টারপ্রেটার হয়, এটি কমান্ড চালাতে এবং কোনও কাজে তাদের সহযোগিতা করার জন্য ডিজাইন করা হয়েছে।

আপনি 1000000000 পর্যন্ত গণনা করতে চান তাহলে, আপনি গণনা করতে, মত একটি (এক) কমান্ড ডাকা seq, bc, awkবা python/ perl... 1000000000 রানিং [[...]]কমান্ড এবং 1000000000 letকমান্ড ভয়ঙ্কর অদক্ষ হতে বাধ্য, বিশেষ করে সঙ্গে bashযা সব ধীরতম শেল হয়।

সে ক্ষেত্রে, একটি শেল অনেক দ্রুত হবে:

$ time sh -c 'seq 100000000' > /dev/null
sh -c 'seq 100000000' > /dev/null  0.77s user 0.03s system 99% cpu 0.805 total
$ time python -c 'i=0
> while i <= 100000000: i=i+1'
python -c 'i=0 while i <= 100000000: i=i+1'  12.12s user 0.00s system 99% cpu 12.127 total

যদিও অবশ্যই, বেশিরভাগ কাজ শেলটি যে কমান্ডগুলি আদেশ করে তা দ্বারা করা হয় it

এখন, আপনি অবশ্যই এটি দিয়ে করতে পারেন python:

python -c '
import os
os.dup2(os.open("/dev/null", os.O_WRONLY), 1);
os.execlp("seq", "seq", "100000000")'

তবে এটি যে আপনি মূলত প্রোগ্রামিং ভাষা pythonহিসাবে pythonকাজ করতে চান তা নয়, কোনও কমান্ড লাইন ইন্টারপ্রেটার নয়।

আপনি কি করতে পারেন তা নোট করুন:

python -c 'import os; os.system("seq 100000000 > /dev/null")'

তবে, pythonসেই কমান্ড লাইনের ব্যাখ্যার জন্য আসলে একটি শেল ডাকবে!


আমি আপনার উত্তর ভালবাসি। অন্যান্য অনেক উত্তরে "কীভাবে" কৌশলগুলি উন্নত হয়েছে তা নিয়ে আলোচনা করা হয়েছে, যখন আপনি "কেন" এবং বোধগম্যভাবে "কেন নয়" ওপির পদ্ধতির পদ্ধতির ত্রুটির সমাধান করার জন্য উভয়ই কভার করেছেন।
greg.arnott


3

কিছুই ঠিক নেই (আপনার প্রত্যাশা ব্যতীত) পাইথনটি সত্যই কম-সংকলিত ভাষার জন্য দ্রুত, https://wiki.python.org/moin/PythonSpeed ​​দেখুন


1
আমি বরং এর মতো উত্তরগুলি থেকে নিরুৎসাহিত করি, এটি আইএমএইচওর মন্তব্যের অন্তর্গত।
লিনাক্সসিকিউরিটিফ্রাইক

2

মন্তব্যগুলি বাদ দিয়ে আপনি কোডটি কিছুটা অনুকূল করতে পারেন , যেমন

#!/bin/bash
for (( i = 0; i <= 1000000000; i++ ))
do
: # null command
done

এই কোডটি কিছুটা কম সময় নিতে হবে ।

তবে প্রকৃতপক্ষে ব্যবহারের যোগ্য হওয়ার জন্য এটি যথেষ্ট দ্রুত নয়।


-3

যৌক্তিক সমতুল্য "যখন" এবং "অবধি" এক্সপ্রেশন ব্যবহার থেকে ব্যাশের মধ্যে নাটকীয় পার্থক্য লক্ষ্য করেছি:

time (i=0 ; while ((i<900000)) ; do  i=$((i+1)) ; done )

real    0m5.339s
user    0m5.324s
sys 0m0.000s

time (i=0 ; until ((i=900000)) ; do  i=$((i+1)) ; done )

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

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


6
এই এক দিয়ে চেষ্টা করুন ((i==900000))
টমাসজ

2
আপনি =অ্যাসাইনমেন্টের জন্য ব্যবহার করছেন। এটি অবিলম্বে সত্য ফিরে আসবে। কোনও লুপ সংঘটিত হবে না।
ওয়াইল্ডকার্ড

1
আপনি আসলে বাশ আগে ব্যবহার করেছেন? :)
লিনাক্সসিকিউরিটিফ্রিকে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.