Beberapa bulan yang lalu, sebuah artikel luar biasa [terjemahan dalam Habré: satu dan dua ] tentang Grand Theft Auto Online muncul di berita .
Saya menyarankan Anda untuk membaca seluruh artikel, tetapi singkatnya, GTA Online tiba - tiba memiliki kinerja kuadrat ketika mem-parsing gumpalan JSON yang besar (karena banyak panggilan
strlen
); setelah memperbaiki kesalahan ini, waktu pemuatan berkurang hampir 70%.
Ini memicu perdebatan yang hidup : Apakah C yang harus disalahkan untuk ini? Atau mungkin "kotoran web" ? Atau kapitalisme dan insentifnya ?
Namun, semua orang setuju pada satu hal : mereka tidak akan pernah menulis omong kosong seperti itu.
( Dapatkah Anda merasakan apa yang akan datang? )
Salah satu proyek sampingan saya adalah penampil model 3D berkinerja tinggi yang disebut Erizo .
Dengan kode pintar, ini membuka biner STL 97MB pada Macbook Pro 2013 hanya dalam 165 milidetik. Ini kecepatan yang luar biasa .
Untuk alasan kompatibilitas, saya juga menulis parser kecil untuk ASCII STL .
ASCII STL adalah format teks biasa yang tidak ditentukan dengan baik yang terlihat seperti ini:
solid cube_corner facet normal 0.0 -1.0 0.0 outer loop vertex 0.0 0.0 0.0 vertex 1.0 0.0 0.0 vertex 0.0 0.0 1.0 endloop endfacet facet normal 0.0 0.0 -1.0 outer loop vertex 0.0 0.0 0.0 vertex 0.0 1.0 0.0 vertex 1.0 0.0 0.0 endloop endfacet ... endsolid
Saya menulis parser yang sangat kuat dengan komentar seperti ini:
/* ASCII STL: ,
* 'vertex', float. */
Memuat ASCII STL selalu tampak agak lambat, tetapi saya berasumsi itu karena format teks yang tidak efisien.
( Awan berkumpul. )
Beberapa peristiwa terjadi dalam beberapa hari:
- Untuk pertama kalinya dalam beberapa tahun, saya kembali ke kode Erizo lama untuk memperbaiki kesalahan fokus pada macOS
- Menerbitkan artikel tentang GTA Online
- Dari diskusi selanjutnya, saya belajar bahwa
- sscanf
- Saya perhatikan bahwa pemuatan ASCII STL sangat lambat .
Berikut adalah log unduhan stempel waktu ASCII STL 1,5 MB (dalam detik):
[erizo] (0.000000) main.c:10 | Startup! [erizo] (0.162895) window.c:91 | Created window [erizo] (0.162900) window.c:95 | Made context current [erizo] (0.168715) window.c:103 | Initialized GLEW [erizo] (0.178329) window.c:91 | Created window [erizo] (0.178333) window.c:95 | Made context current [erizo] (1.818734) loader.c:109 | Parsed ASCII STL [erizo] (1.819471) loader.c:227 | Workers have deduplicated vertices [erizo] (1.819480) loader.c:237 | Got 5146 vertices (7982 triangles) [erizo] (1.819530) loader.c:240 | Waiting for buffer... [erizo] (1.819624) loader.c:326 | Allocated buffer [erizo] (1.819691) loader.c:253 | Sent buffers to worker threads [erizo] (1.819883) loader.c:258 | Joined worker threads [erizo] (1.819887) loader.c:279 | Loader thread done [erizo] (1.821291) instance.c:32 | Showed window
Lebih dari 1,8 detik berlalu dari saat peluncuran ke tampilan jendela!
Melihat parser ASCII dengan segar, saya melihat bahwa alasannya jelas:
/* The most liberal ASCII STL parser: Ignore everything except
* the word 'vertex', then read three floats after each one. */
const char VERTEX_STR[] = "vertex ";
while (1) {
data = strstr(data, VERTEX_STR);
if (!data) {
break;
}
/* Skip to the first character after 'vertex' */
data += strlen(VERTEX_STR);
for (unsigned i=0; i < 3; ++i) {
SKIP_WHILE(isspace);
float f;
const int r = sscanf(data, "%f", &f);
ABORT_IF(r == 0 || r == EOF, "Failed to parse float");
if (buf_size == buf_count) {
buf_size *= 2;
buffer = (float*)realloc(buffer, buf_size * sizeof(float));
}
buffer[buf_count++] = f;
SKIP_WHILE(!isspace);
}
}
Anda dapat melihat bahwa dalam kode ada satu
sscanf
yang membaca satu nilai float dari awal aliran data dan setiap kali memeriksa panjang seluruh string .
Ya, saya membuat kesalahan yang sama dengan programmer yang bekerja di GTA Online: Saya tiba-tiba menulis parser kuadrat!
Mengganti panggilan
sscanf
dengan panggilan
strtof
mengurangi waktu pemuatan hampir 10 kali: dari 1,8 detik menjadi 199 milidetik.
[erizo] (0.000000) main.c:10 | Startup! [erizo] (0.178082) window.c:91 | Created window [erizo] (0.178086) window.c:95 | Made context current [erizo] (0.184226) window.c:103 | Initialized GLEW [erizo] (0.194469) window.c:91 | Created window [erizo] (0.194472) window.c:95 | Made context current [erizo] (0.196126) loader.c:109 | Parsed ASCII STL [erizo] (0.196866) loader.c:227 | Workers have deduplicated vertices [erizo] (0.196871) loader.c:237 | Got 5146 vertices (7982 triangles) [erizo] (0.196921) loader.c:240 | Waiting for buffer... [erizo] (0.197013) loader.c:326 | Allocated buffer [erizo] (0.197082) loader.c:253 | Sent buffers to worker threads [erizo] (0.197303) loader.c:258 | Joined worker threads [erizo] (0.197306) loader.c:279 | Loader thread done [erizo] (0.199328) instance.c:32 | Showed window
Ini adalah pengingat yang sempurna bahwa meskipun Anda telah memprogram selama bertahun-tahun, selalu ada jebakan . Dalam
sscanf
tidak mengisi kompleksitas waktu, sehingga pistol sangat licik menembak dirinya sendiri di kaki, dan tampaknya bagi saya bahwa tidak ada yang saya mengembara dalam kegelapan ketidaktahuan.
Anda sendiri mungkin tidak diingatkan seperti ini, tetapi setiap kali Anda membaca cerita yang luar biasa tentang kode yang buruk, ingatlah - itu juga bisa terjadi pada Anda!
(Jelas, moral dari ceritanya adalah ini: jangan gunakan
sscanf
token tunggal dari awal baris untuk menguraikan beberapa kali; Saya yakin Anda akan baik-baik saja jika Anda menghindarinya.)
Periklanan
VDSina menawarkan VPS yang kuat dan terjangkau dengan pembayaran harian. Saluran Internet untuk setiap server adalah 500 Megabit, perlindungan terhadap serangan DDoS termasuk dalam tarif, kemampuan untuk menginstal Windows, Linux atau OS secara umum dari gambar Anda, dan juga panel kontrol server berpemilik yang sangat nyaman . Cobalah!