পাইথনে মেমরির ভিউয়ের ঠিক কী


86

মেমোরিভিউতে ডকুমেন্টেশন চেক করা হচ্ছে:

মেমোরিভিউ অবজেক্টগুলি পাইথন কোডটিকে এমন কোনও সামগ্রীর অভ্যন্তরীণ ডেটা অ্যাক্সেস করার অনুমতি দেয় যা অনুলিপি ছাড়াই বাফার প্রোটোকল সমর্থন করে।

বর্গ স্মৃতিদর্শন ( আপত্তি )

একটি মেমরির ভিউ তৈরি করুন যা আপত্তিটিকে রেফারেন্স করে। আপত্তি অবশ্যই বাফার প্রোটোকল সমর্থন করবে। বাফার প্রোটোকল সমর্থন করে অন্তর্নির্মিত অবজেক্টগুলির মধ্যে বাইটস এবং বাইটারি অন্তর্ভুক্ত।

তারপরে আমাদের নমুনা কোড দেওয়া হবে:

>>> v = memoryview(b'abcefg')
>>> v[1]
98
>>> v[-1]
103
>>> v[1:4]
<memory at 0x7f3ddc9f4350>
>>> bytes(v[1:4])
b'bce'

উদ্ধৃতি শেষ, এখন আরও ঘুরে দেখুন:

>>> b = b'long bytes stream'
>>> b.startswith(b'long')
True
>>> v = memoryview(b)
>>> vsub = v[5:]
>>> vsub.startswith(b'bytes')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'memoryview' object has no attribute 'startswith'
>>> bytes(vsub).startswith(b'bytes')
True
>>> 

সুতরাং আমি উপরে থেকে কি সংগ্রহ:

অনুলিপি ছাড়াই বাফার অবজেক্টের অভ্যন্তরীণ তথ্য প্রকাশের জন্য আমরা একটি মেমরিভিউ অবজেক্ট তৈরি করি, তবে, বস্তুর সাথে দরকারী কোনও কিছু করার জন্য (বস্তুর সরবরাহিত পদ্ধতিগুলিতে কল করে), আমাদের একটি অনুলিপি তৈরি করতে হবে!

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

উপরোক্ত স্কিমটি সহ, আমি দেখতে পাচ্ছি না যে এটি উভয় পরিস্থিতিতে কীভাবে কার্যকর হতে পারে, যদি না কেউ আমাকে এখানে কী মিস করছি তা আমাকে ব্যাখ্যা না করে।

সম্পাদনা 1:

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

লোকেদের কাজের প্রস্তাব দেয়, উদাহরণস্বরূপ অনেকগুলি স্ট্রিং এবং রেজেক্স ফাংশন অবস্থান যুক্তি গ্রহণ করে যা পয়েন্টারকে অগ্রসর করার জন্য ব্যবহার করা যেতে পারে। এটির সাথে দুটি সমস্যা রয়েছে: প্রথমত এটি একটি কাজ, ত্রুটিগুলি কাটিয়ে ওঠার জন্য আপনাকে আপনার কোডিং স্টাইলটি পরিবর্তন করতে বাধ্য করা হবে এবং দ্বিতীয়: সমস্ত ফাংশনগুলির অবস্থানের আর্গুমেন্ট নেই, উদাহরণস্বরূপ রেজেক্স ফাংশন এবং startswithকরবেন, encode()/ decode()করবেন না।

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

সম্পাদনা 2:

একটি কোড নমুনা জিনিস পরিষ্কার করতে হবে। এটিই আমি করতে চাই এবং আমি যা মনে করেছি স্মৃতিদর্শনটি আমাকে প্রথম নজরে করার অনুমতি দেয়। আমি যে কার্যকারিতাটি সন্ধান করছি তার জন্য পিএমভিউ (সঠিক মেমরি ভিউ) ব্যবহার করতে দিন:

tokens = []
xlarge_str = get_string()
xlarge_str_view =  pmview(xlarge_str)

