SORU
17 Ocak 2012, Salı


Hızlı bir dizedeki her karakter üzerinde yineleme yolu

Java, en hızlı şekilde bir Dize bu: tüm karakter üzerinde yineleme ne olur

String str = "a really, really long string";
for (int i = 0, n = str.length(); i < n; i  ) {
    char c = str.charAt(i);
}

Ya da bu:

char[] chars = str.toCharArray();
for (int i = 0, n = chars.length; i < n; i  ) {
    char c = chars[i];
}

DÜZENLEME :

Ne istiyorum biliyor eğer maliyeti tekrar tekrar arama charAt yöntem sırasında uzun bir yineleme biter olmak ya da daha az ya da daha yüksek maliyet performans tek bir çağrı toCharArray başlangıç ve daha sonra doğrudan erişim dizi boyunca yineleme.

Eğer birisi farklı bir dize uzunluğu için sağlam bir kriter, akılda JİT ısınma zamanı, başlangıç zamanı, vb JVM sahip olsaydı harika olurdu. ve sadece System.currentTimeMillis() iki arama arasında fark yok.

CEVAP
9 AĞUSTOS 2012, PERŞEMBE


String muayene olmak uzunluğuna bağlıdır. Soru söylediği gibi, Eğeruzundizeleri, dize incelemek için en hızlı şekilde yansıma dize destek char[] erişim için kullanmaktır.

Tamamen rastgele bir kriter ile İLGİLENİYORUZ 8 (win32 ve win64) 64 AMD Phenom II 4 çekirdek 955 @ 3.2 GHZ (hem istemci modu ve sunucu modu) ile 9 farklı teknikler (aşağıya bakınız!) String.charAt(n) kullanarak küçük dizeleri için en hızlı ve reflection Dize erişmek için kullandığı gösteriyor dizi destek neredeyse iki kat daha hızlı büyük dizeleri için.

DENEY

  • 9 farklı optimizasyon teknikleri çalıştı.

  • Tüm dize içeriğini ayırılmış

  • Test iki 0,1,2,4,8,16 vb ile başlayan katları dize boyutları için yapılır.

  • Testler dize boyutu başına 1000 kez yapılır

  • Testler rastgele her zaman karıştırılır. Diğer bir deyişle, testlerin rastgele sırayla yaptıklarını her zaman, 1000'den fazla kez yapılır.

  • Tüm test paketi, ileriye ve geriye doğru, optimizasyon ve saatlerde JVM ısınma etkisini göstermek için yapılır.

  • Tüm takımı iki kez, bir kez -server modu -client mod ve diğer yapılır.

SONUÇ

-istemci modu (32 bit)

Dizeleri içinUzunluğu 1 256 karaktersaniyede 588 milyon karakter için 13,4 milyon ortalama işlem ile string.charAt(i) galibiyet arıyor.

Ayrıca, genel olarak 5.5% daha hızlı (istemci) ve bu şekilde 13.9% (sunucu):

    for (int i = 0; i < data.length(); i  ) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }

daha yerel nihai uzunluğu bir değişken ile bu gibi:

    final int len = data.length();
    for (int i = 0; i < len; i  ) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }

Uzun dizeleri için512 256 K için karakter uzunluğuDize destek dizi hızlı erişim için yansıma kullanarak.Bu teknik neredeyse iki kat daha hızlıDize kadar.(ben) (178% daha hızlı) charAt. Bu aralığı boyunca ortalama hızı saniyede 1.111 milyar karakter oldu.

Alan vaktinden önce elde edilmesi ve yeniden kullanılan farklı dizeleri kütüphanede olabilir. İlginçtir, Alan erişim kodu yukarıda farklı olarak, kullanmak için daha 9% yerel nihai uzunluğu değişken olması daha hızlı 'karakter.uzunluk' döngü kontrol edin. Alan erişim hızlı olarak ayarlanabilir.

   final Field field = String.class.getDeclaredField("value");
   field.setAccessible(true);

   try {
       final char[] chars = (char[]) field.get(data);
       final int len = chars.length;
       for (int i = 0; i < len; i  ) {
           if (chars[i] <= ' ') {
               doThrow();
           }
       }
       return len;
   } catch (Exception ex) {
       throw new RuntimeException(ex);
   }

