ปรับปรุงการสุ่มตัวอย่างต่ำสุด/สูงสุด

ฉันมีอาร์เรย์ขนาดใหญ่ (ประมาณ 100 ล้านจุด) ที่ฉันต้องลงจุดแบบโต้ตอบ ฉันกำลังใช้ Matplotlib การพล็อตอาร์เรย์ตามที่เป็นอยู่จะช้ามากและเป็นการสิ้นเปลืองเนื่องจากคุณไม่สามารถเห็นภาพหลายจุดได้

ดังนั้นฉันจึงสร้างฟังก์ชันการทศนิยมขั้นต่ำ/สูงสุดที่ฉันเชื่อมโยงกับการเรียกกลับ 'xlim_changed' ของแกน ฉันเลือกใช้วิธีต่ำสุด/สูงสุด เนื่องจากข้อมูลมีการเพิ่มขึ้นอย่างรวดเร็วซึ่งฉันไม่อยากพลาดเพียงแค่ดูข้อมูลทีละขั้นตอน มี Wrapper อื่นๆ ที่ครอบตัดจนถึงขีดจำกัด x และข้ามการประมวลผลภายใต้เงื่อนไขบางประการ แต่ส่วนที่เกี่ยวข้องอยู่ด้านล่าง:

def min_max_downsample(x,y,num_bins):
    """ Break the data into num_bins and returns min/max for each bin"""
    pts_per_bin = x.size // num_bins    

    #Create temp to hold the reshaped & slightly cropped y
    y_temp = y[:num_bins*pts_per_bin].reshape((num_bins, pts_per_bin))
    y_out      = np.empty((num_bins,2))
    #Take the min/max by rows.
    y_out[:,0] = y_temp.max(axis=1)
    y_out[:,1] = y_temp.min(axis=1)
    y_out = y_out.ravel()

    #This duplicates the x-value for each min/max y-pair
    x_out = np.empty((num_bins,2))
    x_out[:] = x[:num_bins*pts_per_bin:pts_per_bin,np.newaxis]
    x_out = x_out.ravel()
    return x_out, y_out

ใช้งานได้ค่อนข้างดีและเร็วเพียงพอ (~ 80ms บน 1e8 คะแนน & 2k bins) มีความล่าช้าน้อยมากเนื่องจากมีการคำนวณใหม่และอัปเดตข้อมูล x & y ของบรรทัดเป็นระยะ

อย่างไรก็ตาม ข้อร้องเรียนเดียวของฉันอยู่ใน x-data โค้ดนี้จำลองค่า x ของขอบด้านซ้ายของแต่ละถังขยะ และจะไม่ส่งคืนตำแหน่ง x ที่แท้จริงของคู่ค่า y min/max โดยทั่วไปฉันจะตั้งค่าจำนวนถังขยะให้เพิ่มความกว้างพิกเซลของแกนเป็นสองเท่า คุณจึงมองไม่เห็นความแตกต่างเพราะถังขยะมีขนาดเล็กมาก...แต่ฉันรู้ว่ามันอยู่ตรงนั้น... และมันทำให้ฉันรำคาญ

ดังนั้นลองหมายเลข 2 ซึ่งจะคืนค่า x จริงสำหรับคู่ต่ำสุด/สูงสุดทุกคู่ แต่ช้ากว่าประมาณ 5 เท่า

def min_max_downsample_v2(x,y,num_bins):
    pts_per_bin = x.size // num_bins
    #Create temp to hold the reshaped & slightly cropped y
    y_temp = y[:num_bins*pts_per_bin].reshape((num_bins, pts_per_bin))
    #use argmax/min to get column locations
    cc_max = y_temp.argmax(axis=1)
    cc_min = y_temp.argmin(axis=1)    
    rr = np.arange(0,num_bins)
    #compute the flat index to where these are
    flat_max = cc_max + rr*pts_per_bin
    flat_min = cc_min + rr*pts_per_bin
    #Create a boolean mask of these locations
    mm_mask  = np.full((x.size,), False)
    mm_mask[flat_max] = True
    mm_mask[flat_min] = True  
    x_out = x[mm_mask]    
    y_out = y[mm_mask]  
    return x_out, y_out

