আমি এখন প্রায় 15 বছর ধরে একজন সফটওয়্যার ইঞ্জিনিয়ার হয়েছি। আমার কর্মজীবন জুড়ে আমি অনেক কিছু শিখেছি এবং এই শিক্ষাগুলিকে অনেকগুলি বিতরণ করা সিস্টেম ডিজাইন এবং বাস্তবায়নে প্রয়োগ করেছি (এবং মাঝে মাঝে ফেজ আউট বা ত্যাগ করি)। সেই পথে আমি অসংখ্য ভুল করেছি এবং এখনও করে যাচ্ছি। কিন্তু আমার প্রধান ফোকাস ছিল নির্ভরযোগ্যতা হিসাবে আমি ত্রুটি ফ্রিকোয়েন্সি হ্রাস করার উপায় খুঁজে বের করার জন্য আমার অভিজ্ঞতা এবং সম্প্রদায়ের দিকে ফিরে তাকাচ্ছি। আমার নীতিবাক্য হল: আমাদের অবশ্যই নতুন ভুল করার চেষ্টা করতে হবে (কম সুস্পষ্ট, আরও পরিশীলিত)। একটি ভুল করা ভাল - এইভাবে আমরা শিখি, পুনরাবৃত্তি করা - দুঃখজনক এবং নিরুৎসাহিত।
এটিই সম্ভবত গণিত সম্পর্কে আমাকে সর্বদা মুগ্ধ করেছে। শুধু তাই নয় যে এটি মার্জিত এবং সংক্ষিপ্ত, কিন্তু কারণ এর যৌক্তিক কঠোরতা ভুল প্রতিরোধ করে। এটি আপনাকে আপনার বর্তমান প্রেক্ষাপট সম্পর্কে চিন্তা করতে বাধ্য করে, আপনি কোন অনুমান এবং উপপাদ্যগুলির উপর নির্ভর করতে পারেন। এই নিয়মগুলি অনুসরণ করা ফলপ্রসূ প্রমাণিত হয়, আপনি সঠিক ফলাফল পাবেন। এটা সত্য যে, কম্পিউটার বিজ্ঞান গণিতের একটি শাখা। কিন্তু আমরা সাধারণত যা অনুশীলন করি তা হল সফ্টওয়্যার ইঞ্জিনিয়ারিং, একটি খুব আলাদা জিনিস। আমরা কম্পিউটার বিজ্ঞানের কৃতিত্ব এবং আবিষ্কারগুলি অনুশীলনের জন্য প্রয়োগ করি, সময়ের সীমাবদ্ধতা এবং ব্যবসায়ের প্রয়োজনগুলির জন্য অ্যাকাউন্টিং করি। এই ব্লগটি কম্পিউটার প্রোগ্রামের ডিজাইন এবং বাস্তবায়নে আধা-গাণিতিক যুক্তি প্রয়োগ করার একটি প্রচেষ্টা। আমরা বিভিন্ন এক্সিকিউশন রেজিমের একটি মডেল সামনে রাখব যা অনেক প্রোগ্রামিং ত্রুটি এড়াতে একটি কাঠামো প্রদান করে।
যখন আমরা প্রোগ্রাম করতে শিখি এবং আমাদের প্রথম অস্থায়ী (বা সাহসী) পদক্ষেপগুলি করি তখন আমরা সাধারণত কিছু সহজ দিয়ে শুরু করি:
আমরা পেশী মেমরি অর্জন করি, ভাষা সিনট্যাক্স শিখি এবং সবচেয়ে গুরুত্বপূর্ণভাবে আমরা আমাদের চিন্তাভাবনা এবং যুক্তি পরিবর্তন করি। আমরা কোড পড়তে শিখি, কীভাবে এটি কার্যকর করা হচ্ছে সে সম্পর্কে অনুমান তৈরি করি। আমরা প্রায় কখনই একটি ভাষার মান পড়ে শুরু করি না এবং এর "মেমরি মডেল" বিভাগের মাধ্যমে ঘনিষ্ঠভাবে পড়ি - কারণ আমরা এখনও সেগুলিকে পুরোপুরি উপলব্ধি করতে এবং ব্যবহার করতে সজ্জিত নই। আমরা ট্রায়াল এবং ত্রুটি অনুশীলন করি: আমরা আমাদের প্রথম প্রোগ্রামগুলিতে লজিক্যাল এবং গাণিতিক বাগগুলি প্রবর্তন করি। এই ভুলগুলি আমাদের অনুমানগুলি পরীক্ষা করতে শেখায়: এই লুপ অপরিবর্তনীয় কি সঠিক, আমরা কি এইভাবে অ্যারের উপাদানের সূচক এবং দৈর্ঘ্য তুলনা করতে পারি (আপনি এটি -1 কোথায় রাখবেন)? কিন্তু যদি আমরা কিছু ধরণের ত্রুটি দেখতে না পাই, প্রায়শই আমরা কিছু অভ্যন্তরীণ করে নিই
যথা এই এক:
কোডের লাইন সবসময় একই ক্রমে মূল্যায়ন করা হয় (ক্রমিক)।
এই অনুমানটি আমাদের অনুমান করতে দেয় যে পরবর্তী প্রস্তাবগুলি সত্য (আমরা সেগুলি প্রমাণ করতে যাচ্ছি না):
গাণিতিক স্বতঃসিদ্ধ বৃহত্তর স্ট্রাকচারগুলিকে শক্ত ভিত্তিতে তৈরি করতে এবং তৈরি করতে দেয়। গণিতে, আমাদের কাছে 4+1 পোস্টুলেট সহ ইউক্লিডীয় জ্যামিতি রয়েছে। শেষটি বলেছেন:
সমান্তরাল রেখাগুলি সমান্তরাল থাকে, তারা ছেদ বা বিচ্ছিন্ন হয় না
সহস্রাব্দ ধরে গণিতবিদরা এটি প্রমাণ করার চেষ্টা করেছিলেন এবং প্রথম চারটি থেকে এটি আহরণ করেছিলেন। দেখা যাচ্ছে যে এটি সম্ভব নয়। আমরা এই "সমান্তরাল রেখাগুলি" বিকল্পগুলির সাথে প্রতিস্থাপন করতে পারি এবং বিভিন্ন ধরণের জ্যামিতি (যেমন, হাইপারবোলিক এবং উপবৃত্তাকার) পেতে পারি, যা নতুন সম্ভাবনার সূচনা করে এবং প্রযোজ্য এবং দরকারী হতে দেখা যায়। সর্বোপরি আমাদের নিজস্ব গ্রহের পৃষ্ঠ সমতল নয় এবং এর জন্য আমাদেরকে হিসাব করতে হবে যেমন জিপিএস সফ্টওয়্যার এবং বিমানের রুটে।
কিন্তু তার আগে আসুন থামুন এবং সবচেয়ে ইঞ্জিনিয়ারিং প্রশ্ন জিজ্ঞাসা করুন: কেন বিরক্ত? যদি প্রোগ্রামটি তার কাজ করে, এটি সমর্থন করা, রক্ষণাবেক্ষণ করা এবং বিকশিত করা সহজ, কেন আমরা প্রথমে অনুমানযোগ্য অনুক্রমিক সম্পাদনের এই আরামদায়ক পরিবর্তনটি ছেড়ে দেব?
আমি দুটি উত্তর দেখতে. প্রথমটি হল কর্মক্ষমতা । যদি আমরা আমাদের প্রোগ্রামকে দ্বিগুণ দ্রুত বা একইভাবে চালাতে পারি - হার্ডওয়্যারের অর্ধেক প্রয়োজন - এটি একটি ইঞ্জিনিয়ারিং অর্জন। একই পরিমাণ কম্পিউটেশনাল রিসোর্স ব্যবহার করলে আমরা 2x (বা 3, 4, 5, 10x) ডেটা গ্রাইন্ড করতে পারি - এটি একই প্রোগ্রামের সম্পূর্ণ নতুন অ্যাপ্লিকেশন খুলতে পারে। এটি সার্ভারের পরিবর্তে আপনার পকেটে থাকা মোবাইল ফোনে চলতে পারে। কখনও কখনও আমরা চতুর অ্যালগরিদম প্রয়োগ করে বা আরও কার্যকরী ভাষায় পুনরায় লেখার মাধ্যমে গতি অর্জন করতে পারি। এইগুলি আমাদের এক্সপ্লোর করার প্রথম বিকল্প, হ্যাঁ। কিন্তু তাদের একটা সীমা আছে। স্থাপত্য প্রায় সবসময় বাস্তবায়ন বীট. মুরের আইন ইদানীং তেমন ভালো করছে না, একটি একক CPU-এর কর্মক্ষমতা ধীরে ধীরে বাড়ছে, RAM কর্মক্ষমতা (প্রধানতঃ বিলম্বিতা) পিছিয়ে যাচ্ছে। সুতরাং, স্বাভাবিকভাবেই, প্রকৌশলীরা অন্যান্য বিকল্পগুলি সন্ধান করতে শুরু করেছিলেন।
দ্বিতীয় বিবেচনা নির্ভরযোগ্যতা . প্রকৃতি বিশৃঙ্খল, তাপগতিবিদ্যার দ্বিতীয় সূত্র সুনির্দিষ্ট, ক্রমিক এবং পুনরাবৃত্তিযোগ্য যেকোনো কিছুর বিরুদ্ধে ক্রমাগত কাজ করে। বিট ফ্লিপ হয়, উপকরণ ক্ষয় হয়, পাওয়ার ব্রাউন হয়ে যায়, তারগুলি কেটে যায় যা আমাদের প্রোগ্রামগুলি সম্পাদনে বাধা দেয়। ক্রমিক এবং পুনরাবৃত্তিযোগ্য বিমূর্ততা রাখা একটি কঠিন কাজ হয়ে ওঠে। যদি আমাদের প্রোগ্রামগুলি সফ্টওয়্যার এবং হার্ডওয়্যার ব্যর্থতাগুলিকে ছাড়িয়ে যেতে পারে তবে আমরা এমন পরিষেবাগুলি সরবরাহ করতে পারি যেগুলির একটি প্রতিযোগিতামূলক ব্যবসায়িক সুবিধা রয়েছে - এটি আরেকটি প্রকৌশল কাজ যা আমরা সমাধান শুরু করতে পারি।
লক্ষ্যের সাথে সজ্জিত, আমরা অ-ক্রমিক পদ্ধতির সাথে পরীক্ষা শুরু করতে পারি।
আসুন ছদ্ম কোডের এই অংশটি দেখি:
```
def fetch_coordinates(poi: str) -> Point:
…
def find_pois(center: Point, distance: int) -> List[str]:
…
def get_my_location() -> Point:
…
def fetch_coordinates(p) - Point:
…
def main():
me = get_my_location()
for point in find_pois(me, 500):
loc = fetch_coordinates(point)
sys.stdout.write(f“Name: {point} is at x={loc.x} y={loc.y}”)
আমরা টপ-টু-বাটম কোড পড়তে পারি এবং যুক্তিসঙ্গতভাবে ধরে নিতে পারি যে `find_pois` ফাংশনকে `get_my_location`- এর পরে কল করা হবে। এবং আমরা পরেরটি আনার পরে প্রথম POI এর স্থানাঙ্কগুলি আনব এবং ফেরত দেব। এই অনুমানগুলি সঠিক এবং প্রোগ্রাম সম্পর্কে একটি মানসিক মডেল, কারণ তৈরি করার অনুমতি দেয়।
আসুন কল্পনা করি আমরা আমাদের কোডটি অ-ক্রমিকভাবে চালানোর জন্য তৈরি করতে পারি। আমরা সিনট্যাক্টিকভাবে এটি করতে পারি এমন অনেক উপায় রয়েছে। আমরা স্টেটমেন্ট রি-অর্ডারিং নিয়ে পরীক্ষা-নিরীক্ষা এড়িয়ে যাব (এটিই আধুনিক কম্পাইলার এবং CPU গুলি করে) এবং আমাদের ভাষা প্রসারিত করব যাতে আমরা একটি নতুন ফাংশন এক্সিকিউশন শাসন প্রকাশ করতে পারি:
থ্রেডগুলি বিভিন্ন স্বাদে আসে: একটি POSIX থ্রেড, সবুজ থ্রেড, কোরোটিন, গোরুটিন। বিবরণ ব্যাপকভাবে পৃথক, কিন্তু এটি মৃত্যুদন্ড কার্যকর করা যেতে পারে যে কিছু নিচে ফোঁড়া. যদি একাধিক ফাংশন একসাথে চলতে পারে তবে প্রতিটির নিজস্ব শিডিউলিং ইউনিট প্রয়োজন। অর্থাৎ মাল্টি-থ্রেডিং থেকে এসেছে, একের পরিবর্তে, আমাদের এক্সিকিউশনের বেশ কয়েকটি থ্রেড রয়েছে। কিছু এনভায়রনমেন্ট (MPI) এবং ভাষা অস্পষ্টভাবে থ্রেড তৈরি করতে পারে, কিন্তু সাধারণত আমাদের C-তে `pthread_create`, Python-এ `threading` মডিউল ক্লাস বা Go-তে একটি সাধারণ `go` স্টেটমেন্ট ব্যবহার করে এটি করতে হয়। কিছু সতর্কতা অবলম্বন করে আমরা একই কোডকে বেশিরভাগ ক্ষেত্রে সমান্তরালভাবে চালাতে পারি:
def fetch_coordinates(poi, results, idx) -> None: … results[idx] = poi def main(): me = get_my_location() points = find_pois(me, 500) results = [None] * len(points) # Reserve space for each result threads = [] for i, point in enumerate(find_pois(me, 500)): # i - index for result thr = threading.Thread(target=fetch_coordinates, args=(poi, results, i)) thr.start() threads.append(thr) for thr in threads: thr.wait() for point, result in zip(points, results): sys.stdout.write(f“Name: {poi} is at x={loc.x} y={loc.y}”)
আমরা আমাদের কর্মক্ষমতা লক্ষ্য অর্জন করেছি: আমাদের প্রোগ্রাম একাধিক CPU এবং স্কেলে চলতে পারে কারণ কোরের সংখ্যা দ্রুত বৃদ্ধি পায় এবং শেষ হয়। পরবর্তী ইঞ্জিনিয়ারিং প্রশ্ন আমাদের অবশ্যই জিজ্ঞাসা করতে হবে: কি খরচে?
আমরা ইচ্ছাকৃতভাবে সিরিয়ালাইজড এবং অনুমানযোগ্য মৃত্যুদন্ড ছেড়ে দিয়েছি। এখানে
পরবর্তী পরিণতি হল যে একটি ফাংশন এই সময়ে অন্যটির আগে শেষ করতে পারে, পরের বার এটি অন্যভাবে হতে পারে। এক্সিকিউশনের এই নতুন ব্যবস্থা ডেটা রেসের দিকে নিয়ে যায়: যখন সমসাময়িক ফাংশনগুলি ডেটার সাথে কাজ করে তার মানে ডেটাতে প্রয়োগ করা ক্রিয়াকলাপের ক্রম অনির্ধারিত। আমরা ডেটা রেসের মুখোমুখি হতে শুরু করি এবং সেগুলি ব্যবহার করে মোকাবেলা করতে শিখি:
এই মুহুর্তে, আমরা অন্তত দুটি জিনিস আবিষ্কার করি। প্রথমত, ডেটা অ্যাক্সেস করার একাধিক উপায় রয়েছে। কিছু তথ্য আছে
যখন আমরা যুক্তির এই লাইনটি চালিয়ে যাই, অন্যান্য কৌশল যেমন থ্রেড-লোকাল স্টোরেজ স্বাভাবিকভাবেই আসে। আমরা আমাদের প্রোগ্রামিং টুলবেল্টে একটি নতুন গ্যাজেট অর্জন করেছি, সফ্টওয়্যার তৈরি করে আমরা যা অর্জন করতে পারি তা বিস্তৃত করে।
যাইহোক, আমরা এখনও নির্ভর করতে পারি এমন একটি পরিবর্তন আছে। যখন আমরা একটি থ্রেড থেকে ভাগ করা (দূরবর্তী) ডেটার জন্য পৌঁছাই, আমরা সর্বদা এটি পাই। এমন কোন পরিস্থিতি নেই যখন কিছু মেমরি খণ্ড পাওয়া যায় না। ব্যাকিং ফিজিক্যাল মেমরি অঞ্চলে ত্রুটি দেখা দিলে ওএস প্রক্রিয়াটি মেরে সব অংশগ্রহণকারীদের (থ্রেড) বন্ধ করে দেবে। একই কথা "আমাদের" থ্রেডের ক্ষেত্রেও প্রযোজ্য যদি আমরা একটি মিউটেক্স লক করে থাকি, তাহলে লকটি হারানোর কোনো উপায় নেই এবং আমরা যা করছি তা অবিলম্বে বন্ধ করতে হবে। আমরা এই পরিবর্তনের উপর নির্ভর করতে পারি (ওএস এবং আধুনিক হার্ডওয়্যার দ্বারা প্রয়োগ করা হয়েছে) যে সমস্ত অংশগ্রহণকারী হয় মৃত বা জীবিত। সকলেই ভাগ্য ভাগ করে নেয় : যদি প্রক্রিয়া (OOM), OS (কার্নেল বাগ) বা হার্ডওয়্যার কোনো সমস্যার সম্মুখীন হয় - আমাদের সমস্ত থ্রেড বাইরের অবশিষ্ট পার্শ্ব-প্রতিক্রিয়া ছাড়াই একসাথে থাকা বন্ধ হয়ে যাবে।
একটি গুরুত্বপূর্ণ বিষয় লক্ষ্য করুন। কিভাবে আমরা থ্রেড প্রবর্তন করে এই প্রথম ধাপ তৈরি করেছি? আমরা আলাদা, কাঁটাচামচ. একটি শিডিউলিং ইউনিটের পরিবর্তে আমরা একাধিক চালু করেছি। আসুন এই শেয়ার না করার পদ্ধতিটি প্রয়োগ করা চালিয়ে যান এবং দেখুন এটি কীভাবে যায়। এই সময় আমরা ভার্চুয়াল মেমরি প্রক্রিয়া কপি. একে বলা হয় - একটি প্রক্রিয়ার জন্ম দেওয়া । আমরা আমাদের প্রোগ্রামের অন্য একটি উদাহরণ চালাতে পারি বা অন্য বিদ্যমান ইউটিলিটি শুরু করতে পারি। এটি একটি দুর্দান্ত পদ্ধতি:
প্রায় সব ==
শেয়ার্ড ফেট ইনভেরিয়েন্ট ত্যাগ করে এবং ভার্চুয়াল মেমরি শেয়ার না করে এবং একটি কপি তৈরি করে আমরা আবিষ্কৃত মৃত্যুদন্ডের আরেকটি পদ্ধতি। কপি বিনামূল্যে নয়:
এখানে থামা কেন? আসুন আমরা আমাদের প্রোগ্রামটি অনুলিপি এবং বিতরণ করতে পারি আর কী তা অন্বেষণ করি। কিন্তু কেন প্রথম স্থানে বিতরণ যেতে? অনেক ক্ষেত্রে হাতের কাজগুলি একটি একক মেশিন ব্যবহার করে সমাধান করা যেতে পারে।
আমাদের বিতরণ করতে হবে
কয়েকটি নাম বলতে:
OS আপগ্রেড: সময়ে সময়ে আমাদের মেশিনগুলি পুনরায় বুট করতে হবে
হার্ডওয়্যার ব্যর্থতা: এগুলি আমরা যতটা চাই তার চেয়ে বেশি ঘন ঘন ঘটে
বাহ্যিক ব্যর্থতা: পাওয়ার এবং নেটওয়ার্ক বিভ্রাট একটি জিনিস।
যদি আমরা একটি OS অনুলিপি করি - আমরা এটিকে একটি ভার্চুয়াল মেশিন বলি এবং একটি ভৌতিক একটিতে গ্রাহকদের প্রোগ্রাম চালাতে পারি এবং এটিতে একটি বিশাল ক্লাউড ব্যবসা তৈরি করতে পারি। যদি আমরা দুই বা ততোধিক কম্পিউটার নিয়ে যাই এবং প্রতিটিতে আমাদের প্রোগ্রাম চালাই - আমাদের প্রোগ্রাম এমনকি হার্ডওয়্যার ব্যর্থতা থেকেও বাঁচতে পারে, 24/7 পরিষেবা প্রদান করে এবং একটি প্রতিযোগিতামূলক সুবিধা অর্জন করতে পারে। বৃহৎ কর্পোরেশনগুলি অনেক আগে আরও এগিয়ে গিয়েছিল এবং এখন ইন্টারনেট জায়ান্টগুলি বিভিন্ন ডেটাসেন্টার এবং এমনকি মহাদেশগুলিতে অনুলিপি চালায়, এইভাবে একটি প্রোগ্রামকে টাইফুন বা সাধারণ বিদ্যুৎ বিভ্রাটের জন্য স্থিতিস্থাপক করে তোলে।
কিন্তু এই স্বাধীনতা একটি মূল্যে আসে: পুরানো পরিবর্তনগুলি প্রয়োগ করা হয় না, আমরা আমাদের নিজস্ব। কোন চিন্তা নেই, আমরা প্রথম নই। আমাদের সাহায্য করার জন্য প্রচুর কৌশল, সরঞ্জাম এবং পরিষেবা রয়েছে৷
আমরা সবেমাত্র সিস্টেম এবং তাদের নিজ নিজ কার্যকরী ব্যবস্থা সম্পর্কে যুক্তি করার ক্ষমতা অর্জন করেছি। প্রতিটি বৃহৎ স্কেল আউট সিস্টেমের অভ্যন্তরে বেশিরভাগ অংশগুলি পরিচিত অনুক্রমিক এবং রাষ্ট্রহীন, অনেক উপাদান মেমরির ধরন এবং শ্রেণিবিন্যাস সহ বহু-থ্রেডেড কিছু সত্যিকারের বিতরণ করা অংশগুলির মিশ্রণ দ্বারা একত্রিত হয়:
লক্ষ্য হল আমরা বর্তমানে কোথায় আছি, কী পরিবর্তনকারীরা ধারণ করে এবং সেই অনুযায়ী কাজ (পরিবর্তন/ডিজাইন) করে তা আলাদা করতে সক্ষম হওয়া। আমরা মৌলিক যুক্তি হাইলাইট করেছি, "অজানা অজানা" কে "জানা অজানা" তে রূপান্তরিত করেছি। এটিকে হালকাভাবে নেবেন না, এটি একটি উল্লেখযোগ্য অগ্রগতি।