প্রথম দিকে রিটার্ন অন্য তুলনায় মন্থর কেন?


179

এটি কিছু দিন আগে আমি যে উত্তর দিয়েছিলাম তার ফলো-আপ প্রশ্ন । সম্পাদনা: মনে হচ্ছে যে প্রশ্নটির ওপি একই প্রশ্ন জিজ্ঞাসা করার জন্য ইতিমধ্যে আমার কাছে পোস্ট করা কোডটি ব্যবহার করেছিল , তবে আমি এ সম্পর্কে অসচেতন ছিলাম। দুঃক্ষিত। প্রদত্ত উত্তরগুলি যদিও আলাদা!

উল্লেখযোগ্যভাবে আমি পর্যবেক্ষণ করেছি যে:

>>> def without_else(param=False):
...     if param:
...         return 1
...     return 0
>>> def with_else(param=False):
...     if param:
...         return 1
...     else:
...         return 0
>>> from timeit import Timer as T
>>> T(lambda : without_else()).repeat()
[0.3011460304260254, 0.2866089344024658, 0.2871549129486084]
>>> T(lambda : with_else()).repeat()
[0.27536892890930176, 0.2693932056427002, 0.27011704444885254]
>>> T(lambda : without_else(True)).repeat()
[0.3383951187133789, 0.32756996154785156, 0.3279120922088623]
>>> T(lambda : with_else(True)).repeat()
[0.3305950164794922, 0.32186388969421387, 0.3209099769592285]

... বা অন্য কথায়: থাকার elseif শর্তটি চালু হওয়া বা না হওয়া শর্ত ছাড়াই দ্রুততর হয় ।

আমি ধরে নিলাম যে এটি দুটি দ্বারা উত্পন্ন বিভিন্ন বাইকোডের সাথে সম্পর্কযুক্ত, তবে কেউ কি বিশদভাবে নিশ্চিত / ব্যাখ্যা করতে সক্ষম?

সম্পাদনা: দেখে মনে হচ্ছে যে প্রত্যেকেই আমার সময়গুলি পুনরুত্পাদন করতে সক্ষম নয়, তাই আমি ভেবেছিলাম এটি আমার সিস্টেমে কিছু তথ্য দেওয়ার জন্য দরকারী be আমি উবুন্টু ১১.১০ 64৪ বিটটি ডিফল্ট পাইথন ইনস্টল করে চালাচ্ছি। pythonনিম্নলিখিত সংস্করণ তথ্য উত্পন্ন:

Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2

পাইথন ২.7-এ বিচ্ছিন্ন হওয়ার ফলাফল এখানে রয়েছে:

>>> dis.dis(without_else)
  2           0 LOAD_FAST                0 (param)
              3 POP_JUMP_IF_FALSE       10

  3           6 LOAD_CONST               1 (1)
              9 RETURN_VALUE        

  4     >>   10 LOAD_CONST               2 (0)
             13 RETURN_VALUE        
>>> dis.dis(with_else)
  2           0 LOAD_FAST                0 (param)
              3 POP_JUMP_IF_FALSE       10

  3           6 LOAD_CONST               1 (1)
              9 RETURN_VALUE        

  5     >>   10 LOAD_CONST               2 (0)
             13 RETURN_VALUE        
             14 LOAD_CONST               0 (None)
             17 RETURN_VALUE        

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

3
3.x উপর উভয় উত্পাদন অভিন্ন বাইটকোড কিছু অনধিগম্য কোড সংরক্ষণ ( LOAD_CONST(None); RETURN_VALUE- শেষে কিন্তু বিবৃত, এটা পৌঁছে আগে কখনো ছিল না) with_else। আমি অত্যন্ত সন্দেহ করি ডেড কোড একটি ফাংশনটি দ্রুত করে তোলে। কেউ কি dis2.7 এ সরবরাহ করতে পারে?

