سؤال

لدي ملفان (2 جيجابايت لكل منهما) على القرص الصلب وأريد مقارنتها مع بعضها البعض:

  • نسخ الملفات الأصلية مع مستكشف Windows يأخذ تقريبا. 2-4 دقائق (التي هي القراءة والكتابة - على نفس القرص المادي والمنطقي).
  • قراءة مع java.io.FileInputStream يستغرق مرتين ومقارنة صفائف البايت على أساس البايت لكل بايت 20 دقيقة.
  • java.io.BufferedInputStream المخزن المؤقت هو 64 كيلو بايت، تتم قراءة الملفات في قطع ثم مقارنة.
  • تتم المقارنة هي حلقة ضيقة مثل

    int numRead = Math.min(numRead[0], numRead[1]);
    for (int k = 0; k < numRead; k++)
    {
       if (buffer[1][k] != buffer[0][k])
       {
          return buffer[0][k] - buffer[1][k];
       }
    }
    

ماذا يمكنني أن أفعل لتسريع هذا؟ هو NIO من المفترض أن تكون أسرع ثم تيارات عادية؟ هل Java غير قادر على استخدام تقنيات DMA / SATA ويقوم ببعض المكالمات بطيئة OS-API بدلا من ذلك؟

تعديل:
شكرا على الإجابات. فعلت بعض التجارب بناء عليهم. كما أظهر أندرياس

تيارات أو nio النهج لا تختلف كثيرا.
أكثر أهمية هو حجم المخزن المؤقت الصحيح.

