গ্রিনলেট বনাম টপিক


141

আমি জিনেন্টস এবং গ্রিনলেটগুলিতে নতুন। আমি তাদের সাথে কীভাবে কাজ করব সে সম্পর্কে কিছু ভাল ডকুমেন্টেশন পেয়েছি, তবে গ্রীনলেটগুলি কীভাবে এবং কখন ব্যবহার করা উচিত সে সম্পর্কে কেউই আমাকে ন্যায়সঙ্গততা দেয়নি!

  • তারা আসলে কি ভাল?
  • এগুলি প্রক্সি সার্ভারে ব্যবহার করা কি ভাল ধারণা?
  • থ্রেড না কেন?

আমি যে বিষয়ে নিশ্চিত নই তা হ'ল তারা যদি মূলত সহ-রুটিন হয় তবে তারা কীভাবে আমাদের সম্মতি প্রদান করতে পারে।


1
@ ইমরান এটি জাভার গ্রিনথ্রেড সম্পর্কে। আমার প্রশ্ন পাইথনের গ্রিনলেট নিয়ে। আমি কিছু অনুপস্থিত করছি ?
Rsh

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

3
@ ডিডিয়ার সিপিথন (এবং এখনই পাইপাই) পাইথন (বাইট) কোডটিকে সমান্তরালে ব্যাখ্যা করবে না (এটি সত্যই শারীরিকভাবে দুটি স্বতন্ত্র সিপিইউ কোরে একই সময়ে)। তবে পাইথন প্রোগ্রাম যা কিছু করে তা জিআইএল-এর আওতাধীন নয় (সাধারণ উদাহরণগুলি আই / ও এবং সি ফাংশন সহ সিস্কলগুলি যা ইচ্ছাকৃতভাবে জিআইএলকে প্রকাশ করে), এবং একটি threading.Threadআসলে সমস্ত র্যামফিকেশন সহ একটি ওএস থ্রেড। সুতরাং এটি সত্যিই বেশ সহজ না। যাইহোক, জাইথনের কোনও জিআইএল আফাইক নেই এবং পিপিও এটি থেকে মুক্তি পাওয়ার চেষ্টা করছে।

উত্তর:


204

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

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

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

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


1
ধন্যবাদ, মাত্র দুটি ছোট প্রশ্ন: 1) উচ্চতর আউটপুট অর্জনের জন্য কি এই সমাধানটি মাল্টিপ্রসেসিংয়ের সাথে একত্রিত করা সম্ভব? 2) আমি এখনও জানি না কেন কখনও থ্রেড ব্যবহার করেন? অজগর স্ট্যান্ডার্ড লাইব্রেরিতে আমরা কী এগুলিকে একটি নির্বোধ এবং মৌলিক প্রয়োগ হিসাবে বিবেচনা করতে পারি?
Rsh

6
1) হ্যাঁ, একেবারে। আপনার অকাল থেকে এটি করা উচিত নয়, তবে এই প্রশ্নের ক্ষেত্রের বাইরে পুরো কারণগুলির কারণেই, একাধিক প্রক্রিয়া করার অনুরোধগুলি আপনাকে উচ্চতর আউটপুট দেবে। 2) ওএস থ্রেডগুলি পূর্বনির্ধারিতভাবে নির্ধারিত হয় এবং ডিফল্টরূপে সম্পূর্ণ সমান্তরাল হয়। পাইথনে এগুলি ডিফল্ট কারণ পাইথন দেশীয় থ্রেডিং ইন্টারফেসটি প্রকাশ করে এবং আধুনিক অপারেটিং সিস্টেমে প্যারালালিজম এবং সম্মতি উভয়ের জন্য থ্রেডগুলি সেরা সমর্থিত এবং সর্বনিম্ন সাধারণ ডিনোমিনেটর।
ম্যাট জেন্ডার

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

@ ম্যাটজাইনার আমার নীচের ফাংশনটি রয়েছে যা এমডি 5 যোগফল গণনা করতে বিশাল ফাইলটি পড়ে। আমি কীভাবে এই ক্ষেত্রে জেনভেন্টটি দ্রুত পড়তে পারি import hashlib def checksum_md5(filename): md5 = hashlib.md5() with open(filename,'rb') as f: for chunk in iter(lambda: f.read(8192), b''): md5.update(chunk) return md5.digest()
সৌম্য

18

@ ম্যাক্সের উত্তর নেওয়া এবং এটি স্কেলিংয়ের জন্য কিছু প্রাসঙ্গিকতা যুক্ত করা, আপনি পার্থক্যটি দেখতে পাবেন। নিম্নলিখিত হিসাবে পূরণ করা URL গুলি পরিবর্তন করে আমি এটি অর্জন করেছি:

URLS_base = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
URLS = []
for _ in range(10000):
    for url in URLS_base:
        URLS.append(url)

আমি মাল্টিপ্রসেস সংস্করণটি ছাড়তে হয়েছিল কারণ এটি 500 হওয়ার আগেই পড়েছিল; তবে 10,000 পুনরাবৃত্তিতে:

Using gevent it took: 3.756914
-----------
Using multi-threading it took: 15.797028

সুতরাং আপনি দেখতে পারেন যে জেন্টটি ব্যবহার করে আই / ও-তে কিছু উল্লেখযোগ্য পার্থক্য রয়েছে


4
কাজটি শেষ করতে 60000 নেটিভ থ্রেড বা প্রক্রিয়াগুলি সম্পূর্ণভাবে ভুল এবং এই পরীক্ষাটি কিছুই দেখায় না (এছাড়াও আপনি জেনভেন্ট। জয়নাল () কলটি শেষ করে নিলেন?) : 50 সম্পর্কে থ্রেডের একটি থ্রেড পুল ব্যবহার করার চেষ্টা করুন, আমার উত্তর দেখার stackoverflow.com/a/51932442/34549
zzzeek

9

উপরে @ টেম্পোরাল বিয়িংয়ের উত্তরের জন্য সংশোধন করা হচ্ছে, গ্রিনলেটগুলি থ্রেডের চেয়ে "দ্রুত" নয় এবং এটি একযোগিতামূলক সমস্যা সমাধানের জন্য 60000 থ্রেড ছড়িয়ে দেওয়া একটি ভুল প্রোগ্রামিং কৌশল , পরিবর্তে থ্রেডের একটি ছোট পুল উপযুক্ত। এখানে আরও যুক্তিসঙ্গত তুলনা করা হয়েছে ( এই রে পোস্টটির উদ্ধৃতি দিয়ে লোকদের প্রতিক্রিয়াতে আমার রেডডিট পোস্ট থেকে )।

import gevent
from gevent import socket as gsock
import socket as sock
import threading
from datetime import datetime


def timeit(fn, URLS):
    t1 = datetime.now()
    fn()
    t2 = datetime.now()
    print(
        "%s / %d hostnames, %s seconds" % (
            fn.__name__,
            len(URLS),
            (t2 - t1).total_seconds()
        )
    )


def run_gevent_without_a_timeout():
    ip_numbers = []

    def greenlet(domain_name):
        ip_numbers.append(gsock.gethostbyname(domain_name))

    jobs = [gevent.spawn(greenlet, domain_name) for domain_name in URLS]
    gevent.joinall(jobs)
    assert len(ip_numbers) == len(URLS)


def run_threads_correctly():
    ip_numbers = []

    def process():
        while queue:
            try:
                domain_name = queue.pop()
            except IndexError:
                pass
            else:
                ip_numbers.append(sock.gethostbyname(domain_name))

    threads = [threading.Thread(target=process) for i in range(50)]

    queue = list(URLS)
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    assert len(ip_numbers) == len(URLS)

URLS_base = ['www.google.com', 'www.example.com', 'www.python.org',
             'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']

for NUM in (5, 50, 500, 5000, 10000):
    URLS = []

    for _ in range(NUM):
        for url in URLS_base:
            URLS.append(url)

    print("--------------------")
    timeit(run_gevent_without_a_timeout, URLS)
    timeit(run_threads_correctly, URLS)

এখানে কিছু ফলাফল রয়েছে:

--------------------
run_gevent_without_a_timeout / 30 hostnames, 0.044888 seconds
run_threads_correctly / 30 hostnames, 0.019389 seconds
--------------------
run_gevent_without_a_timeout / 300 hostnames, 0.186045 seconds
run_threads_correctly / 300 hostnames, 0.153808 seconds
--------------------
run_gevent_without_a_timeout / 3000 hostnames, 1.834089 seconds
run_threads_correctly / 3000 hostnames, 1.569523 seconds
--------------------
run_gevent_without_a_timeout / 30000 hostnames, 19.030259 seconds
run_threads_correctly / 30000 hostnames, 15.163603 seconds
--------------------
run_gevent_without_a_timeout / 60000 hostnames, 35.770358 seconds
run_threads_correctly / 60000 hostnames, 29.864083 seconds

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


8

এটি বিশ্লেষণ করার জন্য যথেষ্ট আকর্ষণীয়। মাল্টি প্রসেসিং পুল বনাম মাল্টি-থ্রেডিংয়ের তুলনায় গ্রিনলেটগুলির পারফরম্যান্সের তুলনা করার জন্য এখানে একটি কোড রয়েছে:

import gevent
from gevent import socket as gsock
import socket as sock
from multiprocessing import Pool
from threading import Thread
from datetime import datetime

