|  | package main | 
        
          |  |  | 
        
          |  | import ( | 
        
          |  | "encoding/json" | 
        
          |  | "os" | 
        
          |  | "strings" | 
        
          |  | "testing" | 
        
          |  |  | 
        
          |  | "github.com/boltdb/bolt" | 
        
          |  | "github.com/schollz/jsonstore" | 
        
          |  | ) | 
        
          |  |  | 
        
          |  | type Tweet struct { | 
        
          |  | Coordinates interface{} `json:"coordinates"` | 
        
          |  | Favorited   bool        `json:"favorited"` | 
        
          |  | Truncated   bool        `json:"truncated"` | 
        
          |  | CreatedAt   string      `json:"created_at"` | 
        
          |  | IDStr       string      `json:"id_str"` | 
        
          |  | Entities    struct { | 
        
          |  | Urls []struct { | 
        
          |  | ExpandedURL string `json:"expanded_url"` | 
        
          |  | URL         string `json:"url"` | 
        
          |  | Indices     []int  `json:"indices"` | 
        
          |  | DisplayURL  string `json:"display_url"` | 
        
          |  | } `json:"urls"` | 
        
          |  | Hashtags     []interface{} `json:"hashtags"` | 
        
          |  | UserMentions []interface{} `json:"user_mentions"` | 
        
          |  | } `json:"entities"` | 
        
          |  | InReplyToUserIDStr   interface{} `json:"in_reply_to_user_id_str"` | 
        
          |  | Contributors         interface{} `json:"contributors"` | 
        
          |  | Text                 string      `json:"text"` | 
        
          |  | RetweetCount         int         `json:"retweet_count"` | 
        
          |  | InReplyToStatusIDStr interface{} `json:"in_reply_to_status_id_str"` | 
        
          |  | ID                   int64       `json:"id"` | 
        
          |  | Geo                  interface{} `json:"geo"` | 
        
          |  | Retweeted            bool        `json:"retweeted"` | 
        
          |  | PossiblySensitive    bool        `json:"possibly_sensitive"` | 
        
          |  | InReplyToUserID      interface{} `json:"in_reply_to_user_id"` | 
        
          |  | Place                interface{} `json:"place"` | 
        
          |  | User                 struct { | 
        
          |  | ProfileSidebarFillColor   string `json:"profile_sidebar_fill_color"` | 
        
          |  | ProfileSidebarBorderColor string `json:"profile_sidebar_border_color"` | 
        
          |  | ProfileBackgroundTile     bool   `json:"profile_background_tile"` | 
        
          |  | Name                      string `json:"name"` | 
        
          |  | ProfileImageURL           string `json:"profile_image_url"` | 
        
          |  | CreatedAt                 string `json:"created_at"` | 
        
          |  | Location                  string `json:"location"` | 
        
          |  | FollowRequestSent         bool   `json:"follow_request_sent"` | 
        
          |  | ProfileLinkColor          string `json:"profile_link_color"` | 
        
          |  | IsTranslator              bool   `json:"is_translator"` | 
        
          |  | IDStr                     string `json:"id_str"` | 
        
          |  | Entities                  struct { | 
        
          |  | URL struct { | 
        
          |  | Urls []struct { | 
        
          |  | ExpandedURL interface{} `json:"expanded_url"` | 
        
          |  | URL         string      `json:"url"` | 
        
          |  | Indices     []int       `json:"indices"` | 
        
          |  | } `json:"urls"` | 
        
          |  | } `json:"url"` | 
        
          |  | Description struct { | 
        
          |  | Urls []interface{} `json:"urls"` | 
        
          |  | } `json:"description"` | 
        
          |  | } `json:"entities"` | 
        
          |  | DefaultProfile                 bool        `json:"default_profile"` | 
        
          |  | ContributorsEnabled            bool        `json:"contributors_enabled"` | 
        
          |  | FavouritesCount                int         `json:"favourites_count"` | 
        
          |  | URL                            string      `json:"url"` | 
        
          |  | ProfileImageURLHTTPS           string      `json:"profile_image_url_https"` | 
        
          |  | UtcOffset                      int         `json:"utc_offset"` | 
        
          |  | ID                             int         `json:"id"` | 
        
          |  | ProfileUseBackgroundImage      bool        `json:"profile_use_background_image"` | 
        
          |  | ListedCount                    int         `json:"listed_count"` | 
        
          |  | ProfileTextColor               string      `json:"profile_text_color"` | 
        
          |  | Lang                           string      `json:"lang"` | 
        
          |  | FollowersCount                 int         `json:"followers_count"` | 
        
          |  | Protected                      bool        `json:"protected"` | 
        
          |  | Notifications                  interface{} `json:"notifications"` | 
        
          |  | ProfileBackgroundImageURLHTTPS string      `json:"profile_background_image_url_https"` | 
        
          |  | ProfileBackgroundColor         string      `json:"profile_background_color"` | 
        
          |  | Verified                       bool        `json:"verified"` | 
        
          |  | GeoEnabled                     bool        `json:"geo_enabled"` | 
        
          |  | TimeZone                       string      `json:"time_zone"` | 
        
          |  | Description                    string      `json:"description"` | 
        
          |  | DefaultProfileImage            bool        `json:"default_profile_image"` | 
        
          |  | ProfileBackgroundImageURL      string      `json:"profile_background_image_url"` | 
        
          |  | StatusesCount                  int         `json:"statuses_count"` | 
        
          |  | FriendsCount                   int         `json:"friends_count"` | 
        
          |  | Following                      interface{} `json:"following"` | 
        
          |  | ShowAllInlineMedia             bool        `json:"show_all_inline_media"` | 
        
          |  | ScreenName                     string      `json:"screen_name"` | 
        
          |  | } `json:"user"` | 
        
          |  | InReplyToScreenName interface{} `json:"in_reply_to_screen_name"` | 
        
          |  | Source              string      `json:"source"` | 
        
          |  | InReplyToStatusID   interface{} `json:"in_reply_to_status_id"` | 
        
          |  | } | 
        
          |  |  | 
        
          |  | const data = ` | 
        
          |  | [ | 
        
          |  | { | 
        
          |  | "coordinates": null, | 
        
          |  | "favorited": false, | 
        
          |  | "truncated": false, | 
        
          |  | "created_at": "Wed Aug 29 17:12:58 +0000 2012", | 
        
          |  | "id_str": "240859602684612608", | 
        
          |  | "entities": { | 
        
          |  | "urls": [ | 
        
          |  | { | 
        
          |  | "expanded_url": "/blog/twitter-certified-products", | 
        
          |  | "url": "https://t.co/MjJ8xAnT", | 
        
          |  | "indices": [ | 
        
          |  | 52, | 
        
          |  | 73 | 
        
          |  | ], | 
        
          |  | "display_url": "dev.twitter.com/blog/twitter-c\u2026" | 
        
          |  | } | 
        
          |  | ], | 
        
          |  | "hashtags": [ | 
        
          |  |  | 
        
          |  | ], | 
        
          |  | "user_mentions": [ | 
        
          |  |  | 
        
          |  | ] | 
        
          |  | }, | 
        
          |  | "in_reply_to_user_id_str": null, | 
        
          |  | "contributors": null, | 
        
          |  | "text": "Introducing the Twitter Certified Products Program: https://t.co/MjJ8xAnT", | 
        
          |  | "retweet_count": 121, | 
        
          |  | "in_reply_to_status_id_str": null, | 
        
          |  | "id": 240859602684612608, | 
        
          |  | "geo": null, | 
        
          |  | "retweeted": false, | 
        
          |  | "possibly_sensitive": false, | 
        
          |  | "in_reply_to_user_id": null, | 
        
          |  | "place": null, | 
        
          |  | "user": { | 
        
          |  | "profile_sidebar_fill_color": "DDEEF6", | 
        
          |  | "profile_sidebar_border_color": "C0DEED", | 
        
          |  | "profile_background_tile": false, | 
        
          |  | "name": "Twitter API", | 
        
          |  | "profile_image_url": "http://a0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png", | 
        
          |  | "created_at": "Wed May 23 06:01:13 +0000 2007", | 
        
          |  | "location": "San Francisco, CA", | 
        
          |  | "follow_request_sent": false, | 
        
          |  | "profile_link_color": "0084B4", | 
        
          |  | "is_translator": false, | 
        
          |  | "id_str": "6253282", | 
        
          |  | "entities": { | 
        
          |  | "url": { | 
        
          |  | "urls": [ | 
        
          |  | { | 
        
          |  | "expanded_url": null, | 
        
          |  | "url": "", | 
        
          |  | "indices": [ | 
        
          |  | 0, | 
        
          |  | 22 | 
        
          |  | ] | 
        
          |  | } | 
        
          |  | ] | 
        
          |  | }, | 
        
          |  | "description": { | 
        
          |  | "urls": [ | 
        
          |  |  | 
        
          |  | ] | 
        
          |  | } | 
        
          |  | }, | 
        
          |  | "default_profile": true, | 
        
          |  | "contributors_enabled": true, | 
        
          |  | "favourites_count": 24, | 
        
          |  | "url": "", | 
        
          |  | "profile_image_url_https": "https://si0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png", | 
        
          |  | "utc_offset": -28800, | 
        
          |  | "id": 6253282, | 
        
          |  | "profile_use_background_image": true, | 
        
          |  | "listed_count": 10775, | 
        
          |  | "profile_text_color": "333333", | 
        
          |  | "lang": "en", | 
        
          |  | "followers_count": 1212864, | 
        
          |  | "protected": false, | 
        
          |  | "notifications": null, | 
        
          |  | "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png", | 
        
          |  | "profile_background_color": "C0DEED", | 
        
          |  | "verified": true, | 
        
          |  | "geo_enabled": true, | 
        
          |  | "time_zone": "Pacific Time (US & Canada)", | 
        
          |  | "description": "The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don't get an answer? It's on my website.", | 
        
          |  | "default_profile_image": false, | 
        
          |  | "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png", | 
        
          |  | "statuses_count": 3333, | 
        
          |  | "friends_count": 31, | 
        
          |  | "following": null, | 
        
          |  | "show_all_inline_media": false, | 
        
          |  | "screen_name": "twitterapi" | 
        
          |  | }, | 
        
          |  | "in_reply_to_screen_name": null, | 
        
          |  | "source": "YoruFukurou", | 
        
          |  | "in_reply_to_status_id": null | 
        
          |  | }, | 
        
          |  | { | 
        
          |  | "coordinates": null, | 
        
          |  | "favorited": false, | 
        
          |  | "truncated": false, | 
        
          |  | "created_at": "Sat Aug 25 17:26:51 +0000 2012", | 
        
          |  | "id_str": "239413543487819778", | 
        
          |  | "entities": { | 
        
          |  | "urls": [ | 
        
          |  | { | 
        
          |  | "expanded_url": "/issues/485", | 
        
          |  | "url": "https://t.co/p5bOzH0k", | 
        
          |  | "indices": [ | 
        
          |  | 97, | 
        
          |  | 118 | 
        
          |  | ], | 
        
          |  | "display_url": "dev.twitter.com/issues/485" | 
        
          |  | } | 
        
          |  | ], | 
        
          |  | "hashtags": [ | 
        
          |  |  | 
        
          |  | ], | 
        
          |  | "user_mentions": [ | 
        
          |  |  | 
        
          |  | ] | 
        
          |  | }, | 
        
          |  | "in_reply_to_user_id_str": null, | 
        
          |  | "contributors": null, | 
        
          |  | "text": "We are working to resolve issues with application management & logging in to the dev portal: https://t.co/p5bOzH0k ^TS", | 
        
          |  | "retweet_count": 105, | 
        
          |  | "in_reply_to_status_id_str": null, | 
        
          |  | "id": 239413543487819778, | 
        
          |  | "geo": null, | 
        
          |  | "retweeted": false, | 
        
          |  | "possibly_sensitive": false, | 
        
          |  | "in_reply_to_user_id": null, | 
        
          |  | "place": null, | 
        
          |  | "user": { | 
        
          |  | "profile_sidebar_fill_color": "DDEEF6", | 
        
          |  | "profile_sidebar_border_color": "C0DEED", | 
        
          |  | "profile_background_tile": false, | 
        
          |  | "name": "Twitter API", | 
        
          |  | "profile_image_url": "http://a0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png", | 
        
          |  | "created_at": "Wed May 23 06:01:13 +0000 2007", | 
        
          |  | "location": "San Francisco, CA", | 
        
          |  | "follow_request_sent": false, | 
        
          |  | "profile_link_color": "0084B4", | 
        
          |  | "is_translator": false, | 
        
          |  | "id_str": "6253282", | 
        
          |  | "entities": { | 
        
          |  | "url": { | 
        
          |  | "urls": [ | 
        
          |  | { | 
        
          |  | "expanded_url": null, | 
        
          |  | "url": "", | 
        
          |  | "indices": [ | 
        
          |  | 0, | 
        
          |  | 22 | 
        
          |  | ] | 
        
          |  | } | 
        
          |  | ] | 
        
          |  | }, | 
        
          |  | "description": { | 
        
          |  | "urls": [ | 
        
          |  |  | 
        
          |  | ] | 
        
          |  | } | 
        
          |  | }, | 
        
          |  | "default_profile": true, | 
        
          |  | "contributors_enabled": true, | 
        
          |  | "favourites_count": 24, | 
        
          |  | "url": "", | 
        
          |  | "profile_image_url_https": "https://si0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png", | 
        
          |  | "utc_offset": -28800, | 
        
          |  | "id": 6253282, | 
        
          |  | "profile_use_background_image": true, | 
        
          |  | "listed_count": 10775, | 
        
          |  | "profile_text_color": "333333", | 
        
          |  | "lang": "en", | 
        
          |  | "followers_count": 1212864, | 
        
          |  | "protected": false, | 
        
          |  | "notifications": null, | 
        
          |  | "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png", | 
        
          |  | "profile_background_color": "C0DEED", | 
        
          |  | "verified": true, | 
        
          |  | "geo_enabled": true, | 
        
          |  | "time_zone": "Pacific Time (US & Canada)", | 
        
          |  | "description": "The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don't get an answer? It's on my website.", | 
        
          |  | "default_profile_image": false, | 
        
          |  | "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png", | 
        
          |  | "statuses_count": 3333, | 
        
          |  | "friends_count": 31, | 
        
          |  | "following": null, | 
        
          |  | "show_all_inline_media": false, | 
        
          |  | "screen_name": "twitterapi" | 
        
          |  | }, | 
        
          |  | "in_reply_to_screen_name": null, | 
        
          |  | "source": "YoruFukurou", | 
        
          |  | "in_reply_to_status_id": null | 
        
          |  | } | 
        
          |  | ] | 
        
          |  | ` | 
        
          |  |  | 
        
          |  | func makeData() (tweets []Tweet) { | 
        
          |  | err := json.NewDecoder(strings.NewReader(data)).Decode(&tweets) | 
        
          |  | if err != nil { | 
        
          |  | panic(err) | 
        
          |  | } | 
        
          |  | return | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func makeDataN(n int) (tweets []Tweet) { | 
        
          |  | err := json.NewDecoder(strings.NewReader(data)).Decode(&tweets) | 
        
          |  | if err != nil { | 
        
          |  | panic(err) | 
        
          |  | } | 
        
          |  | for len(tweets) < n { | 
        
          |  | tweets = append(tweets, tweets...) | 
        
          |  | } | 
        
          |  | return tweets[:n] | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func BenchmarkJsonstore(b *testing.B) { | 
        
          |  | os.Remove("foo.gz") | 
        
          |  |  | 
        
          |  | sample := makeData() | 
        
          |  | b.ResetTimer() | 
        
          |  | js := new(jsonstore.JSONStore) | 
        
          |  | err := jsonstore.Save(js, "foo.gz") | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | for i := 0; i < b.N; i++ { | 
        
          |  | err = js.Set("data", sample) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | err = jsonstore.Save(js, "foo.gz") | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func BenchmarkBolt(b *testing.B) { | 
        
          |  | os.Remove("foo.db") | 
        
          |  |  | 
        
          |  | sample := makeData() | 
        
          |  | b.ResetTimer() | 
        
          |  |  | 
        
          |  | db, err := bolt.Open("foo.db", 0600, nil) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | defer db.Close() | 
        
          |  |  | 
        
          |  | db.Update(func(tx *bolt.Tx) error { | 
        
          |  | _, err := tx.CreateBucket([]byte("MyBucket")) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | return nil | 
        
          |  | }) | 
        
          |  |  | 
        
          |  | for i := 0; i < b.N; i++ { | 
        
          |  | err := db.Update(func(tx *bolt.Tx) error { | 
        
          |  | b := tx.Bucket([]byte("MyBucket")) | 
        
          |  | bJSON, err := json.Marshal(sample) | 
        
          |  | if err != nil { | 
        
          |  | return err | 
        
          |  | } | 
        
          |  | return b.Put([]byte("data"), bJSON) | 
        
          |  | }) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | err = db.Sync() | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func benchmarkJsonstoreN(b *testing.B, n int) { | 
        
          |  | os.Remove("foo.gz") | 
        
          |  |  | 
        
          |  | sample := makeDataN(n) | 
        
          |  |  | 
        
          |  | js := new(jsonstore.JSONStore) | 
        
          |  | err := jsonstore.Save(js, "foo.gz") | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  |  | 
        
          |  | b.ResetTimer() | 
        
          |  | for i := 0; i < b.N; i++ { | 
        
          |  | js, err = jsonstore.Open("foo.gz") | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | err = js.Set("data", sample) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | err = jsonstore.Save(js, "foo.gz") | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func benchmarkBoltN(b *testing.B, n int) { | 
        
          |  | os.Remove("foo.db") | 
        
          |  |  | 
        
          |  | sample := makeDataN(n) | 
        
          |  |  | 
        
          |  | db, err := bolt.Open("foo.db", 0600, nil) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | defer db.Close() | 
        
          |  |  | 
        
          |  | db.Update(func(tx *bolt.Tx) error { | 
        
          |  | _, err := tx.CreateBucket([]byte("MyBucket")) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | return nil | 
        
          |  | }) | 
        
          |  |  | 
        
          |  | db.Close() | 
        
          |  |  | 
        
          |  | b.ResetTimer() | 
        
          |  | for i := 0; i < b.N; i++ { | 
        
          |  | db, err := bolt.Open("foo.db", 0600, nil) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | err = db.Update(func(tx *bolt.Tx) error { | 
        
          |  | b := tx.Bucket([]byte("MyBucket")) | 
        
          |  | bJSON, err := json.Marshal(sample) | 
        
          |  | if err != nil { | 
        
          |  | return err | 
        
          |  | } | 
        
          |  | return b.Put([]byte("data"), bJSON) | 
        
          |  | }) | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | err = db.Sync() | 
        
          |  | if err != nil { | 
        
          |  | b.Fatal(err) | 
        
          |  | } | 
        
          |  | db.Close() | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func BenchmarkJsonstore1000(b *testing.B) { | 
        
          |  | benchmarkJsonstoreN(b, 1000) | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func BenchmarkBolt1000(b *testing.B) { | 
        
          |  | benchmarkBoltN(b, 1000) | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func BenchmarkJsonstore50000(b *testing.B) { | 
        
          |  | benchmarkJsonstoreN(b, 50000) | 
        
          |  | } | 
        
          |  |  | 
        
          |  | func BenchmarkBolt50000(b *testing.B) { | 
        
          |  | benchmarkBoltN(b, 50000) | 
        
          |  | } |