يتم تأكيد ذلك من قبل تجربتي الخاصة. كما تتم قراءة الملفات في قطع كبيرة، حتى المخازن المؤقتة إضافية (BufferedInputStream) لا تعطي أي شيء. من الممكن تحسين المقارنة، وحصلت على أفضل النتائج مع عدم انتهاء 32 أضعاف، ولكن إنفاق الوقت مقارنة صغيرة مقارنة بقراءة القرص، وبالتالي فإن السرعة صغيرة. يبدو أنه لا يوجد شيء يمكنني القيام به ؛-(

هل كانت مفيدة؟

المحلول

حاولت ثلاث طرق مختلفة لمقارنة ملفتين متطابقتين 3،8 جيجابايت مع أحجام مخزعة بين 8 كيلو بايت و 1 ميغابايت. أول طريقة الأولى تستخدم اثنين فقط من تدفقات الإدخال المخزنة مؤقتا

يستخدم النهج الثاني دراما يقرأ في مؤشراتين مختلفين ويقارن في الثلث. لقد حصلت هذا على إنتاجية أعلى قليلا على حساب استخدام وحدة المعالجة المركزية العالية. يستغرق إدارة Threadpool الكثير من المهام قصيرة التشغيل.

يستخدم النهج الثالث NIO، كما تم إرساله بواسطة Laginimaineb

كما ترون، فإن النهج العام لا يختلف كثيرا. أكثر أهمية هو حجم المخزن المؤقت الصحيح.

ما هو الغريب الذي قرأته 1 بايت أقل استخدام المواضيع. لم أستطع اكتشاف الخطأ صعبة.

comparing just with two streams
I was equal, even after 3684070360 bytes and reading for 704813 ms (4,98MB/sec * 2) with a buffer size of 8 kB
I was equal, even after 3684070360 bytes and reading for 578563 ms (6,07MB/sec * 2) with a buffer size of 16 kB
I was equal, even after 3684070360 bytes and reading for 515422 ms (6,82MB/sec * 2) with a buffer size of 32 kB
I was equal, even after 3684070360 bytes and reading for 534532 ms (6,57MB/sec * 2) with a buffer size of 64 kB
I was equal, even after 3684070360 bytes and reading for 422953 ms (8,31MB/sec * 2) with a buffer size of 128 kB
I was equal, even after 3684070360 bytes and reading for 793359 ms (4,43MB/sec * 2) with a buffer size of 256 kB
I was equal, even after 3684070360 bytes and reading for 746344 ms (4,71MB/sec * 2) with a buffer size of 512 kB
I was equal, even after 3684070360 bytes and reading for 669969 ms (5,24MB/sec * 2) with a buffer size of 1024 kB
comparing with threads
I was equal, even after 3684070359 bytes and reading for 602391 ms (5,83MB/sec * 2) with a buffer size of 8 kB
I was equal, even after 3684070359 bytes and reading for 523156 ms (6,72MB/sec * 2) with a buffer size of 16 kB
I was equal, even after 3684070359 bytes and reading for 527547 ms (6,66MB/sec * 2) with a buffer size of 32 kB
I was equal, even after 3684070359 bytes and reading for 276750 ms (12,69MB/sec * 2) with a buffer size of 64 kB
I was equal, even after 3684070359 bytes and reading for 493172 ms (7,12MB/sec * 2) with a buffer size of 128 kB
I was equal, even after 3684070359 bytes and reading for 696781 ms (5,04MB/sec * 2) with a buffer size of 256 kB
I was equal, even after 3684070359 bytes and reading for 727953 ms (4,83MB/sec * 2) with a buffer size of 512 kB
I was equal, even after 3684070359 bytes and reading for 741000 ms (4,74MB/sec * 2) with a buffer size of 1024 kB
comparing with nio
I was equal, even after 3684070360 bytes and reading for 661313 ms (5,31MB/sec * 2) with a buffer size of 8 kB
I was equal, even after 3684070360 bytes and reading for 656156 ms (5,35MB/sec * 2) with a buffer size of 16 kB
I was equal, even after 3684070360 bytes and reading for 491781 ms (7,14MB/sec * 2) with a buffer size of 32 kB
I was equal, even after 3684070360 bytes and reading for 317360 ms (11,07MB/sec * 2) with a buffer size of 64 kB
I was equal, even after 3684070360 bytes and reading for 643078 ms (5,46MB/sec * 2) with a buffer size of 128 kB
I was equal, even after 3684070360 bytes and reading for 865016 ms (4,06MB/sec * 2) with a buffer size of 256 kB
I was equal, even after 3684070360 bytes and reading for 716796 ms (4,90MB/sec * 2) with a buffer size of 512 kB
I was equal, even after 3684070360 bytes and reading for 652016 ms (5,39MB/sec * 2) with a buffer size of 1024 kB

الرمز المستخدم:

import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.concurrent.*;

public class FileCompare {

    private static final int MIN_BUFFER_SIZE = 1024 * 8;
    private static final int MAX_BUFFER_SIZE = 1024 * 1024;
    private String fileName1;
    private String fileName2;
    private long start;
    private long totalbytes;

    @Before
    public void createInputStream() {
        fileName1 = "bigFile.1";
        fileName2 = "bigFile.2";
    }

    @Test
    public void compareTwoFiles() throws IOException {
        System.out.println("comparing just with two streams");
        int currentBufferSize = MIN_BUFFER_SIZE;
        while (currentBufferSize <= MAX_BUFFER_SIZE) {
            compareWithBufferSize(currentBufferSize);
            currentBufferSize *= 2;
        }
    }

    @Test
    public void compareTwoFilesFutures() 
            throws IOException, ExecutionException, InterruptedException {
        System.out.println("comparing with threads");
        int myBufferSize = MIN_BUFFER_SIZE;
        while (myBufferSize <= MAX_BUFFER_SIZE) {
            start = System.currentTimeMillis();
            totalbytes = 0;
            compareWithBufferSizeFutures(myBufferSize);
            myBufferSize *= 2;
        }
    }

    @Test
    public void compareTwoFilesNio() throws IOException {
        System.out.println("comparing with nio");
        int myBufferSize = MIN_BUFFER_SIZE;
        while (myBufferSize <= MAX_BUFFER_SIZE) {
            start = System.currentTimeMillis();
            totalbytes = 0;
            boolean wasEqual = isEqualsNio(myBufferSize);

            if (wasEqual) {
                printAfterEquals(myBufferSize);
            } else {
                Assert.fail("files were not equal");
            }

            myBufferSize *= 2;
        }

    }

    private void compareWithBufferSize(int myBufferSize) throws IOException {
        final BufferedInputStream inputStream1 =
                new BufferedInputStream(
                        new FileInputStream(new File(fileName1)),
                        myBufferSize);
        byte[] buff1 = new byte[myBufferSize];
        final BufferedInputStream inputStream2 =
                new BufferedInputStream(
                        new FileInputStream(new File(fileName2)),
                        myBufferSize);
        byte[] buff2 = new byte[myBufferSize];
        int read1;

        start = System.currentTimeMillis();
        totalbytes = 0;
        while ((read1 = inputStream1.read(buff1)) != -1) {
            totalbytes += read1;
            int read2 = inputStream2.read(buff2);
            if (read1 != read2) {
                break;
            }
            if (!Arrays.equals(buff1, buff2)) {
                break;
            }
        }
        if (read1 == -1) {
            printAfterEquals(myBufferSize);
        } else {
            Assert.fail("files were not equal");
        }
        inputStream1.close();
        inputStream2.close();
    }

    private void compareWithBufferSizeFutures(int myBufferSize)
            throws ExecutionException, InterruptedException, IOException {
        final BufferedInputStream inputStream1 =
                new BufferedInputStream(
                        new FileInputStream(
                                new File(fileName1)),
                        myBufferSize);
        final BufferedInputStream inputStream2 =
                new BufferedInputStream(
                        new FileInputStream(
                                new File(fileName2)),
                        myBufferSize);

        final boolean wasEqual = isEqualsParallel(myBufferSize, inputStream1, inputStream2);

        if (wasEqual) {
            printAfterEquals(myBufferSize);
        } else {
            Assert.fail("files were not equal");
        }
        inputStream1.close();
        inputStream2.close();
    }

    private boolean isEqualsParallel(int myBufferSize
            , final BufferedInputStream inputStream1
            , final BufferedInputStream inputStream2)
            throws InterruptedException, ExecutionException {
        final byte[] buff1Even = new byte[myBufferSize];
        final byte[] buff1Odd = new byte[myBufferSize];
        final byte[] buff2Even = new byte[myBufferSize];
        final byte[] buff2Odd = new byte[myBufferSize];
        final Callable<Integer> read1Even = new Callable<Integer>() {
            public Integer call() throws Exception {
                return inputStream1.read(buff1Even);
            }
        };
        final Callable<Integer> read2Even = new Callable<Integer>() {
            public Integer call() throws Exception {
                return inputStream2.read(buff2Even);
            }
        };
        final Callable<Integer> read1Odd = new Callable<Integer>() {
            public Integer call() throws Exception {
                return inputStream1.read(buff1Odd);
            }
        };
        final Callable<Integer> read2Odd = new Callable<Integer>() {
            public Integer call() throws Exception {
                return inputStream2.read(buff2Odd);
            }
        };
        final Callable<Boolean> oddEqualsArray = new Callable<Boolean>() {
            public Boolean call() throws Exception {
                return Arrays.equals(buff1Odd, buff2Odd);
            }
        };
        final Callable<Boolean> evenEqualsArray = new Callable<Boolean>() {
            public Boolean call() throws Exception {
                return Arrays.equals(buff1Even, buff2Even);
            }
        };

        ExecutorService executor = Executors.newCachedThreadPool();
        boolean isEven = true;
        Future<Integer> read1 = null;
        Future<Integer> read2 = null;
        Future<Boolean> isEqual = null;
        int lastSize = 0;
        while (true) {
            if (isEqual != null) {
                if (!isEqual.get()) {
                    return false;
                } else if (lastSize == -1) {
                    return true;
                }
            }
            if (read1 != null) {
                lastSize = read1.get();
                totalbytes += lastSize;
                final int size2 = read2.get();
                if (lastSize != size2) {
                    return false;
                }
            }
            isEven = !isEven;
            if (isEven) {
                if (read1 != null) {
                    isEqual = executor.submit(oddEqualsArray);
                }
                read1 = executor.submit(read1Even);
                read2 = executor.submit(read2Even);
            } else {
                if (read1 != null) {
                    isEqual = executor.submit(evenEqualsArray);
                }
                read1 = executor.submit(read1Odd);
                read2 = executor.submit(read2Odd);
            }
        }
    }

    private boolean isEqualsNio(int myBufferSize) throws IOException {
        FileChannel first = null, seconde = null;
        try {
            first = new FileInputStream(fileName1).getChannel();
            seconde = new FileInputStream(fileName2).getChannel();
            if (first.size() != seconde.size()) {
                return false;
            }
            ByteBuffer firstBuffer = ByteBuffer.allocateDirect(myBufferSize);
            ByteBuffer secondBuffer = ByteBuffer.allocateDirect(myBufferSize);
            int firstRead, secondRead;
            while (first.position() < first.size()) {
                firstRead = first.read(firstBuffer);
                totalbytes += firstRead;
                secondRead = seconde.read(secondBuffer);
                if (firstRead != secondRead) {
                    return false;
                }
                if (!nioBuffersEqual(firstBuffer, secondBuffer, firstRead)) {
                    return false;
                }
            }
            return true;
        } finally {
            if (first != null) {
                first.close();
            }
            if (seconde != null) {
                seconde.close();
            }
        }
    }

    private static boolean nioBuffersEqual(ByteBuffer first, ByteBuffer second, final int length) {
        if (first.limit() != second.limit() || length > first.limit()) {
            return false;
        }
        first.rewind();
        second.rewind();
        for (int i = 0; i < length; i++) {
            if (first.get() != second.get()) {
                return false;
            }
        }
        return true;
    }

    private void printAfterEquals(int myBufferSize) {
        NumberFormat nf = new DecimalFormat("#.00");
        final long dur = System.currentTimeMillis() - start;
        double seconds = dur / 1000d;
        double megabytes = totalbytes / 1024 / 1024;
        double rate = (megabytes) / seconds;
        System.out.println("I was equal, even after " + totalbytes
                + " bytes and reading for " + dur
                + " ms (" + nf.format(rate) + "MB/sec * 2)" +
                " with a buffer size of " + myBufferSize / 1024 + " kB");
    }
}

نصائح أخرى

مع مثل هذه الملفات الكبيرة، سوف تحصل على أداء أفضل بكثير مع java.nio.

بالإضافة إلى ذلك، يمكن أن تكون قراءة بايت واحد مع تدفقات جافا بطيئة للغاية. باستخدام مجموعة بايت (عناصر 2-6K من تجاربي الخاصة، YMMV كما يبدو أن منصة / تطبيق محددة) سوف تحسن بشكل كبير أداء القراءة الخاص بك مع التدفقات.

قراءة وكتابة الملفات مع جافا يمكن أن تكون سريعة. يمكنك استخدام filechannels.وبعد أما بالنسبة لمقارنة الملفات، فمن الواضح أن هذا سيستغرق الكثير من الوقت مقارنة بايت إلى البايت هنا مثال باستخدام FileChans و bytebuffers (يمكن تحسينها):

public static boolean compare(String firstPath, String secondPath, final int BUFFER_SIZE) throws IOException {
    FileChannel firstIn = null, secondIn = null;
    try {
        firstIn = new FileInputStream(firstPath).getChannel();
        secondIn = new FileInputStream(secondPath).getChannel();
        if (firstIn.size() != secondIn.size())
            return false;
        ByteBuffer firstBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
        ByteBuffer secondBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
        int firstRead, secondRead;
        while (firstIn.position() < firstIn.size()) {
            firstRead = firstIn.read(firstBuffer);
            secondRead = secondIn.read(secondBuffer);
            if (firstRead != secondRead)
                return false;
            if (!buffersEqual(firstBuffer, secondBuffer, firstRead))
                return false;
        }
        return true;
    } finally {
        if (firstIn != null) firstIn.close();
        if (secondIn != null) firstIn.close();
    }
}

private static boolean buffersEqual(ByteBuffer first, ByteBuffer second, final int length) {
    if (first.limit() != second.limit())
        return false;
    if (length > first.limit())
        return false;
    first.rewind(); second.rewind();
    for (int i=0; i<length; i++)
        if (first.get() != second.get())
            return false;
    return true;
}

فيما يلي مقالة جيدة عن المزايا النسبية بطرق مختلفة لقراءة ملف في Java. قد يكون من بعض الاستخدام:

كيفية قراءة الملفات بسرعة

بعد تعديل وظيفة NIO مقارنة، أحصل على النتائج التالية.

I was equal, even after 4294967296 bytes and reading for 304594 ms (13.45MB/sec * 2) with a buffer size of 1024 kB
I was equal, even after 4294967296 bytes and reading for 225078 ms (18.20MB/sec * 2) with a buffer size of 4096 kB
I was equal, even after 4294967296 bytes and reading for 221351 ms (18.50MB/sec * 2) with a buffer size of 16384 kB

ملاحظة: هذا يعني أن الملفات تتم قراءتها بمعدل 37 ميغابايت / ثانية

تشغيل نفس الشيء على محرك أسرع

I was equal, even after 4294967296 bytes and reading for 178087 ms (23.00MB/sec * 2) with a buffer size of 1024 kB
I was equal, even after 4294967296 bytes and reading for 119084 ms (34.40MB/sec * 2) with a buffer size of 4096 kB
I was equal, even after 4294967296 bytes and reading for 109549 ms (37.39MB/sec * 2) with a buffer size of 16384 kB

ملاحظة: هذا يعني أن الملفات تتم قراءتها بمعدل 74.8 ميغابايت / ثانية

private static boolean nioBuffersEqual(ByteBuffer first, ByteBuffer second, final int length) {
    if (first.limit() != second.limit() || length > first.limit()) {
        return false;
    }
    first.rewind();
    second.rewind();
    int i;
    for (i = 0; i < length-7; i+=8) {
        if (first.getLong() != second.getLong()) {
            return false;
        }
    }
    for (; i < length; i++) {
        if (first.get() != second.get()) {
            return false;
        }
    }
    return true;
}

يمكنك إلقاء نظرة على صنز مقال لضبط I / O (Altough بالفعل بتاريخ بت)، ربما يمكنك العثور على أوجه التشابه بين الأمثلة هناك والرمز الخاص بك. أيضا إلقاء نظرة على java.nio. حزمة التي تحتوي على عناصر I / O أسرع من Java.io. دكتور دوبس مجلة لديه مقال لطيف للغاية IO الأداء العالي باستخدام java.nio.

إذا كان الأمر كذلك، فهناك أمثلة أخرى ونصائح ضبطها المتاحة هناك والتي يجب أن تكون قادرا على مساعدتك في تسريع التعليمات البرمجية الخاصة بك.

علاوة على ذلك فئة صفيفات لديها طرق لمقارنة صفائف البايت بناء في، ربما يمكن أيضا استخدام هذه الأشياء لصنع الأمور بشكل أسرع ومسح حلقة الخاص بك قليلا.

لمقارنة أفضل، حاول نسخ ملفين في وقت واحد. يمكن لمحرك الأقراص الصلبة قراءة ملف واحد بشكل أكثر كفاءة بكثير من القراءة اثنين (حيث يتعين على الرأس أن يتحرك ذهابا وإيابا لقراءة) طريقة واحدة لتقليل ذلك هو استخدام المخازن المؤقتة أكبر، على سبيل المثال 16 ميغابايت. مع bytebuffer.

مع Bytebuffer، يمكنك مقارنة 8 بايت في وقت واحد من خلال مقارنة القيم الطويلة مع Getlong ()

إذا كانت جافا فعالة، فإن معظم العمل موجود في القرص / نظام التشغيل للقراءة والكتابة، لذلك لا ينبغي أن يكون أبطأ بكثير من استخدام أي لغة أخرى (مثل القرص / نظام التشغيل هو عنق الزجاجة)

لا تفترض أن Java بطيء حتى تحدد عدم وجود خطأ في التعليمات البرمجية الخاصة بك.

لقد وجدت أن الكثير من المقالات المرتبطة به في هذا المنصب هي بالفعل خارج مؤرخة (هناك أيضا بعض الأشياء الثاقبة للغاية أيضا). هناك بعض المقالات المرتبطة بعقد 2001، والمعلومات مشكوك فيها في أحسن الأحوال. كتب مارتن طومسون من التعاطف الميكانيكي قليلا عن هذا في عام 2011. يرجى الرجوع إلى ما كتبه لخلفية ونظرية هذا.

لقد وجدت أن NIO أم لا NIO لديه القليل جدا من القيام به مع الأداء. إنه أكثر بكثير حول حجم مخازن الإخراج الخاصة بك (قراءة صفيف بايت على ذلك واحد). Nio ليس السحر جعلها تذهب صلصة نطاق ويب سريع.

كنت قادرا على اتخاذ أمثلة مارتن واستخدام إخراج الحقبة 1.0 وجعله يصرخ. NIO سريع أيضا، لكن المؤشر الأكبر هو مجرد حجم المخزن المؤقت للناتج ليس سواء كنت تستخدم Nio إلا إذا كنت تستخدم بالطبع، فأنت تستخدم ذاكرة معينة NIO ثم يهم. :)