while True:
    token =  get_token(xlarge_str_view)
    if token: 
        xlarge_str_view = xlarge_str_view.vslice(len(token)) 
        # vslice: view slice: default stop paramter at end of buffer
        tokens.append(token)
    else:   
        break


9
রেফারেন্সযুক্ত প্রশ্নের উত্তর বিস্তারিত সরবরাহ করে না। তবুও কোনও শিক্ষার্থীর কোণ থেকে সম্ভাব্য সমস্যাগুলির উপর প্রশ্ন স্পর্শ করে না।
বাসেল শিশানী

উত্তর:


83

এর কারণগুলির memoryviewকার্যকর কারণ হ'ল bytes/ এর বিপরীতে অন্তর্নিহিত ডেটা অনুলিপি না করে কেটে ফেলা যায় str

উদাহরণস্বরূপ, নিম্নলিখিত খেলনা উদাহরণ গ্রহণ করুন।

import time
for n in (100000, 200000, 300000, 400000):
    data = 'x'*n
    start = time.time()
    b = data
    while b:
        b = b[1:]
    print 'bytes', n, time.time()-start

for n in (100000, 200000, 300000, 400000):
    data = 'x'*n
    start = time.time()
    b = memoryview(data)
    while b:
        b = b[1:]
    print 'memoryview', n, time.time()-start

আমার কম্পিউটারে, আমি পেতে

bytes 100000 0.200068950653
bytes 200000 0.938908100128
bytes 300000 2.30898690224
bytes 400000 4.27718806267
memoryview 100000 0.0100269317627
memoryview 200000 0.0208270549774
memoryview 300000 0.0303030014038
memoryview 400000 0.0403470993042

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

সম্পাদনা: নোট করুন এটি সিপিথনে করা হয়েছিল। পাইপিতে ৪.০.১ পর্যন্ত একটি ত্রুটি ছিল যা মেমোরিভিউগুলিকে চতুর্ভুজীয় পারফরম্যান্সের কারণে তৈরি করেছিল।


উদাহরণটি অজগর 3 তে কাজ করে নাTypeError: memoryview: a bytes-like object is required, not 'str'
ক্যালকুলাস

@ জোস printএকটি বিবৃতি হিসাবে পাইথন 3 এও কাজ করে না This এই কোডটি পাইথন 2 এর জন্য লেখা হয়েছিল, যদিও পাইথন 3 এর জন্য প্রয়োজনীয় পরিবর্তনগুলি বেশ নগণ্য।
এন্টিমোনি

পাইমিথন 3 এ ইউমি টাডা strপাইথন 2-তে সম্পূর্ণ আলাদা defined
hcnhcn012

4
এই উত্তরটি জিজ্ঞাসাবাদী হিসাবে আপনাকে "
বায়বাল

4
@ Citiz2020 যেমন আমার উদাহরণটি দেখায়, দক্ষভাবে মধ্যবর্তী ম্যানিপুলেশনগুলি করার জন্য এটি দরকারী, এমনকি যদি আপনি শেষ পর্যন্ত এটি বাইট অবজেক্টে অনুলিপি করেন তবেও।
এন্টিমোনি

59

memoryviewবস্তুগুলি দুর্দান্ত যখন আপনার বাইনারি ডেটাগুলির সাবসেটগুলি প্রয়োজন যা কেবল ইনডেক্সিং সমর্থন করে। অন্য API এ যাওয়ার জন্য টুকরোগুলি নিতে (এবং নতুন, সম্ভাব্যত বৃহত্তর তৈরি করা) বস্তুর পরিবর্তে আপনি কেবল কোনও memoryviewঅবজেক্ট নিতে পারেন ।

যেমন একটি এপিআই উদাহরণ structমডিউল হবে। bytesপ্যাকযুক্ত সি মানগুলি পার্স করার জন্য বৃহত অবজেক্টের একটি টুকরো পেরিয়ে যাওয়ার পরিবর্তে , আপনি memoryviewযে অঞ্চলটি থেকে মানগুলি বের করতে হবে তা কেবলমাত্র একটি অঞ্চলে চলে যান।