4
আমি এটি পুনরুত্পাদন করতে সক্ষম ছিল না। সঙ্গে ফাংশন elseএবং Falseতাদের ধীরতম সমস্ত (152ns) ছিল। দ্বিতীয় দ্রুততম (143ns) Trueছাড়াই ছিল elseএবং অন্য দুটি মূলত একই রকম ছিল (137ns এবং 138ns)। আমি ডিফল্ট প্যারামিটার ব্যবহার করি নি এবং এটি %timeitআইপিসনে এটি পরিমাপ করেছি ।
rplnt

আমি এই সময়গুলি পুনরুত্পাদন করতে পারি না, কখনও কখনও উইলের সাথে দ্রুত হয়, কখনও কখনও এটি বিনা_দিকের সংস্করণ, দেখে মনে হয় এগুলি আমার জন্য বেশ অনুরূপ ...
সিড্রিক জুলিয়েন

1
বিচ্ছিন্নতার ফলাফল যুক্ত হয়েছে। আমি উবুন্টু ১১.১০, 64৪-বিট, পাইথন ২.7 ব্যবহার করছি - @ ম্যাকের মতো একই কনফিগারেশন। আমি দৃ conc়ভাবে সম্মতি জানাই যে with_elseএটি পর্যবেক্ষণযোগ্য দ্রুত।
ক্রিস মরগান

উত্তর:


387

এটি খাঁটি অনুমান, এবং এটি সঠিক কিনা তা যাচাই করার সহজ উপায় আমি খুঁজে পাইনি, তবে আপনার কাছে আমার কাছে একটি তত্ত্ব আছে।

আমি আপনার কোডটি চেষ্টা করেছি এবং ফলাফলগুলির মতোই পেয়েছি, without_else()বার বার এর চেয়ে সামান্য ধীর with_else():

>>> T(lambda : without_else()).repeat()
[0.42015745017874906, 0.3188967452567226, 0.31984281521812363]
>>> T(lambda : with_else()).repeat()
[0.36009842032996175, 0.28962249392031936, 0.2927151355828528]
>>> T(lambda : without_else(True)).repeat()
[0.31709728471076915, 0.3172671387005721, 0.3285821242644147]
>>> T(lambda : with_else(True)).repeat()
[0.30939889008243426, 0.3035132258429485, 0.3046679117038593]

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

>>> def no_else(param=False):
    if param:
        return 1
    return 0

>>> T(lambda : no_else()).repeat()
[0.3359846013948413, 0.29025818923918223, 0.2921801513879245]
>>> T(lambda : no_else(True)).repeat()
[0.3810395594970828, 0.2969634408842694, 0.2960104566362247]

আমার ধারণাটি হ'ল এর without_elseসাথে অন্য কোনও কিছুর সাথে হ্যাশের সংঘর্ষ রয়েছে globals()তাই বিশ্বব্যাপী নামের অনুসন্ধানটি কিছুটা ধীর।

সম্পাদনা করুন : 7 বা 8 কী সহ একটি অভিধানে সম্ভবত 32 স্লট রয়েছে, সুতরাং সেই ভিত্তিতে without_elseএকটি হ্যাশের সংঘর্ষ রয়েছে __builtins__:

>>> [(k, hash(k) % 32) for k in globals().keys() ]
[('__builtins__', 8), ('with_else', 9), ('__package__', 15), ('without_else', 8), ('T', 21), ('__name__', 25), ('no_else', 28), ('__doc__', 29)]

হ্যাশিং কীভাবে কাজ করে তা স্পষ্ট করতে:

__builtins__ -1196389688-এ হ্যাশগুলি টেবিলের আকার (32) কে মডিউল হ্রাস করেছে এর অর্থ এটি টেবিলের # 8 স্লটে সংরক্ষিত।

without_else505688136 এ হ্যাশগুলি যা মডুলো 32 হ্রাস করেছে 8 তাই একটি সংঘর্ষ রয়েছে। এই পাইথন গণনা সমাধান করার জন্য:

দিয়ে শুরু:

j = hash % 32
perturb = hash

কোনও ফ্রি স্লট না পাওয়া পর্যন্ত এটি পুনরাবৃত্তি করুন:

j = (5*j) + 1 + perturb;
perturb >>= 5;
use j % 2**i as the next table index;