إذا كنت ترغب في تحديث المعلومات الموثوقة حول هذا، راجع مدونة مارتن:

http://meconical-sympathy.blogspot.com/logpot.com/2011/12/java- مرفق-io-performance.html.

إذا كنت تريد أن ترى كيف لا يجعل Nio الكثير من الفرق (كما كنت قادرا على كتابة أمثلة باستخدام IO العادية كانت أسرع) انظر هذا:

http://www.dzone.com/links/fast_java_io_nio_is_always_faster_than_fileoutput.html.

لقد قمت باختبار افتراضي على جهاز كمبيوتر محمول Windows الجديد مع قرص ثابت سريع، MacBook Pro مع SSD، XLARGE EC2، وكبير EC2 4X مع ما يصل إلى iOPS / السرعة العالية (وقريبا قريبا على قرص ألياف NAS كبرى صفيف) لذلك يعمل (هناك بعض المشكلات معها لحالات EC2 الأصغر ولكن إذا كنت تهتم بالأداء ... هل ستستخدم مثيل EC2 صغير؟). إذا كنت تستخدم الأجهزة الحقيقية، في اختباراتي حتى الآن، يفوز IO التقليدي دائما. إذا كنت تستخدم EC2 عالية / IO، فهذا هو أيضا فائز واضح. إذا كنت تستخدم تحت مثيلات EC2 تعمل بالطاقة، فيمكن NIO الفوز.