memoryviewবস্তুগুলি, প্রকৃতপক্ষে, structদেশীয়ভাবে আনপ্যাকিং সমর্থন করে; আপনি bytesএকটি স্লাইস সহ অন্তর্নিহিত অবজেক্টের একটি অঞ্চলকে লক্ষ্য করতে পারেন , তারপরে .cast()দীর্ঘতর পূর্ণসংখ্যার হিসাবে অন্তর্নিহিত বাইটগুলি 'বা' ভাসমান পয়েন্ট মানগুলি বা সংখ্যার এন-ডাইমেনশনাল তালিকাগুলির 'ব্যাখ্যা' করতে ব্যবহার করতে পারেন । এটি বাইটের আরও অনুলিপি তৈরি না করে খুব কার্যকরী বাইনারি ফাইল ফর্ম্যাট ব্যাখ্যা করে তোলে।


4
এবং যখন আপনার সূচীকরণের চেয়ে বেশি সমর্থন করে এমন সাবসেটগুলির প্রয়োজন হয় তখন আপনি কী করবেন ?!
বাসেল শিশানী

4
@BaselShishani: একটি ব্যবহার করবেন memoryview। আপনি তখন বাইনারি ডেটা নয়, পাঠ্যের সাথে কাজ করছেন।
মার্টিজন পিটারস

হ্যাঁ, পাঠ্যের সাথে ডিল করছেন। সুতরাং আমরা মেমোরিভিউ ব্যবহার করি না, বিকল্প আছে কি?
বাসেল শিশানী

আপনি কোন সমস্যার সমাধান করার চেষ্টা করছেন? যে সাবস্ট্রিংগুলি আপনার এত বড় পরীক্ষা করতে হবে?
মার্টিজন পিটারস

6
@ বাসেলশিশানী: মেমরির ভিউ টুকরো টুকরো করে ঠিক সেই অঞ্চলটি জুড়ে একটি নতুন স্মৃতিদর্শন।
মার্টিজন পিটারস

5

এখানে বোঝার ক্ষেত্রে যে সমস্যা রয়েছে তা আমাকে সরল করে দিন।

প্রশ্নকারী, আমার মতো, প্রত্যাশিত একটি মেমরির ভিউ তৈরি করতে সক্ষম হবেন যা বিদ্যমান অ্যারেগুলির একটি টুকরো নির্বাচন করে (উদাহরণস্বরূপ বাইট বা বাইটারি)। আমরা তাই এরকম কিছু প্রত্যাশা করেছিলাম:

desired_slice_view = memoryview(existing_array, start_index, end_index)

হায়, এ জাতীয় কোনও নির্মাতা নেই, এবং ডক্সগুলি পরিবর্তে কী করবে সে সম্পর্কে কোনও বিন্দু তোলে না।

মূলটি হ'ল আপনাকে প্রথমে একটি মেমরিভিউ তৈরি করতে হবে যা পুরো বিদ্যমান অ্যারেটি জুড়ে। সেই মেমোরিভিউ থেকে আপনি একটি দ্বিতীয় মেমরিভিউ তৈরি করতে পারেন যা বিদ্যমান অ্যারেগুলির একটি টুকরো coversেকে রাখে, এটির মতো:

whole_view = memoryview(existing_array)
desired_slice_view = whole_view[10:20]

সংক্ষেপে, প্রথম লাইনের উদ্দেশ্য কেবল এমন কোনও বিষয় সরবরাহ করা যার স্লাইস বাস্তবায়ন (ডেন্ডার-গেটাইটেম) একটি স্মৃতিদর্শন দেয়।