ใช้เวลาประมาณ 400+ ms บนเครื่องของฉันซึ่งค่อนข้างสังเกตได้ชัดเจน โดยพื้นฐานแล้วคำถามของฉันคือมีวิธีใดที่จะเร็วขึ้นและให้ผลลัพธ์เหมือนเดิมหรือไม่ คอขวดส่วนใหญ่อยู่ในฟังก์ชัน numpy.argmin และ numpy.argmax ซึ่งช้ากว่า numpy.min และ numpy.max เล็กน้อย

คำตอบอาจเป็นแค่ใช้งานเวอร์ชัน #1 เนื่องจากรูปลักษณ์ไม่สำคัญ หรืออาจลองเร่งความเร็วบางอย่างเช่น cython (ซึ่งฉันไม่เคยใช้)

FYI โดยใช้ Python 3.6.4 บน Windows ... ตัวอย่างการใช้งานจะเป็นดังนี้:

x_big = np.linspace(0,10,100000000)
y_big = np.cos(x_big )
x_small, y_small = min_max_downsample(x_big ,y_big ,2000) #Fast but not exactly correct.
x_small, y_small = min_max_downsample_v2(x_big ,y_big ,2000) #correct but not exactly fast.

person Aero Engy    schedule 30.01.2019    source แหล่งที่มา
comment
จากคำถามของคุณ ฉันคิดว่ามันถูกเรียกหลายครั้งใช่ไหม หากเป็นกรณีนี้ คุณสามารถเพิ่มความเร็วได้อย่างมากโดยใช้วิธี set_data ของบรรทัดที่มีอยู่ใน matplotlib   -  person user2699    schedule 31.01.2019
comment
@ user2699 ใช่นั่นคือสิ่งที่ฉันทำกับผลลัพธ์ของฟังก์ชันนี้ สิ่งที่ฉันโพสต์คือคอขวดหลักเพราะว่ามันถูกเรียกบ่อยเมื่อฉันเลื่อนดูไปรอบๆ อย่างไรก็ตาม ฉันได้เปลี่ยนส่วนด้านนอกของโค้ดนี้ (ไม่แสดง) เพื่อให้มันถูกเรียกน้อยลงโดยการบัฟเฟอร์วันที่ให้เกินขีดจำกัด x ของโครงเรื่อง ดังนั้นจึงต้องคำนวณใหม่เมื่อคุณแพนเกินขีดจำกัดที่กำหนดเท่านั้น นั่นช่วยให้ผู้ใช้ได้รับประสบการณ์ที่ดีเล็กน้อย   -  person Aero Engy    schedule 01.02.2019
comment
ฉันประสบปัญหาที่คล้ายกัน (แม้ว่าจะมีน้อยกว่า 100 ล้านคะแนน) และสามารถรวมส่วนหนึ่งของโค้ดที่ฉันเขียนที่เกี่ยวข้องได้ สิ่งที่ฉันพบว่ามีประสิทธิผลมากที่สุดคือการแคชสัญญาณดาวน์แซมเพิล และดาวน์แซมเพิลเฉพาะเมื่อสร้างความแตกต่างอย่างมากในมุมมอง ตราบใดที่ข้อมูล (ค่อนข้าง) สุ่มตัวอย่างสม่ำเสมอโดยมีจุด x ที่แน่นอนก็ไม่เกี่ยวข้อง   -  person user2699    schedule 01.02.2019
comment
@ user2699 ฉันอยากจะดูว่าคุณแคชอย่างไร ฉันกำลังทำอะไรแบบนั้นตอนนี้ โดยทั่วไปจะโหลดข้อมูลที่เกินขีดจำกัด x ด้วยจำนวนหนึ่งและทำให้ความหนาแน่นของจุดเกินความจำเป็นเล็กน้อย จากนั้นจะเรียกใช้การคำนวณใหม่เฉพาะเมื่อคุณขยายเข้าไปมากพอที่ความหนาแน่นของจุดจะลดลง หรือหากคุณแพนเกินขอบบัฟเฟอร์ ฉันอยากจะดูว่าคนอื่นทำอย่างไรตั้งแต่ฉันสร้างมันขึ้นมาส่วนใหญ่แล้วและฉันรู้ว่ายังมีช่องโหว่อยู่บ้าง ของฉันจะคำนวณใหม่โดยไม่จำเป็นในบางครั้ง   -  person Aero Engy    schedule 01.02.2019
comment
ฉันได้รวมไว้ในคำตอบแล้ว แจ้งให้เราทราบว่ามันทำงานอย่างไรสำหรับคุณ มันค่อนข้างแข็งแกร่งในการใช้งานของฉัน แต่อาจมีปัญหาที่ฉันพลาดไป   -  person user2699    schedule 01.02.2019