-Server modu hakkında özel açıklamalarda bulundu

Alan erişim başlayan AMD 64 benim makinede 64 bit Java bir makinede server modunda 32 karakter uzunluğunda dizeleri sonra kazanan. İstemci modunda 512 karakter uzunluğu kadar görülmüş değil.

Sunucu modunda olduğunu GÖRDÜM 8 (32 bit) kurmak için çalışırken sanırım belirterek, aynı zamanda değer, genel performans ve %7 daha yavaş hem büyük hem de küçük dizeleri için. Bu yapışkan notlar 8 121 Aralık 2013 erken tahliye ile inşa edildi. Yani, şimdi, 32 bit server 32 bit modu istemci modu daha yavaş gerçekleşiyor gibi görünüyor.

Bu varlık çağırma değer yalnızca sunucu modu 64 bit bir makine gibi görünüyor. Aksi takdirde aslında performans engellemektedir.

32 bit AMD64, şunu söyleyebilirim: bir -server mode çalışan yapı

  1. String.charAt(i) net kazanan genel. Boyutları 8 512 karakter arasında kazananlar arasında olmasına rağmen ''' ve 'alan'. yeniden' yeni
  2. String.charAt(i) E daha hızlı istemci modunda
  3. Alan erişim iki kat daha hızlı istemci modunda büyük Dizeleri için.

Ayrıca, Dize söylemeye değer.() karakter (Akış ve paralel sürüm) bir büstü vardır. Başka bir şekilde daha yavaş bir şekilde. Streams API genel string işlemleri gerçekleştirmek için oldukça yavaş bir şekilde.

İstek Listesi

Java Dize yüklem içerir gibi en iyi yöntemleri(yüklem) kabul, (tüketici) dosyalarda grup, forEachWithİndex(tüketici) olabilir. Böylece, kullanıcı uzunluk bilmiyor veya Dize yöntem çağrıları tekrar etmeye gerek kalmadan, bu ayrıştırma kütüphaneler beep-beep beep hızlanma yardımcı olabilir.

Hayal :)

Mutlu Dizeler!

~SH

Test boşluk varlığı için bir dize test 9 aşağıdaki yöntemler kullanılır

< . p ^"charAt1" -- CHECK DİZE İÇERİĞİNİ her ZAMANKİ ŞEKİLDE

int charAtMethod1(final String data) {
    final int len = data.length();
    for (int i = 0; i < len; i  ) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }
    return len;
}
< . p ^"charAt2" -- AMA Dize KULLANIN YUKARIDAKİ GİBİ.() boy Uzunluğu İÇİN SON YEREL BİR int YAPMAK YERİNE

int charAtMethod2(final String data) {
    for (int i = 0; i < data.length(); i  ) {
        if (data.charAt(i) <= ' ') {
            doThrow();
        }
    }
    return data.length();
}
< . p ^"akış JAVA-8 YENİ bir Dize var İntStream KULLANIN VE GEÇİŞ KONTROL YAPMAK İÇİN BİR YÜKLEM" --

int streamMethod(final String data, final IntPredicate predicate) {
    if (data.chars().anyMatch(predicate)) {
        doThrow();
    }
    return data.length();
}
< . p ^"" -- YUKARIDAKİ GİBİ AYNI, AMA o-LA-LA - PARALEL GİT!!! streamPara

// avoid this at all costs
int streamParallelMethod(final String data, IntPredicate predicate) {
    if (data.chars().parallel().anyMatch(predicate)) {
        doThrow();
    }
    return data.length();
}
< . p ^"" -- YENİDEN BİR char DOLUM[] DİZELERİ İÇERİĞİ İLE . yeniden

int reuseBuffMethod(final char[] reusable, final String data) {
    final int len = data.length();
    data.getChars(0, len, reusable, 0);
    for (int i = 0; i < len; i  ) {
        if (reusable[i] <= ' ') {
            doThrow();
        }
    }
    return len;
}
< . p ^"" -- char YENİ BİR KOPYASINI EDİNİN[] dizesinden . new1

int newMethod1(final String data) {
    final int len = data.length();
    final char[] copy = data.toCharArray();
    for (int i = 0; i < len; i  ) {
        if (copy[i] <= ' ') {
            doThrow();
        }
    }
    return len;
}
< . p ^"" YUKARIDAKİ GİBİ AYNI, AMA "HER" . new2

int newMethod2(final String data) {
    for (final char c : data.toCharArray()) {
        if (c <= ' ') {
            doThrow();
        }
    }
    return data.length();
}
<>"" -- FANTEZİ!! alan1 p STRİNG İÇ char ERİŞİM İÇİN ALAN ELDE etmek[]

int fieldMethod1(final Field field, final String data) {
    try {
        final char[] chars = (char[]) field.get(data);
        final int len = chars.length;
        for (int i = 0; i < len; i  ) {
            if (chars[i] <= ' ') {
                doThrow();
            }
        }
        return len;
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}
< . p ^"" YUKARIDAKİ GİBİ AYNI, AMA "HER" . alan2

int fieldMethod2(final Field field, final String data) {
    final char[] chars;
    try {
        chars = (char[]) field.get(data);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
    for (final char c : chars) {
        if (c <= ' ') {
            doThrow();
        }
    }
    return chars.length;
}

MÜŞTERİ İÇİN BİLEŞİK SONUÇLARI -client MOD (ileriye ve geriye testleri birlikte)

-İstemci modu ile 32 bit Java ve Java ile sunucu modu 64 bit AMD64 makinemde aşağıda olduğu gibidir. not:

Size     WINNER  charAt1 charAt2  stream streamPar   reuse    new1    new2  field1  field2
1        charAt    77.0     72.0   462.0     584.0   127.5    89.5    86.0   159.5   165.0
2        charAt    38.0     36.5   284.0   32712.5    57.5    48.3    50.3    89.0    91.5
4        charAt    19.5     18.5   458.6    3169.0    33.0    26.8    27.5    54.1    52.6
8        charAt     9.8      9.9   100.5    1370.9    17.3    14.4    15.0    26.9    26.4
16       charAt     6.1      6.5    73.4     857.0     8.4     8.2     8.3    13.6    13.5
32       charAt     3.9      3.7    54.8     428.9     5.0     4.9     4.7     7.0     7.2
64       charAt     2.7      2.6    48.2     232.9     3.0     3.2     3.3     3.9     4.0
128      charAt     2.1      1.9    43.7     138.8     2.1     2.6     2.6     2.4     2.6
256      charAt     1.9      1.6    42.4      90.6     1.7     2.1     2.1     1.7     1.8
512      field1     1.7      1.4    40.6      60.5     1.4     1.9     1.9     1.3     1.4
1,024    field1     1.6      1.4    40.0      45.6     1.2     1.9     2.1     1.0     1.2
2,048    field1     1.6      1.3    40.0      36.2     1.2     1.8     1.7     0.9     1.1
4,096    field1     1.6      1.3    39.7      32.6     1.2     1.8     1.7     0.9     1.0
8,192    field1     1.6      1.3    39.6      30.5     1.2     1.8     1.7     0.9     1.0
16,384   field1     1.6      1.3    39.8      28.4     1.2     1.8     1.7     0.8     1.0
32,768   field1     1.6      1.3    40.0      26.7     1.3     1.8     1.7     0.8     1.0
65,536   field1     1.6      1.3    39.8      26.3     1.3     1.8     1.7     0.8     1.0
131,072  field1     1.6      1.3    40.1      25.4     1.4     1.9     1.8     0.8     1.0
262,144  field1     1.6      1.3    39.6      25.2     1.5     1.9     1.9     0.8     1.0

SERVER İÇİN KOMPOZİT SONUÇLARI -server MOD (ileriye ve geriye testleri birlikte)

Not: bu Java için test 32 bit AMD64 sunucu modunda çalışıyor. Java 64 bit Java server Modu Bu Alan dışında istemci modunda 32 bit erişim ve 32 karakterden sonra kazanan başlangıç olarak aynı boyutta.

Size     WINNER  charAt1 charAt2  stream streamPar   reuse    new1    new2  field1  field2
1        charAt     74.5    95.5   524.5     783.0    90.5   102.5    90.5   135.0   151.5
2        charAt     48.5    53.0   305.0   30851.3    59.3    57.5    52.0    88.5    91.8
4        charAt     28.8    32.1   132.8    2465.1    37.6    33.9    32.3    49.0    47.0
8          new2     18.0    18.6    63.4    1541.3    18.5    17.9    17.6    25.4    25.8
16         new2     14.0    14.7   129.4    1034.7    12.5    16.2    12.0    16.0    16.6
32         new2      7.8     9.1    19.3     431.5     8.1     7.0     6.7     7.9     8.7
64        reuse      6.1     7.5    11.7     204.7     3.5     3.9     4.3     4.2     4.1
128       reuse      6.8     6.8     9.0     101.0     2.6     3.0     3.0     2.6     2.7
256      field2      6.2     6.5     6.9      57.2     2.4     2.7     2.9     2.3     2.3
512       reuse      4.3     4.9     5.8      28.2     2.0     2.6     2.6     2.1     2.1
1,024    charAt      2.0     1.8     5.3      17.6     2.1     2.5     3.5     2.0     2.0
2,048    charAt      1.9     1.7     5.2      11.9     2.2     3.0     2.6     2.0     2.0
4,096    charAt      1.9     1.7     5.1       8.7     2.1     2.6     2.6     1.9     1.9
8,192    charAt      1.9     1.7     5.1       7.6     2.2     2.5     2.6     1.9     1.9
16,384   charAt      1.9     1.7     5.1       6.9     2.2     2.5     2.5     1.9     1.9
32,768   charAt      1.9     1.7     5.1       6.1     2.2     2.5     2.5     1.9     1.9
65,536   charAt      1.9     1.7     5.1       5.5     2.2     2.4     2.4     1.9     1.9
131,072  charAt      1.9     1.7     5.1       5.4     2.3     2.5     2.5     1.9     1.9
262,144  charAt      1.9     1.7     5.1       5.1     2.3     2.5     2.5     1.9     1.9

TAM ÇALIŞTIRILABİLİR PROGRAM KODU

(Java 7 üzerinde test ve daha önce iki akarsu testleri kaldırmak için)

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.IntPredicate;

/**
 * @author Saint Hill <http://stackoverflow.com/users/1584255/saint-hill>
 */
public final class TestStrings {

    // we will not test strings longer than 512KM
    final int MAX_STRING_SIZE = 1024 * 256;

    // for each string size, we will do all the tests
    // this many times
    final int TRIES_PER_STRING_SIZE = 1000;

    public static void main(String[] args) throws Exception {
        new TestStrings().run();
    }

    void run() throws Exception {

        // double the length of the data until it reaches MAX chars long
        // 0,1,2,4,8,16,32,64,128,256 ... 
        final List<Integer> sizes = new ArrayList<>();
        for (int n = 0; n <= MAX_STRING_SIZE; n = (n == 0 ? 1 : n * 2)) {
            sizes.add(n);
        }

        // CREATE RANDOM (FOR SHUFFLING ORDER OF TESTS)
        final Random random = new Random();

        System.out.println("Rate in nanoseconds per character inspected.");
        System.out.printf("==== FORWARDS (tries per size: %s) ==== \n", TRIES_PER_STRING_SIZE);

        printHeadings(TRIES_PER_STRING_SIZE, random);

        for (int size : sizes) {
            reportResults(size, test(size, TRIES_PER_STRING_SIZE, random));
        }

        // reverse order or string sizes
        Collections.reverse(sizes);

        System.out.println("");
        System.out.println("Rate in nanoseconds per character inspected.");
        System.out.printf("==== BACKWARDS (tries per size: %s) ==== \n", TRIES_PER_STRING_SIZE);

        printHeadings(TRIES_PER_STRING_SIZE, random);

        for (int size : sizes) {
            reportResults(size, test(size, TRIES_PER_STRING_SIZE, random));

        }
    }

    ///
    ///
    ///  METHODS OF CHECKING THE CONTENTS
    ///  OF A STRING. ALWAYS CHECKING FOR
    ///  WHITESPACE (CHAR <=' ')
    ///  
    ///
    // CHECK THE STRING CONTENTS
    int charAtMethod1(final String data) {
        final int len = data.length();
        for (int i = 0; i < len; i  ) {
            if (data.charAt(i) <= ' ') {
                doThrow();
            }
        }
        return len;
    }

    // SAME AS ABOVE BUT USE String.length()
    // instead of making a new final local int 
    int charAtMethod2(final String data) {
        for (int i = 0; i < data.length(); i  ) {
            if (data.charAt(i) <= ' ') {
                doThrow();
            }
        }
        return data.length();
    }

    // USE new Java-8 String's IntStream
    // pass it a PREDICATE to do the checking
    int streamMethod(final String data, final IntPredicate predicate) {
        if (data.chars().anyMatch(predicate)) {
            doThrow();
        }
        return data.length();
    }

    // OH LA LA - GO PARALLEL!!!
    int streamParallelMethod(final String data, IntPredicate predicate) {
        if (data.chars().parallel().anyMatch(predicate)) {
            doThrow();
        }
        return data.length();
    }

    // Re-fill a resuable char[] with the contents
    // of the String's char[]
    int reuseBuffMethod(final char[] reusable, final String data) {
        final int len = data.length();
        data.getChars(0, len, reusable, 0);
        for (int i = 0; i < len; i  ) {
            if (reusable[i] <= ' ') {
                doThrow();
            }
        }
        return len;
    }

    // Obtain a new copy of char[] from String
    int newMethod1(final String data) {
        final int len = data.length();
        final char[] copy = data.toCharArray();
        for (int i = 0; i < len; i  ) {
            if (copy[i] <= ' ') {
                doThrow();
            }
        }
        return len;
    }

    // Obtain a new copy of char[] from String
    // but use FOR-EACH
    int newMethod2(final String data) {
        for (final char c : data.toCharArray()) {
            if (c <= ' ') {
                doThrow();
            }
        }
        return data.length();
    }

    // FANCY!
    // OBTAIN FIELD FOR ACCESS TO THE STRING'S
    // INTERNAL CHAR[]
    int fieldMethod1(final Field field, final String data) {
        try {
            final char[] chars = (char[]) field.get(data);
            final int len = chars.length;
            for (int i = 0; i < len; i  ) {
                if (chars[i] <= ' ') {
                    doThrow();
                }
            }
            return len;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    // same as above but use FOR-EACH
    int fieldMethod2(final Field field, final String data) {
        final char[] chars;
        try {
            chars = (char[]) field.get(data);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        for (final char c : chars) {
            if (c <= ' ') {
                doThrow();
            }
        }
        return chars.length;
    }

    /**
     *
     * Make a list of tests. We will shuffle a copy of this list repeatedly
     * while we repeat this test.
     *
     * @param data
     * @return
     */
    List<Jobber> makeTests(String data) throws Exception {
        // make a list of tests
        final List<Jobber> tests = new ArrayList<Jobber>();

        tests.add(new Jobber("charAt1") {
            int check() {
                return charAtMethod1(data);
            }
        });

        tests.add(new Jobber("charAt2") {
            int check() {
                return charAtMethod2(data);
            }
        });

        tests.add(new Jobber("stream") {
            final IntPredicate predicate = new IntPredicate() {
                public boolean test(int value) {
                    return value <= ' ';
                }
            };

            int check() {
                return streamMethod(data, predicate);
            }
        });

        tests.add(new Jobber("streamPar") {
            final IntPredicate predicate = new IntPredicate() {
                public boolean test(int value) {
                    return value <= ' ';
                }
            };

            int check() {
                return streamParallelMethod(data, predicate);
            }
        });

        // Reusable char[] method
        tests.add(new Jobber("reuse") {
            final char[] cbuff = new char[MAX_STRING_SIZE];

            int check() {
                return reuseBuffMethod(cbuff, data);
            }
        });

        // New char[] from String
        tests.add(new Jobber("new1") {
            int check() {
                return newMethod1(data);
            }
        });

        // New char[] from String
        tests.add(new Jobber("new2") {
            int check() {
                return newMethod2(data);
            }
        });

        // Use reflection for field access
        tests.add(new Jobber("field1") {
            final Field field;

            {
                field = String.class.getDeclaredField("value");
                field.setAccessible(true);
            }

            int check() {
                return fieldMethod1(field, data);
            }
        });

        // Use reflection for field access
        tests.add(new Jobber("field2") {
            final Field field;

            {
                field = String.class.getDeclaredField("value");
                field.setAccessible(true);
            }

            int check() {
                return fieldMethod2(field, data);
            }
        });

        return tests;
    }

    /**
     * We use this class to keep track of test results
     */
    abstract class Jobber {

        final String name;
        long nanos;
        long chars;
        long runs;

        Jobber(String name) {
            this.name = name;
        }

        abstract int check();

        final double nanosPerChar() {
            double charsPerRun = chars / runs;
            long nanosPerRun = nanos / runs;
            return charsPerRun == 0 ? nanosPerRun : nanosPerRun / charsPerRun;
        }

        final void run() {
            runs  ;
            long time = System.nanoTime();
            chars  = check();
            nanos  = System.nanoTime() - time;
        }
    }

    // MAKE A TEST STRING OF RANDOM CHARACTERS A-Z
    private String makeTestString(int testSize, char start, char end) {
        Random r = new Random();
        char[] data = new char[testSize];
        for (int i = 0; i < data.length; i  ) {
            data[i] = (char) (start   r.nextInt(end));
        }
        return new String(data);
    }

    // WE DO THIS IF WE FIND AN ILLEGAL CHARACTER IN THE STRING
    public void doThrow() {
        throw new RuntimeException("Bzzzt -- Illegal Character!!");
    }

    /**
     * 1. get random string of correct length 2. get tests (List<Jobber>) 3.
     * perform tests repeatedly, shuffling each time
     */
    List<Jobber> test(int size, int tries, Random random) throws Exception {
        String data = makeTestString(size, 'A', 'Z');
        List<Jobber> tests = makeTests(data);
        List<Jobber> copy = new ArrayList<>(tests);
        while (tries-- > 0) {
            Collections.shuffle(copy, random);
            for (Jobber ti : copy) {
                ti.run();
            }
        }
        // check to make sure all char counts the same
        long runs = tests.get(0).runs;
        long count = tests.get(0).chars;
        for (Jobber ti : tests) {
            if (ti.runs != runs && ti.chars != count) {
                throw new Exception("Char counts should match if all correct algorithms");
            }
        }
        return tests;
    }

    private void printHeadings(final int TRIES_PER_STRING_SIZE, final Random random) throws Exception {
        System.out.print("  Size");
        for (Jobber ti : test(0, TRIES_PER_STRING_SIZE, random)) {
            System.out.printf("%9s", ti.name);
        }
        System.out.println("");
    }

    private void reportResults(int size, List<Jobber> tests) {
        System.out.printf("m", size);
        for (Jobber ti : tests) {
            System.out.printf("%,9.2f", ti.nanosPerChar());
        }
        System.out.println("");
    }
}

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • habpsu

    habpsu

    25 Temmuz 2007
  • Hey Nadine

    Hey Nadine

    24 Kasım 2006
  • hockeywebcasts

    hockeywebcas

    31 EKİM 2012