যা এটি পরবর্তী সূচক হিসাবে 17 ব্যবহার করতে দেয়। ভাগ্যক্রমে এটি বিনামূল্যে তাই লুপটি একবারে পুনরাবৃত্তি করে। হ্যাশ টেবিলের আকার 2 এর শক্তি, তাই 2**iহ্যাশ টেবিলের আকারও iহ্যাশ মান থেকে ব্যবহৃত বিটের সংখ্যা j

টেবিলের প্রতিটি তদন্তগুলির মধ্যে একটি আবিষ্কার করতে পারে:

  • স্লটটি খালি, সেক্ষেত্রে তদন্ত থামানো হয় এবং আমরা জানি মানটি টেবিলে নেই।

  • স্লটটি অব্যবহৃত তবে অতীতে ব্যবহৃত হয়েছিল যে ক্ষেত্রে আমরা উপরের হিসাবে গণনা করা পরবর্তী মানটি চেষ্টা করে দেখি।

  • স্লটটি পূর্ণ তবে টেবিলে সঞ্চিত পূর্ণ হ্যাশ মানটি আমরা যে কীটির সন্ধান করছি তার হ্যাশের মতো নয় ( __builtins__বনামের ক্ষেত্রে এটিই ঘটে without_else)।

  • স্লটটি পূর্ণ এবং হ্যাশ মানটি আমরা চাই যা ঠিক আছে, তারপরে পাইথন পরীক্ষা করে যাচাই করে যে আমরা কী এবং যে বস্তুটি সন্ধান করছি তা একই বস্তু (যা এই ক্ষেত্রে তারা হবে কারণ শর্ট স্ট্রিংগুলি যা সনাক্তকারী হতে পারে তাই ইন্টার্ন করা হয়েছে) অভিন্ন শনাক্তকারীরা সঠিক একই স্ট্রিং ব্যবহার করে)।

  • অবশেষে যখন স্লটটি পূর্ণ হয়, হ্যাশটি ঠিক মেলে, তবে কীগুলি অভিন্ন বস্তু নয়, তারপরে এবং কেবল তখনই পাইথন তাদের সাথে সাম্যের জন্য তুলনা করার চেষ্টা করবে। এটি তুলনামূলকভাবে ধীর, তবে নাম অনুসন্ধানগুলির ক্ষেত্রে প্রকৃতপক্ষে হওয়া উচিত নয়।


9
@ ক্রিস, স্ট্রিংয়ের কোনও দৈর্ঘ্য উল্লেখযোগ্য হওয়া উচিত নয়। প্রথমবার যখন আপনি একটি স্ট্রিং হ্যাশ করেন তখন স্ট্রিং দৈর্ঘ্যের সাথে আনুপাতিক সময় লাগবে তবে তারপরে গণনা করা হ্যাশ স্ট্রিং অবজেক্টে ক্যাশে হবে যাতে পরবর্তী হ্যাশগুলি ও (1) হয়।
ডানকান

1
আহ ঠিক আছে, আমি ক্যাচিং সম্পর্কে সচেতন ছিলাম না, তবে তা বোঝা যায়
ক্রিস এবারেল

9
আকর্ষনীয়! আমি কি আপনাকে শার্লক বলতে পারি? ;) যাইহোক আমি আশা করি প্রশ্নটি যোগ্য হওয়ার সাথে সাথে আমি আপনাকে কিছু পরিমাণ পয়েন্ট দিতে ভুলে যাব না।
ভু

4
@ ম্যাক, বেশ নয় আমি হ্যাশ রেজোলিউশন সম্পর্কে কিছুটা যুক্ত করব (এটি মন্তব্যটিতে চেপে যাচ্ছিল তবে এটি আমার ধারণা থেকে বেশি আকর্ষণীয়)।
ডানকান

6
@ ডানকান - হ্যাশ প্রক্রিয়াটি চিত্রিত করার জন্য সময় দেওয়ার জন্য আপনাকে অনেক ধন্যবাদ। শীর্ষ খাঁজ উত্তর! :)
ম্যাক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.