คำตอบ (4)


ฉันจัดการเพื่อให้ได้ประสิทธิภาพที่ดีขึ้นโดยใช้เอาต์พุตของ arg(min|max) โดยตรงเพื่อสร้างดัชนีอาร์เรย์ข้อมูล ซึ่งต้องเสียค่าใช้จ่ายในการเรียก np.sort เพิ่มเติม แต่แกนที่จะจัดเรียงมีเพียงสององค์ประกอบเท่านั้น (ดัชนีต่ำสุด / สูงสุด) และอาร์เรย์โดยรวมค่อนข้างเล็ก (จำนวนถังขยะ):

def min_max_downsample_v3(x, y, num_bins):
    pts_per_bin = x.size // num_bins

    x_view = x[:pts_per_bin*num_bins].reshape(num_bins, pts_per_bin)
    y_view = y[:pts_per_bin*num_bins].reshape(num_bins, pts_per_bin)
    i_min = np.argmin(y_view, axis=1)
    i_max = np.argmax(y_view, axis=1)

    r_index = np.repeat(np.arange(num_bins), 2)
    c_index = np.sort(np.stack((i_min, i_max), axis=1)).ravel()

    return x_view[r_index, c_index], y_view[r_index, c_index]

ฉันตรวจสอบกำหนดเวลาสำหรับตัวอย่างของคุณและฉันได้รับ:

  • min_max_downsample_v1: 110 ms ± 5 ms
  • min_max_downsample_v2: 240 ms ± 8.01 ms
  • min_max_downsample_v3: 164 ms ± 1.23 ms

ฉันยังตรวจสอบการกลับมาโดยตรงหลังจากการโทรไปที่ arg(min|max) และผลลัพธ์ก็เท่ากับ 164 ms นั่นคือไม่มีค่าใช้จ่ายจริงหลังจากนั้นอีกต่อไป

person a_guest    schedule 31.01.2019
comment
เคล็ดลับการจัดทำดัชนีที่ดีและการหลีกเลี่ยงหน้ากาก ฉันไม่คิดว่า sort จะเร็วกว่านี้ ฉันได้รับ 87, 421 และ 338ms สำหรับ v1, v2 และ v3 ฉันต้องการให้มันกลับไปที่ช่วงต่ำกว่า 100 มิลลิวินาที แต่ฉันจะปรับปรุงทุกอย่างเท่าที่ทำได้ อย่างไรก็ตาม ฉันสังเกตเห็นว่าโค้ดด้านบนจะเกิดข้อผิดพลาดหาก x ไม่ใช่องค์ประกอบ num_bin*pts_per_bin ยาว ... ซึ่งมักจะเป็นเช่นนั้น นั่นคือเหตุผลที่ฉันทำ y_temp = y[:num_bins*pts_per_bin].reshape - person Aero Engy; 31.01.2019
comment
@AeroEngy คุณพูดถูกรวมไว้ด้วย คุณสามารถตรวจสอบผลลัพธ์ของเวลาเมื่อคุณกลับมาทันทีหลังจากการโทร arg(min|max) เพื่อดูการมีส่วนร่วมจากการโทรต่อไปนี้ไปที่ sort ได้หรือไม่ มีหลายวิธีในการรวมดัชนี แต่ในเครื่องของฉันมันไม่สำคัญเลย - person a_guest; 31.01.2019
comment
ฉันเรียกใช้ผ่านโปรไฟล์และได้ทั้งหมด 339ms (142ms สำหรับ argmax และ 196ms สำหรับ argmin) sort นั้นไม่สำคัญที่ 45us ฉันคิดว่าคุณได้ลบค่าใช้จ่ายอื่นๆ ทั้งหมดแล้ว หากต้องการดำเนินการให้เร็วขึ้นจะต้องใช้แนวทางใหม่ที่ไม่รู้จัก - person Aero Engy; 31.01.2019
comment
@AeroEngy บางทีคุณอาจปฏิบัติตามแนวทางนี้เพื่อรวบรวมฟังก์ชัน argmin_max ของคุณเองที่คำนวณ argmin และ argmax ในครั้งเดียวแทน ของการวนซ้ำอาเรย์สองครั้ง สำหรับอาร์เรย์ที่มีขนาดดังกล่าว ควรวัดความแตกต่างได้ - person a_guest; 31.01.2019
comment
ขอบคุณสำหรับลิงค์! ก่อนอื่นฉันจะลองทำ single pass argminmax เวอร์ชัน numba เพื่อดูว่ามันเปรียบเทียบกันอย่างไร ฉันยังไม่เคยใช้ numba หรือ cython มาก่อน แต่มันดูน่าสนใจ ฉันเพิ่งเปลี่ยนมาใช้ Python จาก Matlab ดังนั้นฉันจึงคุ้นเคยกับการเร่งความเร็วโค้ดด้วย MEX ซึ่งดูเหมือนเป็นแนวคิดที่คล้ายกัน - person Aero Engy; 01.02.2019
comment
การใช้คำตอบของคุณ + ลิงก์นั้นช่วยให้ฉันสร้างเวอร์ชันที่ดีและรวดเร็วด้วย numba ฉันโพสต์มันเป็นคำตอบแยกต่างหากสำหรับคนรุ่นหลัง แต่จะยกเว้นคำตอบของคุณเนื่องจากคุณนำฉันไปสู่เส้นทางที่ถูกต้อง ขอบคุณอีกครั้ง! - person Aero Engy; 01.02.2019