এটিকে অবাস্তব বলে মনে হতে পারে তবে কেউ কেউ এটি বেশ কয়েকটি উপায়ে যুক্তিযুক্ত করতে পারেন:

  1. আমাদের কাঙ্ক্ষিত আউটপুটটি একটি স্মৃতিদর্শন যা কোনও কিছুর টুকরো। সাধারণত আমরা একই ধরণের একটি অবজেক্ট থেকে স্লাইস অপারেটর [10:20] ব্যবহার করে একটি কাটা বস্তু পাই। সুতরাং আশা করার কিছু কারণ আছে যে আমাদের একটি মেমরির ভিউ থেকে আমাদের কাঙ্ক্ষিত_স্লাইস_ভিউ পাওয়া দরকার, এবং সেইজন্য প্রথম পদক্ষেপটি সম্পূর্ণ অন্তর্নিহিত অ্যারের একটি স্মৃতিদর্শন পাওয়া।

  2. শুরু এবং শেষ যুক্তিগুলির সাথে মেমরিভিউ কনস্ট্রাক্টরের নিখুঁত এক্সপ্রেশন বিবেচনা করে ব্যর্থ হয় যে স্লাইস স্পেসিফিকেশনের সত্যিকারের স্বাভাবিক স্লাইস অপারেটরের সমস্ত প্রকাশ (যেমন [3 :: 2] বা [: -4] ইত্যাদি) সহ প্রয়োজন needs ওয়ান-লাইনার কনস্ট্রাক্টরটিতে বিদ্যমান (এবং বোঝা) অপারেটরটি ব্যবহার করার কোনও উপায় নেই। আপনি এটিকে বিদ্যমান_ অ্যারে আর্গুমেন্টের সাথে সংযুক্ত করতে পারবেন না কারণ এটি মেমরিভিউ কনস্ট্রাক্টরকে কিছু স্লাইস প্যারামিটার না বলার পরিবর্তে সেই অ্যারের একটি টুকরো তৈরি করবে। এবং আপনি অপারেটরটিকে নিজেই একটি আর্গুমেন্ট হিসাবে ব্যবহার করতে পারবেন না, কারণ এটি অপারেটর এবং মান বা অবজেক্ট নয়।

সম্ভবত, একটি মেমোরিভিউ নির্মাণকারী একটি স্লাইস বস্তু নিতে পারে:

desired_slice_view = memoryview(existing_array, slice(1, 5, 2) )

... তবে এটি খুব সন্তোষজনক নয়, যেহেতু স্লাইস অপারেটরের স্বরলিপিটি বিবেচনা করার ক্ষেত্রে ব্যবহারকারীরা স্লাইস অবজেক্ট এবং তার নির্মাতার পরামিতিগুলির অর্থ কী তা সম্পর্কে জানতে হবে।


4

পাইথন 3 কোডটি এখানে।

#!/usr/bin/env python3

import time
for n in (100000, 200000, 300000, 400000):
    data = b'x'*n
    start = time.time()
    b = data
    while b:
        b = b[1:]
    print ('bytes {:d} {:f}'.format(n,time.time()-start))

for n in (100000, 200000, 300000, 400000):
    data = b'x'*n
    start = time.time()
    b = memoryview(data)
    while b:
        b = b[1:]
    print ('memview {:d} {:f}'.format(n,time.time()-start))

1

অ্যান্টিমনি দ্বারা দুর্দান্ত উদাহরণ। আসলে, পাইথন 3 এ, আপনি ডেটা = 'x' * n কে ডেটা = বাইটস (এন) দ্বারা প্রতিস্থাপন করতে পারেন এবং নীচে হিসাবে স্টেটমেন্টগুলি মুদ্রণের জন্য প্রথম বন্ধনী রাখতে পারেন:

import time
for n in (100000, 200000, 300000, 400000):
    #data = 'x'*n
    data = bytes(n)
    start = time.time()
    b = data
    while b:
        b = b[1:]
    print('bytes', n, time.time()-start)

for n in (100000, 200000, 300000, 400000):
    #data = 'x'*n
    data = bytes(n)
    start = time.time()
    b = memoryview(data)
    while b:
        b = b[1:]
    print('memoryview', n, time.time()-start)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.