class IpGetter(Thread):
    def __init__(self, domain):
        Thread.__init__(self)
        self.domain = domain
    def run(self):
        self.ip = sock.gethostbyname(self.domain)

if __name__ == "__main__":
    URLS = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
    t1 = datetime.now()
    jobs = [gevent.spawn(gsock.gethostbyname, url) for url in URLS]
    gevent.joinall(jobs, timeout=2)
    t2 = datetime.now()
    print "Using gevent it took: %s" % (t2-t1).total_seconds()
    print "-----------"
    t1 = datetime.now()
    pool = Pool(len(URLS))
    results = pool.map(sock.gethostbyname, URLS)
    t2 = datetime.now()
    pool.close()
    print "Using multiprocessing it took: %s" % (t2-t1).total_seconds()
    print "-----------"
    t1 = datetime.now()
    threads = []
    for url in URLS:
        t = IpGetter(url)
        t.start()
        threads.append(t)
    for t in threads:
        t.join()
    t2 = datetime.now()
    print "Using multi-threading it took: %s" % (t2-t1).total_seconds()

ফলাফল এখানে:

Using gevent it took: 0.083758
-----------
Using multiprocessing it took: 0.023633
-----------
Using multi-threading it took: 0.008327

আমি মনে করি যে গ্রিনলেট দাবি করেছে যে এটি মাল্টিথ্রেডিং লাইব্রেরির মতো জিআইএল দ্বারা আবদ্ধ নয়। তদুপরি, গ্রিনলেট ডক বলেছেন যে এটি নেটওয়ার্ক অপারেশনগুলির জন্য। নেটওয়ার্ক নিবিড় ক্রিয়াকলাপের জন্য, থ্রেড-স্যুইচিং ঠিক আছে এবং আপনি দেখতে পাচ্ছেন যে মাল্টিথ্রেডিং পদ্ধতিটি খুব দ্রুত। এছাড়াও অজগরটির অফিসিয়াল লাইব্রেরিগুলি ব্যবহার করা সবসময়ই প্রাকৃতিক; আমি উইন্ডোতে গ্রিনলেট ইনস্টল করার চেষ্টা করেছি এবং একটি dll নির্ভরতা সমস্যার সম্মুখীন হয়েছি তাই আমি এই পরীক্ষাটি একটি লিনাক্স ভিএম এ চালিয়েছি ran সবসময় কোনও কোডটি লেখার চেষ্টা করুন এই আশায় যে এটি কোনও মেশিনে চলে।


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

1
@ক ট. এটা একটা চমৎকার বিন্দু. একটি ভাল ছবি পাওয়ার জন্য আপনাকে এই পরীক্ষাটি বেশ কয়েকবার চালানো এবং উপায়, পদ্ধতি এবং মিডিয়ান নেওয়া দরকার take প্রোটোকলগুলির জন্য রাউটারগুলি ক্যাশে রুটের পাথ এবং যেখানে তারা রুট পাথগুলি ক্যাশে করে না সেখানে আপনি বিভিন্ন ডিএনএস রুটের পাথ ট্র্যাফিক থেকে আলাদা ল্যাগ পেতে পারেন Note এবং ডিএনএস সার্ভারগুলি ভারী ক্যাশে করে। টাইমক্লক () ব্যবহার করে থ্রেডিং পরিমাপ করা আরও ভাল যেখানে নেটওয়ার্ক হার্ডওয়ারের উপরে বিলম্বিত হওয়ার কারণে সিপিইউ চক্র ব্যবহার করা হয়। এটি অন্যান্য ওএস পরিষেবাদিগুলি স্নেহ করা এবং আপনার পরিমাপ থেকে সময় যুক্ত করতে পারে।
দেবপ্লেয়ার

ওহ এবং আপনি এই তিনটি পরীক্ষার মধ্যে ওএস স্তরে একটি ডিএনএস ফ্লাশ চালাতে পারেন তবে আবার এটি কেবল স্থানীয় ডিএনএস ক্যাচিং থেকে মিথ্যা ডেটা হ্রাস করবে।
দেবপ্লেয়ার

হা. এই পরিষ্কার সংস্করণটি চালানো হচ্ছে: paste.ubuntu.com/p/pg3KTzT2FG আমি বেশ অনেকটা অভিন্ন সময় using_gevent() 421.442985535ms using_multiprocessing() 394.540071487ms using_multithreading() 402.48298645ms
পেয়েছি

আমি মনে করি ওএসএক্স DNS ক্যাশে করছে কিন্তু লিনাক্স এটি একটি "ডিফল্ট" জিনিস না: stackoverflow.com/a/11021207/34549 , তাই হ্যাঁ সম্পাতবিন্দু greenlets নিম্ন মাত্রার এ ওভারহেড অনুবাদক কারণে যে কত খারাপ
zzzeek
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.