ดังนั้น นี่ไม่ได้กล่าวถึงการเร่งความเร็วฟังก์ชันเฉพาะที่เป็นปัญหา แต่แสดงวิธีการสองสามวิธีในการวางแผนเส้นที่มีจุดจำนวนมากค่อนข้างมีประสิทธิภาพ วิธีนี้จะถือว่าจุด x ได้รับการเรียงลำดับและสุ่มตัวอย่างสม่ำเสมอ (หรือใกล้เคียงกัน)

ติดตั้ง

from pylab import *

นี่คือฟังก์ชันที่ฉันชอบซึ่งจะลดจำนวนคะแนนโดยการสุ่มเลือกหนึ่งจุดในแต่ละช่วงเวลา ไม่รับประกันว่าจะแสดงทุกจุดสูงสุดของข้อมูล แต่ไม่มีปัญหามากเท่ากับการทำลายข้อมูลโดยตรงและรวดเร็ว

def calc_rand(y, factor):
    split = y[:len(y)//factor*factor].reshape(-1, factor)
    idx = randint(0, split.shape[-1], split.shape[0])
    return split[arange(split.shape[0]), idx]

และนี่คือค่าต่ำสุดและสูงสุดเพื่อดูซองสัญญาณ

def calc_env(y, factor):
    """
    y : 1D signal
    factor : amount to reduce y by (actually returns twice this for min and max)
    Calculate envelope (interleaved min and max points) for y
    """
    split = y[:len(y)//factor*factor].reshape(-1, factor)
    upper = split.max(axis=-1)
    lower = split.min(axis=-1)
    return c_[upper, lower].flatten()

ฟังก์ชันต่อไปนี้สามารถใช้อย่างใดอย่างหนึ่งเหล่านี้ และใช้เพื่อลดข้อมูลที่ถูกดึงออกมา จำนวนคะแนนที่ได้รับจริงคือ 5,000 คะแนนโดยค่าเริ่มต้น ซึ่งน่าจะเกินความละเอียดของจอภาพอย่างมาก ข้อมูลจะถูกแคชหลังจากที่ลดลง หน่วยความจำอาจเป็นปัญหา โดยเฉพาะอย่างยิ่งกับข้อมูลจำนวนมาก แต่ไม่ควรเกินปริมาณที่สัญญาณดั้งเดิมต้องการ

def plot_bigly(x, y, *, ax=None, M=5000, red=calc_env, **kwargs):
    """
    x : the x data
    y : the y data
    ax : axis to plot on
    M : The maximum number of line points to display at any given time
    kwargs : passed to line
    """
    assert x.shape == y.shape, "x and y data must have same shape!"
    if ax is None:
        ax = gca()

    cached = {}

    # Setup line to be drawn beforehand, note this doesn't increment line properties so
    #  style needs to be passed in explicitly
    line = plt.Line2D([],[], **kwargs)
    def update(xmin, xmax):
        """
        Update line data

        precomputes and caches entire line at each level, so initial
        display may be slow but panning and zooming should speed up after that
        """
        # Find nearest power of two as a factor to downsample by
        imin = max(np.searchsorted(x, xmin)-1, 0)
        imax = min(np.searchsorted(x, xmax) + 1, y.shape[0])
        L = imax - imin + 1
        factor = max(2**int(round(np.log(L/M) / np.log(2))), 1)

        # only calculate reduction if it hasn't been cached, do reduction using nearest cached version if possible
        if factor not in cached:
            cached[factor] = red(y, factor=factor)

        ## Make sure lengths match correctly here, by ensuring at least
        #   "factor" points for each x point, then matching y length
        #  this assumes x has uniform sample spacing - but could be modified
        newx = x[imin:imin + ((imax-imin)//factor)* factor:factor]
        start = imin//factor
        newy = cached[factor][start:start + newx.shape[-1]]
        assert newx.shape == newy.shape, "decimation error {}/{}!".format(newx.shape, newy.shape)

        ## Update line data
        line.set_xdata(newx)
        line.set_ydata(newy)

    update(x[0], x[-1])
    ax.add_line(line)
    ## Manually update limits of axis, as adding line doesn't do this
    #   if drawing multiple lines this can quickly slow things down, and some
    #   sort of check should be included to prevent unnecessary changes in limits
    #   when a line is first drawn.
    ax.set_xlim(min(ax.get_xlim()[0], x[0]), max(ax.get_xlim()[1], x[1]))
    ax.set_ylim(min(ax.get_ylim()[0], np.min(y)), max(ax.get_ylim()[1], np.max(y)))

    def callback(*ignore):
        lims = ax.get_xlim()
        update(*lims)

    ax.callbacks.connect('xlim_changed', callback)

    return [line]

นี่คือรหัสทดสอบบางส่วน

L=int(100e6)
x=linspace(0,1,L)
y=0.1*randn(L)+sin(2*pi*18*x)
plot_bigly(x,y, red=calc_env)

บนเครื่องของฉันสิ่งนี้แสดงเร็วมาก การซูมมีความล่าช้าเล็กน้อย โดยเฉพาะอย่างยิ่งเมื่อมีจำนวนมาก การแพนไม่มีปัญหา การใช้การเลือกแบบสุ่มแทนค่าต่ำสุดและสูงสุดนั้นค่อนข้างเร็วกว่าเล็กน้อย และมีปัญหาเฉพาะในระดับการซูมที่สูงมากเท่านั้น

person user2699    schedule 31.01.2019
comment
ขอบคุณ ฉันไม่ได้พิจารณาที่จะจัดเก็บเวอร์ชันตัวอย่างลงด้วยปัจจัยที่แตกต่างกันเช่นนั้นเพื่อใช้ในภายหลัง ฉันอาจจะยืมมันมารวมไว้ในกิจวัตรประจำวันของฉัน - person Aero Engy; 01.02.2019

แก้ไข: เพิ่ม Parallel=True ถึง numba ... เร็วขึ้นอีก

ฉันลงเอยด้วยการสร้างไฮบริดของรูทีน argmin+max ผ่านครั้งเดียวและการจัดทำดัชนีที่ปรับปรุงจากคำตอบของ @a_guest และลิงก์ไปยัง คำถามสูงสุดขั้นต่ำที่เกี่ยวข้องพร้อมกันนี้

เวอร์ชันนี้ส่งคืนค่า x ที่ถูกต้องสำหรับแต่ละคู่ min/max y แต่ละคู่ และต้องขอบคุณ numba ที่ทำให้เร็วกว่าเวอร์ชัน "เร็วแต่ไม่ถูกต้อง" เล็กน้อย

from numba import jit, prange
@jit(parallel=True)
def min_max_downsample_v4(x, y, num_bins):
    pts_per_bin = x.size // num_bins
    x_view = x[:pts_per_bin*num_bins].reshape(num_bins, pts_per_bin)
    y_view = y[:pts_per_bin*num_bins].reshape(num_bins, pts_per_bin)    
    i_min = np.zeros(num_bins,dtype='int64')
    i_max = np.zeros(num_bins,dtype='int64')

    for r in prange(num_bins):
        min_val = y_view[r,0]
        max_val = y_view[r,0]
        for c in range(pts_per_bin):
            if y_view[r,c] < min_val:
                min_val = y_view[r,c]
                i_min[r] = c
            elif y_view[r,c] > max_val:
                max_val = y_view[r,c]
                i_max[r] = c                
    r_index = np.repeat(np.arange(num_bins), 2)
    c_index = np.sort(np.stack((i_min, i_max), axis=1)).ravel()        
    return x_view[r_index, c_index], y_view[r_index, c_index]

การเปรียบเทียบความเร็วโดยใช้ timeit แสดงว่าโค้ด numba เร็วกว่าประมาณ 2.6 เท่า และให้ผลลัพธ์ที่ดีกว่า v1 มันเร็วกว่าการทำ argmin & argmax ของ numpy มากกว่า 10 เท่าเล็กน้อยในซีรีย์

%timeit min_max_downsample_v1(x_big ,y_big ,2000)
96 ms ± 2.46 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit min_max_downsample_v2(x_big ,y_big ,2000)
507 ms ± 4.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit min_max_downsample_v3(x_big ,y_big ,2000)
365 ms ± 1.27 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit min_max_downsample_v4(x_big ,y_big ,2000)
36.2 ms ± 487 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
person Aero Engy    schedule 01.02.2019
comment
ที่น่ากลัว! ฉันไม่รู้ว่าด้วย Numba คุณสามารถทำทุกอย่างด้วย Python โค้ดที่ได้นั้นอ่านง่ายสุด ๆ และบูรณาการได้เพียงแค่ใช้มัณฑนากรเท่านั้น ขอบคุณสำหรับการแบ่งปันรหัส :-) - person a_guest; 01.02.2019
comment
@a_guest ฉันประหลาดใจมากกับความง่ายในการใช้งาน ก่อนที่ฉันจะเพิ่ม prange และ parallel=True เวลาคือ ~86ms ซึ่งทำให้ฉันมีความสุข หลังจากเพิ่มสิ่งเหล่านั้นเข้าไปแล้ว มันน่าทึ่งมากว่ามันเร็วกว่า Numpy เพียงอย่างเดียวมากแค่ไหน ใช้เวลาประมาณ 1/2 วินาทีหรือประมาณนั้นในการคอมไพล์ในการใช้งานครั้งแรก แต่สิ่งนี้ถูกเรียกบ่อยมาก ดังนั้นมันจึงไม่สำคัญเลย - person Aero Engy; 01.02.2019

คุณลองใช้ pyqtgraph เพื่อการวางแผนเชิงโต้ตอบหรือไม่? มันตอบสนองได้ดีกว่า matplotlib

เคล็ดลับหนึ่งที่ฉันใช้สำหรับการลดขนาดตัวอย่างคือการใช้ array_split และคำนวณขั้นต่ำและสูงสุดสำหรับการแยก การแยกจะกระทำตามจำนวนตัวอย่างต่อพิกเซลของพื้นที่ลงจุด

person danielhrisca    schedule 03.02.2019
comment
ฉันลองใช้ pyqtgraph แล้วดูเหมือนว่าจะเร็วขึ้น นอกจากนี้ยังมีรูทีนการสุ่มตัวอย่างขั้นต่ำ / สูงสุดในตัวซึ่งดีมาก อย่างไรก็ตาม ฉันไม่ชอบกลไกการพล็อตเรื่องเลย หากคุณอ่านคำถาม/คำตอบ คุณจะเห็นว่าฉันกำลังทำค่าต่ำสุด/สูงสุดด้วยการปรับรูปร่างใหม่แทนที่จะเป็น array_split reshape มีค่าใช้จ่ายน้อยกว่ามากเนื่องจากส่งคืนมุมมองเดียวของอาร์เรย์ array_split ดูเหมือนว่าจะคืนค่าการดู list การแยกอาร์เรย์องค์ประกอบ 1e8 ใช้เวลาประมาณ ~ 2ms ในขณะที่การปรับรูปร่างใหม่คือ ~ 520ns ... ดังนั้นการปรับรูปร่างใหม่จึงเร็วขึ้น 3800x บนเครื่องของฉัน - person Aero Engy; 04.02.2019
comment
array_split นั้นช้ากว่ามากอย่างแน่นอนจากนั้นจึงปรับรูปร่างใหม่ - person danielhrisca; 05.02.2019