لا يوجد استبدال للمعايير.

على أي حال، أنا لا خبير، أنا فقط فعلت بعض الاختبارات التجريبية باستخدام الإطار الذي كتبه السير مارتن طومسون في وظيفة مدونته.

أخذت هذا إلى الخطوة التالية وتستخدم الملفات.newputstream (من JDK 7) مع transcingqueue. لإنشاء وصفة لصنع Java I / O Scream (حتى على مثيلات EC2 الصغيرة). يمكن العثور على الوصفة في أسفل هذه الوثائق ل Boon (https://github.com/richardhightower/boon/wiki/auto-growable-byteby-buffer-like-a-bytebuilder.). هذا يسمح لي باستخدام إخراج تقليدي ولكن مع شيء يعمل بشكل جيد على مثيلات EC2 أصغر. (أنا المؤلف الرئيسي من نعمة. لكنني أقبل المؤلفين الجدد. تمتص الأجور. 0 $ في الساعة. لكن الخبر السار هو، يمكنني مضاعفة دفعتك كلما أردت.)

2 سنتات.

نرى هذا لمعرفة لماذا transcingqueue. أنه مهم. http://php.sabscape.com/blog/؟p=557.

التعليم الرئيسي:

  1. إذا كنت تهتم بالأداء أبدا، فستستخدم أبدا bufferedoutputstream.
  2. NIO لا دائما متساوي الأداء.
  3. حجم المخزن المؤقت يهم أكثر.
  4. إعادة تدوير المخازن المؤقتة للكتابة عالية السرعة أمر بالغ الأهمية.
  5. هل يمكن / الإرادة / سوف تنفجر أدائك لكتابة عالية السرعة.
  6. يجب أن يكون لديك بعض الآلية لإعادة استخدام المخازن المؤقتة.

DMA / SATA هي الأجهزة / TechLonogies منخفضة المستوى وغير مرئية لأي لغة برمجة على الإطلاق.

بالنسبة للذاكرة المعينة الإدخال / الإخراج، يجب عليك استخدام Java.nio، أعتقد.

هل أنت متأكد من أنك لا تقرأ هذه الملفات بواسطة بايت واحد؟ من شأنها أن تكون مهدرة، أود أن أفعل ذلك من خلال كتلة من قبل كتلة، وكل كتلة يجب أن تكون مثل 64 ميغابايت لتقليل البحث عن البحث.

حاول تعيين المخزن المؤقت على دفق الإدخال حتى عدة ميغابايت.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top